import { fireEvent, render, screen } from '@testing-library/react'
import { renderHook } from '@testing-library/react-hooks'

import {
  AGENT_BASE_PATH,
  AgentHelpCenterEntry,
  AgentPanel,
  PersistentAgentPanel,
  useAgentHelpCenterAvailability
} from '.'

const mockSetCurrentPath = jest.fn()
const mockSetAgentPanelExpanded = jest.fn()
const mockUseGetExpWhiteList = jest.fn()
let mockColorScheme: 'light' | 'dark' = 'light'
const mockTenantData = {
  id: 'org-123'
}
const mockHelpCenterState = {
  agentPanelExpanded: true,
  currentPath: AGENT_BASE_PATH,
  dialogOpened: true
}

jest.mock('@tidbcloud/uikit', () => {
  const Component = ({
    children,
    'aria-hidden': ariaHidden,
    'data-testid': dataTestId,
    display
  }: React.PropsWithChildren<{
    'aria-hidden'?: boolean
    'data-testid'?: string
    display?: React.CSSProperties['display']
  }>) => (
    <div data-testid={dataTestId} aria-hidden={ariaHidden} style={{ display }}>
      {children}
    </div>
  )

  return {
    ActionIcon: ({
      children,
      onClick,
      ...rest
    }: React.PropsWithChildren<React.ButtonHTMLAttributes<HTMLButtonElement>>) => (
      <button onClick={onClick} {...rest}>
        {children}
      </button>
    ),
    Box: Component,
    Center: Component,
    Flex: Component,
    Loader: () => <div data-testid="loader" />,
    useComputedColorScheme: () => mockColorScheme
  }
})

jest.mock('@tidbcloud/uikit/icons', () => ({
  IconCube01: ({ strokeWidth }: { strokeWidth?: number }) => (
    <span data-testid="cube-icon" data-stroke-width={strokeWidth} />
  ),
  IconExpand03: ({ size }: { size?: number }) => <span data-testid="expand-03-icon" data-size={size} />,
  IconScaleDown: ({ size }: { size?: number }) => <span data-testid="scale-down-icon" data-size={size} />
}))

jest.mock('mobx-react', () => ({
  observer: (component: unknown) => component
}))

jest.mock(
  'config',
  () => ({
    __esModule: true,
    default: {
      AGENT_IFRAME_URL: 'https://external-customer.dev.investibook.tidbcloud.com/'
    }
  }),
  { virtual: true }
)

jest.mock('dbaas/utils/tracking', () => ({
  MixpanelEvents: {
    HelpCenterAgent: {
      Operate: {
        ButtonClicked: 'HelpCenterAgent_Operate_ButtonClicked'
      },
      View: {
        ButtonClicked: 'HelpCenterAgent_View_ButtonClicked'
      }
    }
  }
}))

jest.mock('dbaas/services', () => ({
  useGetExpWhiteList: (...args: unknown[]) => mockUseGetExpWhiteList(...args)
}))

jest.mock('dbaas/stores/useStores', () => ({
  __esModule: true,
  default: () => ({
    store: {
      tenantData: mockTenantData
    }
  })
}))

jest.mock('../CenterCard', () => {
  const MockHelpCenterCard = ({ children }: React.PropsWithChildren) => <>{children}</>
  MockHelpCenterCard.Header = ({
    children,
    rightSection
  }: React.PropsWithChildren<{ rightSection?: React.ReactNode }>) => (
    <header>
      {children}
      {rightSection}
    </header>
  )
  MockHelpCenterCard.Content = ({ children }: React.PropsWithChildren) => <main>{children}</main>

  return {
    EntryCard: ({
      title,
      description,
      avatar,
      onClick,
      dataTestId,
      dataMpEvent
    }: {
      title: React.ReactNode
      description?: React.ReactNode
      avatar?: React.ReactNode
      onClick: () => void
      dataTestId: string
      dataMpEvent: string
    }) => (
      <button data-testid={dataTestId} data-mp-event={dataMpEvent} onClick={onClick}>
        {title}
        {description}
        {avatar}
      </button>
    ),
    HelpCenterCard: MockHelpCenterCard
  }
})

jest.mock('../CenterRoute', () => ({
  CenterBreadcrumbs: ({ items }: { items: Array<{ title: string }> }) => (
    <nav>{items.map((item) => item.title).join(' / ')}</nav>
  )
}))

jest.mock('../context', () => ({
  DEFAULT_HELP_PATH: '/help',
  useHelpCenterContext: () => ({
    ...mockHelpCenterState,
    setAgentPanelExpanded: mockSetAgentPanelExpanded,
    setCurrentPath: mockSetCurrentPath
  })
}))

const PersistentAgent = ({ enabled = true }: { enabled?: boolean }) => (
  <PersistentAgentPanel enabled={enabled}>
    <AgentPanel />
  </PersistentAgentPanel>
)

describe('Help Center Agent', () => {
  beforeEach(() => {
    mockHelpCenterState.agentPanelExpanded = true
    mockHelpCenterState.currentPath = AGENT_BASE_PATH
    mockHelpCenterState.dialogOpened = true
    mockTenantData.id = 'org-123'
    mockColorScheme = 'light'
    mockSetAgentPanelExpanded.mockClear()
    mockSetCurrentPath.mockClear()
    mockUseGetExpWhiteList.mockReset()
    mockUseGetExpWhiteList.mockReturnValue({ data: false, isFetched: true, isFetching: false })
  })

  it('only enables Agent for organizations on the investibook_on_console whitelist', () => {
    mockUseGetExpWhiteList.mockReturnValue({ data: true, isFetched: true, isFetching: false })

    const { result } = renderHook(() => useAgentHelpCenterAvailability())

    expect(result.current).toEqual({ enabled: true, resolved: true })
    expect(mockUseGetExpWhiteList).toHaveBeenCalledWith('org-123', undefined, expect.any(Object))

    const queryOptions = mockUseGetExpWhiteList.mock.calls[0][2].query
    expect(queryOptions.enabled).toBe(true)
    expect(queryOptions.select({ exp_names: ['investibook_on_console'] })).toBe(true)
    expect(queryOptions.select({ exp_names: ['another_feature'] })).toBe(false)
  })

  it('keeps Agent hidden when the organization is not whitelisted', () => {
    const { result } = renderHook(() => useAgentHelpCenterAvailability())

    expect(result.current).toEqual({ enabled: false, resolved: true })
  })

  it('does not resolve Agent availability while the whitelist is loading', () => {
    mockUseGetExpWhiteList.mockReturnValue({ data: undefined, isFetched: false, isFetching: true })

    const { result } = renderHook(() => useAgentHelpCenterAvailability())

    expect(result.current).toEqual({ enabled: false, resolved: false })
  })

  it('opens the Agent route from the Help Center entry', () => {
    render(<AgentHelpCenterEntry />)

    const entry = screen.getByTestId('agent-entry')
    expect(entry.textContent).toContain('Chat with Investibook for Diagnosis')
    expect(entry.textContent).not.toContain('Investigate this cluster, alerts, and SQL performance')
    expect(screen.queryByTestId('cube-icon')).not.toBeNull()
    expect(screen.getByTestId('cube-icon').getAttribute('data-stroke-width')).toBe('1.5')
    expect(entry.getAttribute('data-mp-event')).toBe('HelpCenterAgent_View_ButtonClicked')

    fireEvent.click(entry)

    expect(mockSetCurrentPath).toHaveBeenCalledWith(AGENT_BASE_PATH)
  })

  it('loads the configured Agent URL with restricted iframe permissions', () => {
    render(<AgentPanel />)

    const iframe = screen.getByTestId('agent-iframe') as HTMLIFrameElement
    expect(iframe.getAttribute('title')).toBe('Investibook')
    expect(screen.queryByText('Help / Investibook')).not.toBeNull()
    expect(iframe.getAttribute('src')).toBe(
      'https://external-customer.dev.investibook.tidbcloud.com/?orgId=org-123&theme=light'
    )
    expect(iframe.getAttribute('sandbox')).toBe('allow-scripts allow-forms allow-same-origin allow-downloads')
    expect(iframe.getAttribute('allow')).toBe('clipboard-write')
    expect(iframe.getAttribute('referrerpolicy')).toBe('no-referrer')
    expect(screen.queryByTestId('agent-loading')).not.toBeNull()

    const postMessage = jest.spyOn(iframe.contentWindow!, 'postMessage')

    fireEvent.load(iframe)

    expect(screen.queryByTestId('agent-loading')).toBeNull()
    expect(postMessage).toHaveBeenCalledWith(
      {
        channel: 'tidbcloud.tipilot',
        version: 1,
        type: 'tidbcloud.appearance.update',
        payload: { theme: 'light' }
      },
      'https://external-customer.dev.investibook.tidbcloud.com'
    )
  })

  it('sends the latest theme on load when the theme changes while loading', () => {
    const { rerender } = render(<AgentPanel />)
    const iframe = screen.getByTestId('agent-iframe') as HTMLIFrameElement
    const initialSrc = iframe.getAttribute('src')
    const postMessage = jest.spyOn(iframe.contentWindow!, 'postMessage')

    mockColorScheme = 'dark'
    rerender(<AgentPanel />)

    expect(screen.getByTestId('agent-iframe')).toBe(iframe)
    expect(iframe.getAttribute('src')).toBe(initialSrc)
    expect(postMessage).not.toHaveBeenCalled()

    fireEvent.load(iframe)

    expect(postMessage).toHaveBeenCalledWith(
      expect.objectContaining({ payload: { theme: 'dark' } }),
      'https://external-customer.dev.investibook.tidbcloud.com'
    )
  })

  it('initializes a new iframe with the current dark theme', () => {
    mockColorScheme = 'dark'

    render(<AgentPanel />)

    expect(screen.getByTestId('agent-iframe').getAttribute('src')).toBe(
      'https://external-customer.dev.investibook.tidbcloud.com/?orgId=org-123&theme=dark'
    )
  })

  it('pushes theme changes without reloading the initialized iframe', () => {
    const { rerender } = render(<AgentPanel />)
    const iframe = screen.getByTestId('agent-iframe') as HTMLIFrameElement
    const initialSrc = iframe.getAttribute('src')
    const postMessage = jest.spyOn(iframe.contentWindow!, 'postMessage')

    fireEvent.load(iframe)
    postMessage.mockClear()

    mockColorScheme = 'dark'
    rerender(<AgentPanel />)

    expect(screen.getByTestId('agent-iframe')).toBe(iframe)
    expect(iframe.getAttribute('src')).toBe(initialSrc)
    expect(postMessage).toHaveBeenCalledTimes(1)
    expect(postMessage).toHaveBeenCalledWith(
      expect.objectContaining({ payload: { theme: 'dark' } }),
      'https://external-customer.dev.investibook.tidbcloud.com'
    )
  })

  it('shows the scale-down action at full height', () => {
    render(<AgentPanel />)

    const scaleDown = screen.getByTestId('agent-panel-scale-down')
    expect(scaleDown.getAttribute('aria-label')).toBe('Scale down Investibook panel')
    expect(scaleDown.getAttribute('data-mp-event')).toBe('HelpCenterAgent_Operate_ButtonClicked')
    expect(scaleDown.getAttribute('data-mp-action')).toBe('scale_down')
    expect(screen.getByTestId('scale-down-icon').getAttribute('data-size')).toBe('12')
    expect(screen.queryByTestId('agent-panel-scale-up')).toBeNull()

    fireEvent.click(scaleDown)

    expect(mockSetAgentPanelExpanded).toHaveBeenCalledWith(false)
  })

  it('shows the scale-up action at normal height', () => {
    mockHelpCenterState.agentPanelExpanded = false

    render(<AgentPanel />)

    const scaleUp = screen.getByTestId('agent-panel-scale-up')
    expect(scaleUp.getAttribute('aria-label')).toBe('Scale up Investibook panel')
    expect(scaleUp.getAttribute('data-mp-event')).toBe('HelpCenterAgent_Operate_ButtonClicked')
    expect(scaleUp.getAttribute('data-mp-action')).toBe('scale_up')
    expect(screen.getByTestId('expand-03-icon').getAttribute('data-size')).toBe('12')
    expect(screen.queryByTestId('agent-panel-scale-down')).toBeNull()

    fireEvent.click(scaleUp)

    expect(mockSetAgentPanelExpanded).toHaveBeenCalledWith(true)
  })

  it('keeps the iframe mounted while changing panel height', () => {
    const { rerender } = render(<AgentPanel />)
    const iframe = screen.getByTestId('agent-iframe') as HTMLIFrameElement

    mockHelpCenterState.agentPanelExpanded = false
    rerender(<AgentPanel />)

    expect(screen.getByTestId('agent-iframe')).toBe(iframe)
  })

  it('keeps the initialized iframe mounted while the Help Center is minimized', () => {
    const { rerender } = render(<PersistentAgent />)
    const iframe = screen.getByTestId('agent-iframe') as HTMLIFrameElement
    const postMessage = jest.spyOn(iframe.contentWindow!, 'postMessage')

    fireEvent.load(iframe)
    postMessage.mockClear()
    mockHelpCenterState.dialogOpened = false
    mockColorScheme = 'dark'
    rerender(<PersistentAgent />)

    expect(screen.getByTestId('agent-iframe')).toBe(iframe)
    expect(screen.queryByTestId('agent-loading')).toBeNull()
    expect(postMessage).toHaveBeenCalledWith(
      expect.objectContaining({ payload: { theme: 'dark' } }),
      'https://external-customer.dev.investibook.tidbcloud.com'
    )

    mockHelpCenterState.dialogOpened = true
    rerender(<PersistentAgent />)

    expect(screen.getByTestId('agent-iframe')).toBe(iframe)
    expect(screen.queryByTestId('agent-loading')).toBeNull()
  })

  it('does not initialize Agent before its route is visited', () => {
    mockHelpCenterState.currentPath = '/help'

    render(<PersistentAgent />)

    expect(screen.queryByTestId('agent-iframe')).toBeNull()
  })

  it('keeps the initialized iframe mounted while visiting other Help Center routes', () => {
    const { rerender } = render(<PersistentAgent />)
    const iframe = screen.getByTestId('agent-iframe') as HTMLIFrameElement
    const postMessage = jest.spyOn(iframe.contentWindow!, 'postMessage')

    fireEvent.load(iframe)
    postMessage.mockClear()
    mockHelpCenterState.currentPath = '/help'
    mockColorScheme = 'dark'
    rerender(<PersistentAgent />)

    expect(screen.getByTestId('agent-iframe')).toBe(iframe)
    expect(screen.getByTestId('agent-panel-host').getAttribute('aria-hidden')).toBe('true')
    expect(screen.getByTestId('agent-panel-host').style.display).toBe('none')
    expect(screen.queryByTestId('agent-loading')).toBeNull()
    expect(postMessage).toHaveBeenCalledWith(
      expect.objectContaining({ payload: { theme: 'dark' } }),
      'https://external-customer.dev.investibook.tidbcloud.com'
    )

    mockHelpCenterState.currentPath = AGENT_BASE_PATH
    mockColorScheme = 'dark'
    rerender(<PersistentAgent />)

    expect(screen.getByTestId('agent-iframe')).toBe(iframe)
    expect(screen.getByTestId('agent-panel-host').getAttribute('aria-hidden')).toBe('false')
  })

  it('disposes the initialized iframe when Agent becomes disabled', () => {
    const { rerender } = render(<PersistentAgent />)

    expect(screen.queryByTestId('agent-iframe')).not.toBeNull()

    rerender(<PersistentAgent enabled={false} />)

    expect(screen.queryByTestId('agent-iframe')).toBeNull()
  })

  it('creates a new iframe for a different organization', () => {
    const { rerender } = render(<PersistentAgent />)
    const previousIframe = screen.getByTestId('agent-iframe') as HTMLIFrameElement

    mockHelpCenterState.currentPath = '/help'
    rerender(<PersistentAgent />)

    mockTenantData.id = 'org-456'
    rerender(<PersistentAgent />)

    expect(screen.queryByTestId('agent-iframe')).toBeNull()

    mockColorScheme = 'dark'
    mockHelpCenterState.currentPath = AGENT_BASE_PATH
    rerender(<PersistentAgent />)

    const nextIframe = screen.getByTestId('agent-iframe') as HTMLIFrameElement
    expect(nextIframe).not.toBe(previousIframe)
    expect(nextIframe.getAttribute('src')).toBe(
      'https://external-customer.dev.investibook.tidbcloud.com/?orgId=org-456&theme=dark'
    )
  })

  it('keeps Agent unmounted when it is disabled', () => {
    render(<PersistentAgent enabled={false} />)

    expect(screen.queryByTestId('agent-iframe')).toBeNull()
  })
})
