import type { createDocumentResponse, DataSet, FullDocumentDetail, IconInfo } from '@/models/datasets'
import { render, screen } from '@testing-library/react'
import { RETRIEVE_METHOD } from '@/types/app'
import StepThree from '../index'

// Mock the EmbeddingProcess component since it has complex async logic
vi.mock('../../embedding-process', () => ({
  default: vi.fn(({ datasetId, batchId, documents, indexingType, retrievalMethod }) => (
    <div data-testid="embedding-process">
      <span data-testid="ep-dataset-id">{datasetId}</span>
      <span data-testid="ep-batch-id">{batchId}</span>
      <span data-testid="ep-documents-count">{documents?.length ?? 0}</span>
      <span data-testid="ep-indexing-type">{indexingType}</span>
      <span data-testid="ep-retrieval-method">{retrievalMethod}</span>
    </div>
  )),
}))

// Mock useBreakpoints hook
let mockMediaType = 'pc'
vi.mock('@/hooks/use-breakpoints', () => ({
  MediaType: {
    mobile: 'mobile',
    tablet: 'tablet',
    pc: 'pc',
  },
  default: vi.fn(() => mockMediaType),
}))

// Mock useDocLink hook
vi.mock('@/context/i18n', () => ({
  useDocLink: () => (path?: string) => `https://docs.dify.ai/en-US${path || ''}`,
}))

// Factory function to create mock IconInfo
const createMockIconInfo = (overrides: Partial<IconInfo> = {}): IconInfo => ({
  icon: '📙',
  icon_type: 'emoji',
  icon_background: '#FFF4ED',
  icon_url: '',
  ...overrides,
})

// Factory function to create mock FullDocumentDetail
const createMockDocument = (overrides: Partial<FullDocumentDetail> = {}): FullDocumentDetail => ({
  id: 'doc-123',
  name: 'test-document.txt',
  data_source_type: 'upload_file',
  data_source_info: {
    upload_file: {
      id: 'file-123',
      name: 'test-document.txt',
      extension: 'txt',
      mime_type: 'text/plain',
      size: 1024,
      created_by: 'user-1',
      created_at: Date.now(),
    },
  },
  batch: 'batch-123',
  created_api_request_id: 'request-123',
  processing_started_at: Date.now(),
  parsing_completed_at: Date.now(),
  cleaning_completed_at: Date.now(),
  splitting_completed_at: Date.now(),
  tokens: 100,
  indexing_latency: 5000,
  completed_at: Date.now(),
  paused_by: '',
  paused_at: 0,
  stopped_at: 0,
  indexing_status: 'completed',
  disabled_at: 0,
  ...overrides,
} as FullDocumentDetail)

// Factory function to create mock createDocumentResponse
const createMockCreationCache = (overrides: Partial<createDocumentResponse> = {}): createDocumentResponse => ({
  dataset: {
    id: 'dataset-123',
    name: 'Test Dataset',
    icon_info: createMockIconInfo(),
    indexing_technique: 'high_quality',
    retrieval_model_dict: {
      search_method: 'semantic_search',
    },
  } as createDocumentResponse['dataset'],
  batch: 'batch-123',
  documents: [createMockDocument()] as createDocumentResponse['documents'],
  ...overrides,
})

// Helper to render StepThree with default props
const renderStepThree = (props: Partial<Parameters<typeof StepThree>[0]> = {}) => {
  const defaultProps = {
    ...props,
  }
  return render(<StepThree {...defaultProps} />)
}

// StepThree Component Tests
describe('StepThree', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    mockMediaType = 'pc'
  })

  // Rendering Tests - Verify component renders properly
  describe('Rendering', () => {
    it('should render without crashing', () => {
      renderStepThree()

      expect(screen.getByTestId('embedding-process')).toBeInTheDocument()
    })

    it('should render with creation title when datasetId is not provided', () => {
      renderStepThree()

      expect(screen.getByText('datasetCreation.stepThree.creationTitle')).toBeInTheDocument()
      expect(screen.getByText('datasetCreation.stepThree.creationContent')).toBeInTheDocument()
    })

    it('should render with addition title when datasetId is provided', () => {
      renderStepThree({
        datasetId: 'existing-dataset-123',
        datasetName: 'Existing Dataset',
      })

      expect(screen.getByText('datasetCreation.stepThree.additionTitle')).toBeInTheDocument()
      expect(screen.queryByText('datasetCreation.stepThree.creationTitle')).not.toBeInTheDocument()
    })

    it('should render label text in creation mode', () => {
      renderStepThree()

      expect(screen.getByText('datasetCreation.stepThree.label')).toBeInTheDocument()
    })

    it('should render side tip panel on desktop', () => {
      mockMediaType = 'pc'

      renderStepThree()

      expect(screen.getByText('datasetCreation.stepThree.sideTipTitle')).toBeInTheDocument()
      expect(screen.getByText('datasetCreation.stepThree.sideTipContent')).toBeInTheDocument()
      expect(screen.getByText('datasetPipeline.addDocuments.stepThree.learnMore')).toBeInTheDocument()
    })

    it('should not render side tip panel on mobile', () => {
      mockMediaType = 'mobile'

      renderStepThree()

      expect(screen.queryByText('datasetCreation.stepThree.sideTipTitle')).not.toBeInTheDocument()
      expect(screen.queryByText('datasetCreation.stepThree.sideTipContent')).not.toBeInTheDocument()
    })

    it('should render EmbeddingProcess component', () => {
      renderStepThree()

      expect(screen.getByTestId('embedding-process')).toBeInTheDocument()
    })

    it('should render documentation link with correct href on desktop', () => {
      mockMediaType = 'pc'

      renderStepThree()

      const link = screen.getByText('datasetPipeline.addDocuments.stepThree.learnMore')
      expect(link).toHaveAttribute('href', 'https://docs.dify.ai/en-US/use-dify/knowledge/integrate-knowledge-within-application')
      expect(link).toHaveAttribute('target', '_blank')
      expect(link).toHaveAttribute('rel', 'noreferrer noopener')
    })

    it('should apply correct container classes', () => {
      const { container } = renderStepThree()

      const outerDiv = container.firstChild as HTMLElement
      expect(outerDiv).toHaveClass('flex', 'size-full', 'max-h-full', 'justify-center', 'overflow-y-auto')
    })
  })

  // Props Testing - Test all prop variations
  describe('Props', () => {
    describe('datasetId prop', () => {
      it('should render creation mode when datasetId is undefined', () => {
        renderStepThree({ datasetId: undefined })

        expect(screen.getByText('datasetCreation.stepThree.creationTitle')).toBeInTheDocument()
      })

      it('should render addition mode when datasetId is provided', () => {
        renderStepThree({ datasetId: 'dataset-123' })

        expect(screen.getByText('datasetCreation.stepThree.additionTitle')).toBeInTheDocument()
      })

      it('should pass datasetId to EmbeddingProcess', () => {
        const datasetId = 'my-dataset-id'

        renderStepThree({ datasetId })

        expect(screen.getByTestId('ep-dataset-id')).toHaveTextContent(datasetId)
      })

      it('should use creationCache dataset id when datasetId is not provided', () => {
        const creationCache = createMockCreationCache()

        renderStepThree({ creationCache })

        expect(screen.getByTestId('ep-dataset-id')).toHaveTextContent('dataset-123')
      })
    })

    describe('datasetName prop', () => {
      it('should display datasetName in creation mode', () => {
        renderStepThree({ datasetName: 'My Custom Dataset' })

        expect(screen.getByText('My Custom Dataset')).toBeInTheDocument()
      })

      it('should display datasetName in addition mode description', () => {
        renderStepThree({
          datasetId: 'dataset-123',
          datasetName: 'Existing Dataset Name',
        })

        // Assert - Check the text contains the dataset name (in the description)
        const description = screen.getByText(/datasetCreation.stepThree.additionP1.*Existing Dataset Name.*datasetCreation.stepThree.additionP2/i)
        expect(description).toBeInTheDocument()
      })

      it('should fallback to creationCache dataset name when datasetName is not provided', () => {
        const creationCache = createMockCreationCache()
        creationCache.dataset!.name = 'Cache Dataset Name'

        renderStepThree({ creationCache })

        expect(screen.getByText('Cache Dataset Name')).toBeInTheDocument()
      })
    })

    describe('indexingType prop', () => {
      it('should pass indexingType to EmbeddingProcess', () => {
        renderStepThree({ indexingType: 'high_quality' })

        expect(screen.getByTestId('ep-indexing-type')).toHaveTextContent('high_quality')
      })

      it('should use creationCache indexing_technique when indexingType is not provided', () => {
        const creationCache = createMockCreationCache()
        creationCache.dataset!.indexing_technique = 'economy' as unknown as DataSet['indexing_technique']

        renderStepThree({ creationCache })

        expect(screen.getByTestId('ep-indexing-type')).toHaveTextContent('economy')
      })

      it('should prefer creationCache indexing_technique over indexingType prop', () => {
        const creationCache = createMockCreationCache()
        creationCache.dataset!.indexing_technique = 'cache_technique' as unknown as DataSet['indexing_technique']

        renderStepThree({ creationCache, indexingType: 'prop_technique' })

        // Assert - creationCache takes precedence
        expect(screen.getByTestId('ep-indexing-type')).toHaveTextContent('cache_technique')
      })
    })

    describe('retrievalMethod prop', () => {
      it('should pass retrievalMethod to EmbeddingProcess', () => {
        renderStepThree({ retrievalMethod: RETRIEVE_METHOD.semantic })

        expect(screen.getByTestId('ep-retrieval-method')).toHaveTextContent('semantic_search')
      })

      it('should use creationCache retrieval method when retrievalMethod is not provided', () => {
        const creationCache = createMockCreationCache()
        creationCache.dataset!.retrieval_model_dict = { search_method: 'full_text_search' } as unknown as DataSet['retrieval_model_dict']

        renderStepThree({ creationCache })

        expect(screen.getByTestId('ep-retrieval-method')).toHaveTextContent('full_text_search')
      })
    })

    describe('creationCache prop', () => {
      it('should pass batchId from creationCache to EmbeddingProcess', () => {
        const creationCache = createMockCreationCache()
        creationCache.batch = 'custom-batch-123'

        renderStepThree({ creationCache })

        expect(screen.getByTestId('ep-batch-id')).toHaveTextContent('custom-batch-123')
      })

      it('should pass documents from creationCache to EmbeddingProcess', () => {
        const creationCache = createMockCreationCache()
        creationCache.documents = [createMockDocument(), createMockDocument(), createMockDocument()] as unknown as createDocumentResponse['documents']

        renderStepThree({ creationCache })

        expect(screen.getByTestId('ep-documents-count')).toHaveTextContent('3')
      })

      it('should use icon_info from creationCache dataset', () => {
        const creationCache = createMockCreationCache()
        creationCache.dataset!.icon_info = createMockIconInfo({
          icon: '🚀',
          icon_background: '#FF0000',
        })

        const { container } = renderStepThree({ creationCache })

        // Assert - Check AppIcon component receives correct props
        const appIcon = container.querySelector('span[style*="background"]')
        expect(appIcon).toBeInTheDocument()
      })

      it('should handle undefined creationCache', () => {
        renderStepThree({ creationCache: undefined })

        // Assert - Should not crash, use fallback values
        expect(screen.getByTestId('ep-dataset-id')).toHaveTextContent('')
        expect(screen.getByTestId('ep-batch-id')).toHaveTextContent('')
      })

      it('should handle creationCache with undefined dataset', () => {
        const creationCache: createDocumentResponse = {
          dataset: undefined,
          batch: 'batch-123',
          documents: [],
        }

        renderStepThree({ creationCache })

        // Assert - Should use default icon info
        expect(screen.getByTestId('embedding-process')).toBeInTheDocument()
      })
    })
  })

  // Edge Cases Tests - Test null, undefined, empty values and boundaries
  describe('Edge Cases', () => {
    it('should handle all props being undefined', () => {
      renderStepThree({
        datasetId: undefined,
        datasetName: undefined,
        indexingType: undefined,
        retrievalMethod: undefined,
        creationCache: undefined,
      })

      // Assert - Should render creation mode with fallbacks
      expect(screen.getByText('datasetCreation.stepThree.creationTitle')).toBeInTheDocument()
      expect(screen.getByTestId('embedding-process')).toBeInTheDocument()
    })

    it('should handle empty string datasetId', () => {
      renderStepThree({ datasetId: '' })

      // Assert - Empty string is falsy, should show creation mode
      expect(screen.getByText('datasetCreation.stepThree.creationTitle')).toBeInTheDocument()
    })

    it('should handle empty string datasetName', () => {
      renderStepThree({ datasetName: '' })

      // Assert - Should not crash
      expect(screen.getByTestId('embedding-process')).toBeInTheDocument()
    })

    it('should handle empty documents array in creationCache', () => {
      const creationCache = createMockCreationCache()
      creationCache.documents = []

      renderStepThree({ creationCache })

      expect(screen.getByTestId('ep-documents-count')).toHaveTextContent('0')
    })

    it('should handle creationCache with missing icon_info', () => {
      const creationCache = createMockCreationCache()
      creationCache.dataset!.icon_info = undefined as unknown as IconInfo

      renderStepThree({ creationCache })

      // Assert - Should use default icon info
      expect(screen.getByTestId('embedding-process')).toBeInTheDocument()
    })

    it('should handle very long datasetName', () => {
      const longName = 'A'.repeat(500)

      renderStepThree({ datasetName: longName })

      // Assert - Should render without crashing
      expect(screen.getByText(longName)).toBeInTheDocument()
    })

    it('should handle special characters in datasetName', () => {
      const specialName = 'Dataset <script>alert("xss")</script> & "quotes" \'apostrophe\''

      renderStepThree({ datasetName: specialName })

      // Assert - Should render safely as text
      expect(screen.getByText(specialName)).toBeInTheDocument()
    })

    it('should handle unicode characters in datasetName', () => {
      const unicodeName = '数据集名称 🚀 émojis & spëcîal çhàrs'

      renderStepThree({ datasetName: unicodeName })

      expect(screen.getByText(unicodeName)).toBeInTheDocument()
    })

    it('should handle creationCache with null dataset name', () => {
      const creationCache = createMockCreationCache()
      creationCache.dataset!.name = null as unknown as string

      const { container } = renderStepThree({ creationCache })

      // Assert - Should not crash
      expect(container.firstChild).toBeInTheDocument()
    })
  })

  // Conditional Rendering Tests - Test mode switching behavior
  describe('Conditional Rendering', () => {
    describe('Creation Mode (no datasetId)', () => {
      it('should show AppIcon component', () => {
        const { container } = renderStepThree()

        // Assert - AppIcon should be rendered
        const appIcon = container.querySelector('span')
        expect(appIcon).toBeInTheDocument()
      })

      it('should show Divider component', () => {
        const { container } = renderStepThree()

        // Assert - Divider should be rendered (it adds hr with specific classes)
        const dividers = container.querySelectorAll('[class*="divider"]')
        expect(dividers.length).toBeGreaterThan(0)
      })

      it('should show dataset name input area', () => {
        const datasetName = 'Test Dataset Name'

        renderStepThree({ datasetName })

        expect(screen.getByText(datasetName)).toBeInTheDocument()
      })
    })

    describe('Addition Mode (with datasetId)', () => {
      it('should not show AppIcon component', () => {
        renderStepThree({ datasetId: 'dataset-123' })

        // Assert - Creation section should not be rendered
        expect(screen.queryByText('datasetCreation.stepThree.label')).not.toBeInTheDocument()
      })

      it('should show addition description with dataset name', () => {
        renderStepThree({
          datasetId: 'dataset-123',
          datasetName: 'My Dataset',
        })

        // Assert - Description should include dataset name
        expect(screen.getByText(/datasetCreation.stepThree.additionP1/)).toBeInTheDocument()
      })
    })

    describe('Mobile vs Desktop', () => {
      it('should show side panel on tablet', () => {
        mockMediaType = 'tablet'

        renderStepThree()

        // Assert - Tablet is not mobile, should show side panel
        expect(screen.getByText('datasetCreation.stepThree.sideTipTitle')).toBeInTheDocument()
      })

      it('should not show side panel on mobile', () => {
        mockMediaType = 'mobile'

        renderStepThree()

        expect(screen.queryByText('datasetCreation.stepThree.sideTipTitle')).not.toBeInTheDocument()
      })

      it('should render EmbeddingProcess on mobile', () => {
        mockMediaType = 'mobile'

        renderStepThree()

        // Assert - Main content should still be rendered
        expect(screen.getByTestId('embedding-process')).toBeInTheDocument()
      })
    })
  })

  // EmbeddingProcess Integration Tests - Verify correct props are passed
  describe('EmbeddingProcess Integration', () => {
    it('should pass correct datasetId to EmbeddingProcess with datasetId prop', () => {
      renderStepThree({ datasetId: 'direct-dataset-id' })

      expect(screen.getByTestId('ep-dataset-id')).toHaveTextContent('direct-dataset-id')
    })

    it('should pass creationCache dataset id when datasetId prop is undefined', () => {
      const creationCache = createMockCreationCache()
      creationCache.dataset!.id = 'cache-dataset-id'

      renderStepThree({ creationCache })

      expect(screen.getByTestId('ep-dataset-id')).toHaveTextContent('cache-dataset-id')
    })

    it('should pass empty string for datasetId when both sources are undefined', () => {
      renderStepThree()

      expect(screen.getByTestId('ep-dataset-id')).toHaveTextContent('')
    })

    it('should pass batchId from creationCache', () => {
      const creationCache = createMockCreationCache()
      creationCache.batch = 'test-batch-456'

      renderStepThree({ creationCache })

      expect(screen.getByTestId('ep-batch-id')).toHaveTextContent('test-batch-456')
    })

    it('should pass empty string for batchId when creationCache is undefined', () => {
      renderStepThree()

      expect(screen.getByTestId('ep-batch-id')).toHaveTextContent('')
    })

    it('should prefer datasetId prop over creationCache dataset id', () => {
      const creationCache = createMockCreationCache()
      creationCache.dataset!.id = 'cache-id'

      renderStepThree({ datasetId: 'prop-id', creationCache })

      // Assert - datasetId prop takes precedence
      expect(screen.getByTestId('ep-dataset-id')).toHaveTextContent('prop-id')
    })
  })

  // Icon Rendering Tests - Verify AppIcon behavior
  describe('Icon Rendering', () => {
    it('should use default icon info when creationCache is undefined', () => {
      const { container } = renderStepThree()

      // Assert - Default background color should be applied
      const appIcon = container.querySelector('span[style*="background"]')
      if (appIcon)
        expect(appIcon).toHaveStyle({ background: '#FFF4ED' })
    })

    it('should use icon_info from creationCache when available', () => {
      const creationCache = createMockCreationCache()
      creationCache.dataset!.icon_info = {
        icon: '🎉',
        icon_type: 'emoji',
        icon_background: '#00FF00',
        icon_url: '',
      }

      const { container } = renderStepThree({ creationCache })

      // Assert - Custom background color should be applied
      const appIcon = container.querySelector('span[style*="background"]')
      if (appIcon)
        expect(appIcon).toHaveStyle({ background: '#00FF00' })
    })

    it('should use default icon when creationCache dataset icon_info is undefined', () => {
      const creationCache = createMockCreationCache()
      delete (creationCache.dataset as Partial<DataSet>).icon_info

      const { container } = renderStepThree({ creationCache })

      // Assert - Component should still render with default icon
      expect(container.firstChild).toBeInTheDocument()
    })
  })

  // Layout Tests - Verify correct CSS classes and structure
  describe('Layout', () => {
    it('should have correct outer container classes', () => {
      const { container } = renderStepThree()

      const outerDiv = container.firstChild as HTMLElement
      expect(outerDiv).toHaveClass('flex')
      expect(outerDiv).toHaveClass('size-full')
      expect(outerDiv).toHaveClass('justify-center')
    })

    it('should have correct inner container classes', () => {
      const { container } = renderStepThree()

      const innerDiv = container.querySelector('.max-w-\\[960px\\]')
      expect(innerDiv).toBeInTheDocument()
      expect(innerDiv).toHaveClass('shrink-0', 'grow')
    })

    it('should have content wrapper with correct max width', () => {
      const { container } = renderStepThree()

      const contentWrapper = container.querySelector('.max-w-\\[640px\\]')
      expect(contentWrapper).toBeInTheDocument()
    })

    it('should have side tip panel with correct width on desktop', () => {
      mockMediaType = 'pc'

      const { container } = renderStepThree()

      const sidePanel = container.querySelector('.w-\\[328px\\]')
      expect(sidePanel).toBeInTheDocument()
    })
  })

  // Accessibility Tests - Verify accessibility features
  describe('Accessibility', () => {
    it('should have correct link attributes for external documentation link', () => {
      mockMediaType = 'pc'

      renderStepThree()

      const link = screen.getByText('datasetPipeline.addDocuments.stepThree.learnMore')
      expect(link.tagName).toBe('A')
      expect(link).toHaveAttribute('target', '_blank')
      expect(link).toHaveAttribute('rel', 'noreferrer noopener')
    })

    it('should have semantic heading structure in creation mode', () => {
      renderStepThree()

      const title = screen.getByText('datasetCreation.stepThree.creationTitle')
      expect(title).toBeInTheDocument()
      expect(title.className).toContain('title-2xl-semi-bold')
    })

    it('should have semantic heading structure in addition mode', () => {
      renderStepThree({ datasetId: 'dataset-123' })

      const title = screen.getByText('datasetCreation.stepThree.additionTitle')
      expect(title).toBeInTheDocument()
      expect(title.className).toContain('title-2xl-semi-bold')
    })
  })

  // Side Panel Tests - Verify side panel behavior
  describe('Side Panel', () => {
    it('should render RiBookOpenLine icon in side panel', () => {
      mockMediaType = 'pc'

      const { container } = renderStepThree()

      // Assert - Icon should be present in side panel
      const iconContainer = container.querySelector('.size-10')
      expect(iconContainer).toBeInTheDocument()
    })

    it('should have correct side panel section background', () => {
      mockMediaType = 'pc'

      const { container } = renderStepThree()

      const sidePanel = container.querySelector('.bg-background-section')
      expect(sidePanel).toBeInTheDocument()
    })

    it('should have correct padding for side panel', () => {
      mockMediaType = 'pc'

      const { container } = renderStepThree()

      const sidePanelWrapper = container.querySelector('.pr-8')
      expect(sidePanelWrapper).toBeInTheDocument()
    })
  })
})
