import type { EmbeddedChatbotContextValue } from '../../context'
import type { AppData } from '@/models/share'
import type { SystemFeatures } from '@/types/feature'
import { act, render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { useGlobalPublicStore } from '@/context/global-public-context'
import { InstallationScope, LicenseStatus } from '@/types/feature'
import { useEmbeddedChatbotContext } from '../../context'
import Header from '../index'

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

vi.mock('@/context/global-public-context', () => ({
  useGlobalPublicStore: vi.fn(),
}))

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

type GlobalPublicStoreMock = {
  systemFeatures: SystemFeatures
  setSystemFeatures: (systemFeatures: SystemFeatures) => void
}

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 defaultSystemFeatures: SystemFeatures = {
    trial_models: [],
    plugin_installation_permission: {
      plugin_installation_scope: InstallationScope.ALL,
      restrict_to_marketplace_only: false,
    },
    sso_enforced_for_signin: false,
    sso_enforced_for_signin_protocol: '',
    sso_enforced_for_web: false,
    sso_enforced_for_web_protocol: '',
    enable_marketplace: false,
    enable_change_email: false,
    enable_email_code_login: false,
    enable_email_password_login: false,
    enable_social_oauth_login: false,
    is_allow_create_workspace: false,
    is_allow_register: false,
    is_email_setup: false,
    license: {
      status: LicenseStatus.NONE,
      expired_at: '',
    },
    branding: {
      enabled: true,
      workspace_logo: '',
      login_page_logo: '',
      favicon: '',
      application_title: '',
    },
    webapp_auth: {
      enabled: false,
      allow_sso: false,
      sso_config: { protocol: '' },
      allow_email_code_login: false,
      allow_email_password_login: false,
    },
    enable_trial_app: false,
    enable_explore_banner: 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()
    vi.mocked(useEmbeddedChatbotContext).mockReturnValue(defaultContext as EmbeddedChatbotContextValue)
    vi.mocked(useGlobalPublicStore).mockImplementation((selector: (s: GlobalPublicStoreMock) => unknown) => selector({
      systemFeatures: defaultSystemFeatures,
      setSystemFeatures: vi.fn(),
    }))

    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', () => {
      vi.mocked(useGlobalPublicStore).mockImplementation((selector: (s: GlobalPublicStoreMock) => unknown) => selector({
        systemFeatures: {
          ...defaultSystemFeatures,
          branding: {
            ...defaultSystemFeatures.branding,
            workspace_logo: 'https://example.com/workspace.png',
          },
        },
        setSystemFeatures: vi.fn(),
      }))

      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', () => {
      vi.mocked(useGlobalPublicStore).mockImplementation((selector: (s: GlobalPublicStoreMock) => unknown) => selector({
        systemFeatures: {
          ...defaultSystemFeatures,
          branding: {
            ...defaultSystemFeatures.branding,
            enabled: true,
            workspace_logo: '',
          },
        },
        setSystemFeatures: vi.fn(),
      }))
      render(<Header title="Test Chatbot" />)
      expect(screen.getByAltText('Dify logo')).toBeInTheDocument()
    })

    it('should render Dify logo when branding is disabled', () => {
      vi.mocked(useGlobalPublicStore).mockImplementation((selector: (s: GlobalPublicStoreMock) => unknown) => selector({
        systemFeatures: {
          ...defaultSystemFeatures,
          branding: {
            ...defaultSystemFeatures.branding,
            enabled: false,
          },
        },
        setSystemFeatures: vi.fn(),
      }))
      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.getByTestId('reset-chat-button')).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.getByTestId('reset-chat-button'))
      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.getByTestId('mobile-reset-chat-button')).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.queryByTestId('mobile-reset-chat-button')).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.findByTestId('mobile-expand-button')
      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.findByTestId('expand-button')
      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.queryByTestId('expand-button')).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.findByTestId('expand-button')

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

      // Should still be visible (not hidden by the malicious message)
      expect(screen.getByTestId('expand-button')).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.findByTestId('expand-button')
      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))
    })
  })
})
