import type { ChatWithHistoryContextValue } from '../../context'
import type { AppData, ConversationItem } from '@/models/share'
import { act, render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { useChatWithHistoryContext } from '../../context'
import Header from '../index'

// Mock context module
vi.mock('../../context', () => ({
  useChatWithHistoryContext: vi.fn(),
}))

// Mock InputsFormContent
vi.mock('@/app/components/base/chat/chat-with-history/inputs-form/content', () => ({
  default: () => <div data-testid="inputs-form-content">InputsFormContent</div>,
}))

// Mock PortalToFollowElem using React Context
vi.mock('@/app/components/base/portal-to-follow-elem', async () => {
  const React = await import('react')
  const MockContext = React.createContext(false)

  return {
    PortalToFollowElem: ({ children, open }: { children: React.ReactNode, open: boolean }) => {
      return (
        <MockContext.Provider value={open}>
          <div data-open={open}>{children}</div>
        </MockContext.Provider>
      )
    },
    PortalToFollowElemContent: ({ children }: { children: React.ReactNode }) => {
      const open = React.useContext(MockContext)
      if (!open)
        return null
      return <div>{children}</div>
    },
    PortalToFollowElemTrigger: ({ children, onClick }: { children: React.ReactNode, onClick: () => void }) => (
      <div onClick={onClick}>{children}</div>
    ),
  }
})

// Mock Modal to avoid Headless UI issues in tests
vi.mock('@/app/components/base/modal', () => ({
  default: ({ children, isShow, title }: { children: React.ReactNode, isShow: boolean, title: React.ReactNode }) => {
    if (!isShow)
      return null
    return (
      <div data-testid="modal">
        {!!title && <div>{title}</div>}
        {children}
      </div>
    )
  },
}))

const mockAppData: AppData = {
  app_id: 'app-1',
  site: {
    title: 'Test App',
    icon_type: 'emoji',
    icon: '🤖',
    icon_background: '#fff',
    icon_url: '',
  },
  end_user_id: 'user-1',
  custom_config: null,
  can_replace_logo: false,
}

const mockContextDefaults: ChatWithHistoryContextValue = {
  appData: mockAppData,
  currentConversationId: '',
  currentConversationItem: undefined,
  inputsForms: [],
  pinnedConversationList: [],
  handlePinConversation: vi.fn(),
  handleUnpinConversation: vi.fn(),
  handleRenameConversation: vi.fn(),
  handleDeleteConversation: vi.fn(),
  handleNewConversation: vi.fn(),
  sidebarCollapseState: true,
  handleSidebarCollapse: vi.fn(),
  isResponding: false,
  conversationRenaming: false,
  showConfig: false,
} as unknown as ChatWithHistoryContextValue

const setup = (overrides: Partial<ChatWithHistoryContextValue> = {}) => {
  vi.mocked(useChatWithHistoryContext).mockReturnValue({
    ...mockContextDefaults,
    ...overrides,
  })
  return render(<Header />)
}

describe('Header Component', () => {
  beforeEach(() => {
    vi.clearAllMocks()
  })

  describe('Rendering', () => {
    it('should render conversation name when conversation is selected', () => {
      const mockConv = { id: 'conv-1', name: 'My Chat' } as ConversationItem
      setup({
        currentConversationId: 'conv-1',
        currentConversationItem: mockConv,
        sidebarCollapseState: true,
      })
      expect(screen.getByText('My Chat')).toBeInTheDocument()
    })

    it('should render ViewFormDropdown trigger when inputsForms are present', () => {
      const mockConv = { id: 'conv-1', name: 'My Chat' } as ConversationItem
      setup({
        currentConversationId: 'conv-1',
        currentConversationItem: mockConv,
        inputsForms: [{ id: 'form-1' }],
      })

      const buttons = screen.getAllByRole('button')
      // Sidebar(1) + NewChat(1) + ResetChat(1) + ViewForm(1) = 4 buttons
      expect(buttons).toHaveLength(4)
    })
  })

  describe('Interactions', () => {
    it('should handle new conversation', async () => {
      const handleNewConversation = vi.fn()
      setup({ handleNewConversation, sidebarCollapseState: true, currentConversationId: 'conv-1' })

      const buttons = screen.getAllByRole('button')
      // Sidebar, NewChat, ResetChat (3)
      const resetChatBtn = buttons[buttons.length - 1]
      await userEvent.click(resetChatBtn)

      expect(handleNewConversation).toHaveBeenCalled()
    })

    it('should handle sidebar toggle', async () => {
      const handleSidebarCollapse = vi.fn()
      setup({ handleSidebarCollapse, sidebarCollapseState: true })

      const buttons = screen.getAllByRole('button')
      const sidebarBtn = buttons[0]
      await userEvent.click(sidebarBtn)

      expect(handleSidebarCollapse).toHaveBeenCalledWith(false)
    })

    it('should render operation menu and handle pin', async () => {
      const handlePinConversation = vi.fn()
      const mockConv = { id: 'conv-1', name: 'My Chat' } as ConversationItem
      setup({
        currentConversationId: 'conv-1',
        currentConversationItem: mockConv,
        handlePinConversation,
        sidebarCollapseState: true,
      })

      const trigger = screen.getByText('My Chat')
      await userEvent.click(trigger)

      const pinBtn = await screen.findByText('explore.sidebar.action.pin')
      expect(pinBtn).toBeInTheDocument()

      await userEvent.click(pinBtn)

      expect(handlePinConversation).toHaveBeenCalledWith('conv-1')
    })

    it('should handle unpin', async () => {
      const handleUnpinConversation = vi.fn()
      const mockConv = { id: 'conv-1', name: 'My Chat' } as ConversationItem
      setup({
        currentConversationId: 'conv-1',
        currentConversationItem: mockConv,
        handleUnpinConversation,
        pinnedConversationList: [{ id: 'conv-1' } as ConversationItem],
        sidebarCollapseState: true,
      })

      await userEvent.click(screen.getByText('My Chat'))

      const unpinBtn = await screen.findByText('explore.sidebar.action.unpin')
      await userEvent.click(unpinBtn)

      expect(handleUnpinConversation).toHaveBeenCalledWith('conv-1')
    })

    it('should handle rename cancellation', async () => {
      const mockConv = { id: 'conv-1', name: 'My Chat' } as ConversationItem
      setup({
        currentConversationId: 'conv-1',
        currentConversationItem: mockConv,
        sidebarCollapseState: true,
      })

      await userEvent.click(screen.getByText('My Chat'))

      const renameMenuBtn = await screen.findByText('explore.sidebar.action.rename')
      await userEvent.click(renameMenuBtn)

      const cancelBtn = await screen.findByText('common.operation.cancel')
      await userEvent.click(cancelBtn)

      await waitFor(() => {
        expect(screen.queryByText('common.chat.renameConversation')).not.toBeInTheDocument()
      })
    })

    it('should handle rename success flow', async () => {
      const handleRenameConversation = vi.fn()
      const mockConv = { id: 'conv-1', name: 'My Chat' } as ConversationItem
      setup({
        currentConversationId: 'conv-1',
        currentConversationItem: mockConv,
        handleRenameConversation,
        sidebarCollapseState: true,
      })

      await userEvent.click(screen.getByText('My Chat'))

      const renameMenuBtn = await screen.findByText('explore.sidebar.action.rename')
      await userEvent.click(renameMenuBtn)

      expect(await screen.findByText('common.chat.renameConversation')).toBeInTheDocument()

      const input = screen.getByDisplayValue('My Chat')
      await userEvent.clear(input)
      await userEvent.type(input, 'New Name')

      const saveBtn = await screen.findByText('common.operation.save')
      await userEvent.click(saveBtn)

      expect(handleRenameConversation).toHaveBeenCalledWith('conv-1', 'New Name', expect.any(Object))

      const successCallback = handleRenameConversation.mock.calls[0][2].onSuccess
      await act(async () => {
        successCallback()
      })

      await waitFor(() => {
        expect(screen.queryByText('common.chat.renameConversation')).not.toBeInTheDocument()
      })
    })

    it('should handle delete flow', async () => {
      const handleDeleteConversation = vi.fn()
      const mockConv = { id: 'conv-1', name: 'My Chat' } as ConversationItem
      setup({
        currentConversationId: 'conv-1',
        currentConversationItem: mockConv,
        handleDeleteConversation,
        sidebarCollapseState: true,
      })

      await userEvent.click(screen.getByText('My Chat'))

      const deleteMenuBtn = await screen.findByText('explore.sidebar.action.delete')
      await userEvent.click(deleteMenuBtn)

      expect(handleDeleteConversation).not.toHaveBeenCalled()
      expect(await screen.findByText('share.chat.deleteConversation.title')).toBeInTheDocument()

      const confirmBtn = await screen.findByText('common.operation.confirm')
      await userEvent.click(confirmBtn)

      expect(handleDeleteConversation).toHaveBeenCalledWith('conv-1', expect.any(Object))

      const successCallback = handleDeleteConversation.mock.calls[0][1].onSuccess
      await act(async () => {
        successCallback()
      })

      await waitFor(() => {
        expect(screen.queryByText('share.chat.deleteConversation.title')).not.toBeInTheDocument()
      })
    })

    it('should handle delete cancellation', async () => {
      const mockConv = { id: 'conv-1', name: 'My Chat' } as ConversationItem
      setup({
        currentConversationId: 'conv-1',
        currentConversationItem: mockConv,
        sidebarCollapseState: true,
      })

      await userEvent.click(screen.getByText('My Chat'))

      const deleteMenuBtn = await screen.findByText('explore.sidebar.action.delete')
      await userEvent.click(deleteMenuBtn)

      const cancelBtn = await screen.findByText('common.operation.cancel')
      await userEvent.click(cancelBtn)

      await waitFor(() => {
        expect(screen.queryByText('share.chat.deleteConversation.title')).not.toBeInTheDocument()
      })
    })

    it('should handle empty translated delete content via fallback', async () => {
      const mockConv = { id: 'conv-1', name: 'My Chat' } as ConversationItem
      setup({
        currentConversationId: 'conv-1',
        currentConversationItem: mockConv,
        sidebarCollapseState: true,
      })

      await userEvent.click(screen.getByText('My Chat'))
      await userEvent.click(await screen.findByText('explore.sidebar.action.delete'))

      expect(await screen.findByText('share.chat.deleteConversation.title')).toBeInTheDocument()
    })
  })

  describe('Edge Cases', () => {
    it('should not render inputs form dropdown if inputsForms is empty', () => {
      const mockConv = { id: 'conv-1', name: 'My Chat' } as ConversationItem
      setup({
        currentConversationId: 'conv-1',
        currentConversationItem: mockConv,
        inputsForms: [],
      })

      const buttons = screen.getAllByRole('button')
      // Sidebar(1) + NewChat(1) + ResetChat(1) = 3 buttons
      expect(buttons).toHaveLength(3)
    })

    it('should render system title if conversation id is missing', () => {
      setup({ currentConversationId: '', sidebarCollapseState: true })
      const titleEl = screen.getByText('Test App')
      expect(titleEl).toHaveClass('system-md-semibold')
    })

    it('should render app icon from URL when icon_url is provided', () => {
      setup({
        appData: {
          ...mockAppData,
          site: {
            ...mockAppData.site,
            icon_type: 'image',
            icon_url: 'https://example.com/icon.png',
          },
        },
      })
      const img = screen.getByAltText('app icon')
      expect(img).toHaveAttribute('src', 'https://example.com/icon.png')
    })

    it('should handle undefined appData gracefully (optional chaining)', () => {
      setup({ appData: null as unknown as AppData })
      // Just verify it doesn't crash and renders the basic structure
      expect(screen.getAllByRole('button').length).toBeGreaterThan(0)
    })

    it('should handle missing name in conversation item', () => {
      const mockConv = { id: 'conv-1', name: '' } as ConversationItem
      setup({
        currentConversationId: 'conv-1',
        currentConversationItem: mockConv,
        sidebarCollapseState: true,
      })
      // The separator is just a div with text content '/'
      expect(screen.getByText('/')).toBeInTheDocument()
    })

    it('should handle New Chat button state when currentConversationId is present but isResponding is true', () => {
      setup({
        isResponding: true,
        sidebarCollapseState: true,
        currentConversationId: 'conv-1',
      })

      const buttons = screen.getAllByRole('button')
      // Sidebar, NewChat, ResetChat (3)
      const newChatBtn = buttons[1]
      expect(newChatBtn).toBeDisabled()
    })

    it('should handle New Chat button state when currentConversationId is missing and isResponding is false', () => {
      setup({
        isResponding: false,
        sidebarCollapseState: true,
        currentConversationId: '',
      })

      const buttons = screen.getAllByRole('button')
      // Sidebar, NewChat (2)
      const newChatBtn = buttons[1]
      expect(newChatBtn).toBeDisabled()
    })

    it('should not render operation menu if conversation id is missing', () => {
      setup({ currentConversationId: '', sidebarCollapseState: true })
      expect(screen.queryByText('My Chat')).not.toBeInTheDocument()
    })

    it('should not render operation menu if sidebar is NOT collapsed', () => {
      const mockConv = { id: 'conv-1', name: 'My Chat' } as ConversationItem
      setup({
        currentConversationId: 'conv-1',
        currentConversationItem: mockConv,
        sidebarCollapseState: false,
      })
      expect(screen.queryByText('My Chat')).not.toBeInTheDocument()
    })

    it('should pass empty rename value when conversation name is undefined', async () => {
      const mockConv = { id: 'conv-1' } as ConversationItem
      const { container } = setup({
        currentConversationId: 'conv-1',
        currentConversationItem: mockConv,
        sidebarCollapseState: true,
      })

      const operationTrigger = container.querySelector('.flex.cursor-pointer.items-center.rounded-lg.p-1\\.5.pl-2.text-text-secondary.hover\\:bg-state-base-hover') as HTMLElement
      await userEvent.click(operationTrigger)
      await userEvent.click(await screen.findByText('explore.sidebar.action.rename'))

      const input = screen.getByRole('textbox') as HTMLInputElement
      expect(input.value).toBe('')
    })
  })
})
