import type { ReactElement } from 'react'
import type { EmbeddedChatbotContextValue } from '../../context'
import type { AppData } from '@/models/share'
import { act, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { renderWithSystemFeatures } from '@/__tests__/utils/mock-system-features'
import { useEmbeddedChatbotContext } from '../../context'
import Header from '../index'

let mockBranding = { enabled: true, workspace_logo: '' }
const render = (ui: ReactElement) => renderWithSystemFeatures(ui, {
  systemFeatures: { branding: { ...mockBranding } },
})

vi.mock('../../context', () => ({
  useEmbeddedChatbotContext: vi.fn(),
}))

vi.mock('@/app/components/base/chat/embedded-chatbot/inputs-form/view-form-dropdown', () => ({
  default: () => <div data-testid="view-form-dropdown" />,
}))

describe('EmbeddedChatbot Header', () => {
  const defaultAppData: AppData = {
    app_id: 'test-app-id',
    can_replace_logo: true,
    custom_config: {
      remove_webapp_brand: false,
      replace_webapp_logo: '',
    },
    enable_site: true,
    end_user_id: 'test-user-id',
    site: {
      title: 'Test Site',
    },
  }

  const defaultContext: Partial<EmbeddedChatbotContextValue> = {
    appData: defaultAppData,
    currentConversationId: 'test-conv-id',
    inputsForms: [],
    allInputsHidden: false,
  }

  const setupIframe = () => {
    const mockPostMessage = vi.fn()
    const mockTop = { postMessage: mockPostMessage }
    Object.defineProperty(window, 'self', { value: {}, configurable: true })
    Object.defineProperty(window, 'top', { value: mockTop, configurable: true })
    Object.defineProperty(window, 'parent', { value: mockTop, configurable: true })
    return mockPostMessage
  }

  beforeEach(() => {
    vi.clearAllMocks()
    mockBranding = { enabled: true, workspace_logo: '' }
    vi.mocked(useEmbeddedChatbotContext).mockReturnValue(defaultContext as EmbeddedChatbotContextValue)

    Object.defineProperty(window, 'self', { value: window, configurable: true })
    Object.defineProperty(window, 'top', { value: window, configurable: true })
  })

  const dispatchChatbotConfigMessage = async (origin: string, payload: { isToggledByButton: boolean, isDraggable: boolean }) => {
    await act(async () => {
      window.dispatchEvent(new MessageEvent('message', {
        origin,
        data: {
          type: 'dify-chatbot-config',
          payload,
        },
      }))
    })
  }

  describe('Desktop Rendering', () => {
    it('should render desktop header with branding by default', async () => {
      render(<Header title="Test Chatbot" />)

      expect(screen.getByTestId('webapp-brand')).toBeInTheDocument()
      expect(screen.getByText('share.chat.poweredBy')).toBeInTheDocument()
    })

    it('should render custom logo when provided in appData', () => {
      vi.mocked(useEmbeddedChatbotContext).mockReturnValue({
        ...defaultContext,
        appData: {
          ...defaultAppData,
          custom_config: {
            ...defaultAppData.custom_config,
            replace_webapp_logo: 'https://example.com/logo.png',
          },
        },
      } as EmbeddedChatbotContextValue)

      render(<Header title="Test Chatbot" />)

      const img = screen.getByAltText('logo')
      expect(img).toHaveAttribute('src', 'https://example.com/logo.png')
    })

    it('should render workspace logo when branding is enabled and logo exists', () => {
      mockBranding = { enabled: true, workspace_logo: 'https://example.com/workspace.png' }

      render(<Header title="Test Chatbot" />)

      const img = screen.getByAltText('logo')
      expect(img).toHaveAttribute('src', 'https://example.com/workspace.png')
    })

    it('should render Dify logo by default when branding enabled is true but no logo provided', () => {
      mockBranding = { enabled: true, workspace_logo: '' }
      render(<Header title="Test Chatbot" />)
      expect(screen.getByAltText('Dify logo')).toBeInTheDocument()
    })

    it('should render Dify logo when branding is disabled', () => {
      mockBranding = { enabled: false, workspace_logo: '' }
      render(<Header title="Test Chatbot" />)
      expect(screen.getByAltText('Dify logo')).toBeInTheDocument()
    })

    it('should NOT render branding when remove_webapp_brand is true', () => {
      vi.mocked(useEmbeddedChatbotContext).mockReturnValue({
        ...defaultContext,
        appData: {
          ...defaultAppData,
          custom_config: {
            ...defaultAppData.custom_config,
            remove_webapp_brand: true,
          },
        },
      } as EmbeddedChatbotContextValue)

      render(<Header title="Test Chatbot" />)

      expect(screen.queryByTestId('webapp-brand')).not.toBeInTheDocument()
    })

    it('should render divider only when currentConversationId is present', () => {
      vi.mocked(useEmbeddedChatbotContext).mockReturnValue({ ...defaultContext } as EmbeddedChatbotContextValue)
      const { unmount } = render(<Header title="Test Chatbot" />)
      expect(screen.getByTestId('divider')).toBeInTheDocument()
      unmount()

      vi.mocked(useEmbeddedChatbotContext).mockReturnValue({
        ...defaultContext,
        currentConversationId: '',
      } as EmbeddedChatbotContextValue)
      render(<Header title="Test Chatbot" />)
      expect(screen.queryByTestId('divider')).not.toBeInTheDocument()
    })

    it('should render reset button when allowResetChat is true and conversation exists', () => {
      render(<Header title="Test Chatbot" allowResetChat={true} />)

      expect(screen.getByRole('button', { name: 'share.chat.resetChat' })).toBeInTheDocument()
    })

    it('should call onCreateNewChat when reset button is clicked', async () => {
      const user = userEvent.setup()
      const onCreateNewChat = vi.fn()
      render(<Header title="Test Chatbot" allowResetChat={true} onCreateNewChat={onCreateNewChat} />)

      await user.click(screen.getByRole('button', { name: 'share.chat.resetChat' }))
      expect(onCreateNewChat).toHaveBeenCalled()
    })

    it('should render ViewFormDropdown when conditions are met', () => {
      vi.mocked(useEmbeddedChatbotContext).mockReturnValue({
        ...defaultContext,
        inputsForms: [{ id: '1' }],
        allInputsHidden: false,
      } as EmbeddedChatbotContextValue)

      render(<Header title="Test Chatbot" />)

      expect(screen.getByTestId('view-form-dropdown')).toBeInTheDocument()
    })

    it('should NOT render ViewFormDropdown when inputs are hidden', () => {
      vi.mocked(useEmbeddedChatbotContext).mockReturnValue({
        ...defaultContext,
        inputsForms: [{ id: '1' }],
        allInputsHidden: true,
      } as EmbeddedChatbotContextValue)

      render(<Header title="Test Chatbot" />)

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

    it('should NOT render ViewFormDropdown when currentConversationId is missing', () => {
      vi.mocked(useEmbeddedChatbotContext).mockReturnValue({
        ...defaultContext,
        currentConversationId: '',
        inputsForms: [{ id: '1' }],
      } as EmbeddedChatbotContextValue)

      render(<Header title="Test Chatbot" />)

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

  describe('Mobile Rendering', () => {
    it('should render mobile header with title', () => {
      render(<Header title="Mobile Chatbot" isMobile />)

      expect(screen.getByText('Mobile Chatbot')).toBeInTheDocument()
    })

    it('should render customer icon in mobile header', () => {
      render(<Header title="Mobile Chatbot" isMobile customerIcon={<div data-testid="custom-icon" />} />)

      expect(screen.getByTestId('custom-icon')).toBeInTheDocument()
    })

    it('should render mobile reset button when allowed', () => {
      render(<Header title="Mobile Chatbot" isMobile allowResetChat />)

      expect(screen.getByRole('button', { name: 'share.chat.resetChat' })).toBeInTheDocument()
    })

    it('should NOT render mobile reset button when currentConversationId is missing', () => {
      vi.mocked(useEmbeddedChatbotContext).mockReturnValue({
        ...defaultContext,
        currentConversationId: '',
      } as EmbeddedChatbotContextValue)
      render(<Header title="Mobile Chatbot" isMobile allowResetChat />)

      expect(screen.queryByRole('button', { name: 'share.chat.resetChat' })).not.toBeInTheDocument()
    })

    it('should render ViewFormDropdown in mobile when conditions are met', () => {
      vi.mocked(useEmbeddedChatbotContext).mockReturnValue({
        ...defaultContext,
        inputsForms: [{ id: '1' }],
      } as EmbeddedChatbotContextValue)
      render(<Header title="Mobile Chatbot" isMobile />)
      expect(screen.getByTestId('view-form-dropdown')).toBeInTheDocument()
    })

    it('should handle mobile expand button', async () => {
      const user = userEvent.setup()
      const mockPostMessage = setupIframe()
      render(<Header title="Mobile Chatbot" isMobile />)

      await dispatchChatbotConfigMessage('https://parent.com', { isToggledByButton: true, isDraggable: false })

      const expandBtn = await screen.findByRole('button', { name: 'share.chat.expand' })
      expect(expandBtn).toBeInTheDocument()

      await user.click(expandBtn)
      expect(mockPostMessage).toHaveBeenCalledWith(
        { type: 'dify-chatbot-expand-change' },
        'https://parent.com',
      )
    })
  })

  describe('Iframe Communication', () => {
    it('should send dify-chatbot-iframe-ready on mount', () => {
      const mockPostMessage = setupIframe()
      render(<Header title="Iframe" />)

      expect(mockPostMessage).toHaveBeenCalledWith(
        { type: 'dify-chatbot-iframe-ready' },
        '*',
      )
    })

    it('should update expand button visibility and handle click', async () => {
      const user = userEvent.setup()
      const mockPostMessage = setupIframe()
      render(<Header title="Iframe" />)

      await dispatchChatbotConfigMessage('https://parent.com', { isToggledByButton: true, isDraggable: false })

      const expandBtn = await screen.findByRole('button', { name: 'share.chat.expand' })
      expect(expandBtn).toBeInTheDocument()

      await user.click(expandBtn)

      expect(mockPostMessage).toHaveBeenCalledWith(
        { type: 'dify-chatbot-expand-change' },
        'https://parent.com',
      )
      expect(expandBtn.querySelector('.i-ri-collapse-diagonal-2-line')).toBeInTheDocument()
    })

    it('should NOT show expand button if isDraggable is true', async () => {
      setupIframe()
      render(<Header title="Iframe" />)

      await dispatchChatbotConfigMessage('https://parent.com', { isToggledByButton: true, isDraggable: true })

      await waitFor(() => {
        expect(screen.queryByRole('button', { name: 'share.chat.expand' })).not.toBeInTheDocument()
      })
    })

    it('should ignore messages from different origins after security lock', async () => {
      setupIframe()
      render(<Header title="Iframe" />)

      await dispatchChatbotConfigMessage('https://secure.com', { isToggledByButton: true, isDraggable: false })

      await screen.findByRole('button', { name: 'share.chat.expand' })

      await dispatchChatbotConfigMessage('https://malicious.com', { isToggledByButton: false, isDraggable: false })

      // Should still be visible (not hidden by the malicious message)
      expect(screen.getByRole('button', { name: 'share.chat.expand' })).toBeInTheDocument()
    })

    it('should ignore non-config messages for origin locking', async () => {
      setupIframe()
      render(<Header title="Iframe" />)

      await act(async () => {
        window.dispatchEvent(new MessageEvent('message', {
          origin: 'https://first.com',
          data: { type: 'other-type' },
        }))
      })

      await dispatchChatbotConfigMessage('https://second.com', { isToggledByButton: true, isDraggable: false })

      // Should lock to second.com
      const expandBtn = await screen.findByRole('button', { name: 'share.chat.expand' })
      expect(expandBtn).toBeInTheDocument()
    })

    it('should NOT handle toggle expand if showToggleExpandButton is false', async () => {
      const mockPostMessage = setupIframe()
      render(<Header title="Iframe" />)
      // Directly call handleToggleExpand would require more setup, but we can verify it doesn't trigger unexpectedly
      expect(mockPostMessage).not.toHaveBeenCalledWith(
        expect.objectContaining({ type: 'dify-chatbot-expand-change' }),
        expect.anything(),
      )
    })
  })

  describe('Edge Cases', () => {
    it('should handle document.referrer for targetOrigin', () => {
      const mockPostMessage = setupIframe()
      Object.defineProperty(document, 'referrer', { value: 'https://referrer.com', configurable: true })
      render(<Header title="Referrer" />)

      expect(mockPostMessage).toHaveBeenCalledWith(
        expect.anything(),
        'https://referrer.com',
      )
    })

    it('should NOT add message listener if not in iframe', () => {
      const addSpy = vi.spyOn(window, 'addEventListener')
      render(<Header title="Direct" />)
      expect(addSpy).not.toHaveBeenCalledWith('message', expect.any(Function))
    })
  })
})
