import type { App, AppSSO } from '@/types/app'
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import * as React from 'react'
import { AppModeEnum, AppTypeEnum } from '@/types/app'
import AppInfoDetailPanel from '../app-info-detail-panel'

vi.mock('../../../base/app-icon', () => ({
  default: ({ size, icon }: { size: string, icon: string }) => (
    <div data-testid="app-icon" data-size={size} data-icon={icon} />
  ),
}))

vi.mock('@/app/components/base/content-dialog', () => ({
  default: ({ show, onClose, children, className }: {
    show: boolean
    onClose: () => void
    children: React.ReactNode
    className?: string
  }) => (
    show
      ? (
          <div data-testid="content-dialog" className={className}>
            <button type="button" data-testid="dialog-close" onClick={onClose}>Close</button>
            {children}
          </div>
        )
      : null
  ),
}))

vi.mock('@/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/card-view', () => ({
  default: ({ appId }: { appId: string }) => (
    <div data-testid="card-view" data-app-id={appId} />
  ),
}))

vi.mock('@langgenius/dify-ui/button', () => ({
  Button: ({ children, onClick, className, size, variant }: {
    children: React.ReactNode
    onClick?: () => void
    className?: string
    size?: string
    variant?: string
  }) => (
    <button type="button" onClick={onClick} className={className} data-size={size} data-variant={variant}>
      {children}
    </button>
  ),
}))

vi.mock('../app-operations', () => ({
  default: ({ primaryOperations, secondaryOperations }: {
    primaryOperations?: Array<{ id: string, title: string, onClick: () => void }>
    secondaryOperations?: Array<{ id: string, title: string, onClick: () => void, type?: string }>
  }) => (
    <div data-testid="app-operations">
      {primaryOperations?.map(op => (
        <button key={op.id} type="button" data-testid={`op-${op.id}`} onClick={op.onClick}>{op.title}</button>
      ))}
      {secondaryOperations?.map(op => (
        op.type === 'divider'
          ? <button key={op.id} type="button" data-testid={`op-${op.id}`} onClick={op.onClick}>divider</button>
          : <button key={op.id} type="button" data-testid={`op-${op.id}`} onClick={op.onClick}>{op.title}</button>
      ))}
    </div>
  ),
}))

const createAppDetail = (overrides: Partial<App> = {}): App & Partial<AppSSO> => ({
  id: 'app-1',
  name: 'Test App',
  mode: AppModeEnum.CHAT,
  icon: '🤖',
  icon_type: 'emoji',
  icon_background: '#FFEAD5',
  icon_url: '',
  description: 'A test description',
  use_icon_as_answer_icon: false,
  ...overrides,
} as App & Partial<AppSSO>)

describe('AppInfoDetailPanel', () => {
  const defaultProps = {
    appDetail: createAppDetail(),
    show: true,
    onClose: vi.fn(),
    openModal: vi.fn(),
    exportCheck: vi.fn(),
  }

  beforeEach(() => {
    vi.clearAllMocks()
  })

  describe('Rendering', () => {
    it('should not render when show is false', () => {
      render(<AppInfoDetailPanel {...defaultProps} show={false} />)
      expect(screen.queryByTestId('content-dialog')).not.toBeInTheDocument()
    })

    it('should render dialog when show is true', () => {
      render(<AppInfoDetailPanel {...defaultProps} />)
      expect(screen.getByTestId('content-dialog')).toBeInTheDocument()
    })

    it('should display app name', () => {
      render(<AppInfoDetailPanel {...defaultProps} />)
      expect(screen.getByText('Test App')).toBeInTheDocument()
    })

    it('should display app mode label', () => {
      render(<AppInfoDetailPanel {...defaultProps} />)
      expect(screen.getByText('app.types.chatbot')).toBeInTheDocument()
    })

    it('should display description when available', () => {
      render(<AppInfoDetailPanel {...defaultProps} />)
      expect(screen.getByText('A test description')).toBeInTheDocument()
    })

    it('should not display description when empty', () => {
      render(<AppInfoDetailPanel {...defaultProps} appDetail={createAppDetail({ description: '' })} />)
      expect(screen.queryByText('A test description')).not.toBeInTheDocument()
    })

    it('should not display description when undefined', () => {
      render(<AppInfoDetailPanel {...defaultProps} appDetail={createAppDetail({ description: undefined as unknown as string })} />)
      expect(screen.queryByText('A test description')).not.toBeInTheDocument()
    })

    it('should render CardView with correct appId', () => {
      render(<AppInfoDetailPanel {...defaultProps} />)
      const cardView = screen.getByTestId('card-view')
      expect(cardView).toHaveAttribute('data-app-id', 'app-1')
    })

    it('should not render CardView when app type is evaluation', () => {
      render(
        <AppInfoDetailPanel
          {...defaultProps}
          appDetail={createAppDetail({ type: AppTypeEnum.EVALUATION })}
        />,
      )

      expect(screen.queryByTestId('card-view')).not.toBeInTheDocument()
    })

    it('should render app icon with large size', () => {
      render(<AppInfoDetailPanel {...defaultProps} />)
      const icon = screen.getByTestId('app-icon')
      expect(icon).toHaveAttribute('data-size', 'large')
    })
  })

  describe('Operations', () => {
    it('should render edit, duplicate, and export operations', () => {
      render(<AppInfoDetailPanel {...defaultProps} />)
      expect(screen.getByTestId('op-edit')).toBeInTheDocument()
      expect(screen.getByTestId('op-duplicate')).toBeInTheDocument()
      expect(screen.getByTestId('op-export')).toBeInTheDocument()
    })

    it('should call openModal with edit when edit is clicked', async () => {
      const user = userEvent.setup()
      render(<AppInfoDetailPanel {...defaultProps} />)

      await user.click(screen.getByTestId('op-edit'))

      expect(defaultProps.openModal).toHaveBeenCalledWith('edit')
    })

    it('should call openModal with duplicate when duplicate is clicked', async () => {
      const user = userEvent.setup()
      render(<AppInfoDetailPanel {...defaultProps} />)

      await user.click(screen.getByTestId('op-duplicate'))

      expect(defaultProps.openModal).toHaveBeenCalledWith('duplicate')
    })

    it('should call exportCheck when export is clicked', async () => {
      const user = userEvent.setup()
      render(<AppInfoDetailPanel {...defaultProps} />)

      await user.click(screen.getByTestId('op-export'))

      expect(defaultProps.exportCheck).toHaveBeenCalledTimes(1)
    })

    it('should render delete operation', () => {
      render(<AppInfoDetailPanel {...defaultProps} />)
      expect(screen.getByTestId('op-delete')).toBeInTheDocument()
    })

    it('should call openModal with delete when delete is clicked', async () => {
      const user = userEvent.setup()
      render(<AppInfoDetailPanel {...defaultProps} />)

      await user.click(screen.getByTestId('op-delete'))

      expect(defaultProps.openModal).toHaveBeenCalledWith('delete')
    })
  })

  describe('Import DSL option', () => {
    it('should show import DSL for advanced_chat mode', () => {
      render(
        <AppInfoDetailPanel
          {...defaultProps}
          appDetail={createAppDetail({ mode: AppModeEnum.ADVANCED_CHAT })}
        />,
      )
      expect(screen.getByTestId('op-import')).toBeInTheDocument()
    })

    it('should show import DSL for workflow mode', () => {
      render(
        <AppInfoDetailPanel
          {...defaultProps}
          appDetail={createAppDetail({ mode: AppModeEnum.WORKFLOW })}
        />,
      )
      expect(screen.getByTestId('op-import')).toBeInTheDocument()
    })

    it('should not show import DSL for chat mode', () => {
      render(<AppInfoDetailPanel {...defaultProps} />)
      expect(screen.queryByTestId('op-import')).not.toBeInTheDocument()
    })

    it('should call openModal with importDSL when import is clicked', async () => {
      const user = userEvent.setup()
      render(
        <AppInfoDetailPanel
          {...defaultProps}
          appDetail={createAppDetail({ mode: AppModeEnum.ADVANCED_CHAT })}
        />,
      )
      await user.click(screen.getByTestId('op-import'))
      expect(defaultProps.openModal).toHaveBeenCalledWith('importDSL')
    })

    it('should render divider in secondary operations', async () => {
      const user = userEvent.setup()
      render(<AppInfoDetailPanel {...defaultProps} />)
      const divider = screen.getByTestId('op-divider-1')
      expect(divider).toBeInTheDocument()
      await user.click(divider)
    })
  })

  describe('Switch operation', () => {
    it('should show switch button for chat mode', () => {
      render(<AppInfoDetailPanel {...defaultProps} />)
      expect(screen.getByText('app.switch')).toBeInTheDocument()
    })

    it('should show switch button for completion mode', () => {
      render(
        <AppInfoDetailPanel
          {...defaultProps}
          appDetail={createAppDetail({ mode: AppModeEnum.COMPLETION })}
        />,
      )
      expect(screen.getByText('app.switch')).toBeInTheDocument()
    })

    it('should not show switch button for workflow mode', () => {
      render(
        <AppInfoDetailPanel
          {...defaultProps}
          appDetail={createAppDetail({ mode: AppModeEnum.WORKFLOW })}
        />,
      )
      expect(screen.queryByText('app.switch')).not.toBeInTheDocument()
    })

    it('should not show switch button for advanced_chat mode', () => {
      render(
        <AppInfoDetailPanel
          {...defaultProps}
          appDetail={createAppDetail({ mode: AppModeEnum.ADVANCED_CHAT })}
        />,
      )
      expect(screen.queryByText('app.switch')).not.toBeInTheDocument()
    })

    it('should call openModal with switch when switch button is clicked', async () => {
      const user = userEvent.setup()
      render(<AppInfoDetailPanel {...defaultProps} />)

      await user.click(screen.getByText('app.switch'))

      expect(defaultProps.openModal).toHaveBeenCalledWith('switch')
    })
  })

  describe('Dialog interactions', () => {
    it('should call onClose when dialog close button is clicked', async () => {
      const user = userEvent.setup()
      render(<AppInfoDetailPanel {...defaultProps} />)

      await user.click(screen.getByTestId('dialog-close'))

      expect(defaultProps.onClose).toHaveBeenCalledTimes(1)
    })
  })
})
