import { act, fireEvent, render, screen } from '@testing-library/react'
import { createMemoryHistory } from 'history'
import { Router } from 'react-router-dom'

import { sendVerificationEmail } from 'dbaas/services/account'

import Verify from './index'

const setHrefMock = jest.fn()
let locationSearch = ''
const sendVerificationEmailMock = sendVerificationEmail as jest.MockedFunction<typeof sendVerificationEmail>

jest.mock('@tidbcloud/uikit', () => ({
  Button: ({ children, disabled, onClick }: React.PropsWithChildren<{ disabled?: boolean; onClick?: () => void }>) => (
    <button disabled={disabled} onClick={onClick}>
      {children}
    </button>
  ),
  Card: ({ children }: React.PropsWithChildren) => <div>{children}</div>,
  Center: ({ children }: React.PropsWithChildren) => <div>{children}</div>,
  Divider: () => <hr />,
  Flex: ({ children }: React.PropsWithChildren) => <div>{children}</div>,
  FormErrorMessage: ({ message }: { message: string }) => <div>{message}</div>,
  Typography: ({ children }: React.PropsWithChildren) => <div>{children}</div>
}))

jest.mock('@tidbcloud/uikit/icons', () => ({
  IconAlertCircle: () => null,
  IconCheckCircle: () => null,
  IconMail05: () => null
}))

jest.mock('dbaas/components/Link', () => ({
  __esModule: true,
  default: ({ children, href }: React.PropsWithChildren<{ href: string }>) => <a href={href}>{children}</a>
}))

jest.mock('dbaas/services/account', () => ({
  sendVerificationEmail: jest.fn()
}))

jest.mock('dbaas/services/errorCodes', () => ({
  getErrorMessage: () => 'Unable to resend verification email.'
}))

jest.mock('dbaas/utils/tracking', () => ({
  eventTracking: jest.fn()
}))

jest.mock('../Components/helpers', () => ({
  useIsMdScreen: () => false
}))

jest.mock('../Components/Shell', () => ({
  PageShell: ({ children }: React.PropsWithChildren) => <div>{children}</div>
}))

const renderVerify = (search: string) => {
  locationSearch = search
  const history = createMemoryHistory({ initialEntries: [`/oauth/verify/callback${search}`] })
  window.history.pushState({}, '', `/oauth/verify/callback${search}`)

  const result = render(
    <Router history={history}>
      <Verify />
    </Router>
  )

  return { history, ...result }
}

describe('OAuth verification callback', () => {
  const originalLocation = window.location

  beforeEach(() => {
    jest.useFakeTimers()
    setHrefMock.mockReset()
    sendVerificationEmailMock.mockReset()
    sendVerificationEmailMock.mockResolvedValue(undefined as never)
    locationSearch = ''

    Object.defineProperty(window, 'location', {
      configurable: true,
      value: {
        ...window.location,
        origin: 'http://localhost',
        get search() {
          return locationSearch
        },
        get href() {
          return 'http://localhost/oauth/verify/callback'
        },
        set href(value: string) {
          setHrefMock(value)
        }
      }
    })
  })

  afterEach(() => {
    jest.useRealTimers()
    Object.defineProperty(window, 'location', {
      configurable: true,
      value: originalLocation
    })
  })

  test('falls back to sign in when Continue receives an executable return target', () => {
    renderVerify('?status=success&returnTo=javascript%3Aalert(document.cookie)')

    fireEvent.click(screen.getByRole('button', { name: /Continue/ }))

    expect(setHrefMock).toHaveBeenCalledWith('/oauth/signin')
  })

  test('falls back to sign in after the countdown for a cross-origin return target', () => {
    renderVerify('?status=success&returnTo=https%3A%2F%2Fexample.org%2F')

    act(() => {
      jest.advanceTimersByTime(5000)
    })

    expect(setHrefMock).toHaveBeenCalledWith('/oauth/signin')
  })

  test('continues to a valid internal return target', () => {
    renderVerify('?status=success&returnTo=%2Foauth%2Fauthorize%3Fclient_id%3Dtest')

    fireEvent.click(screen.getByRole('button', { name: /Continue/ }))

    expect(setHrefMock).toHaveBeenCalledWith('/oauth/authorize?client_id=test')
  })

  test('does not propagate an executable target when resending or returning to sign in', () => {
    const { history } = renderVerify(
      '?status=verifying&email=person%40example.com&usage=oauth-authorize&returnTo=javascript%3Aalert(document.cookie)'
    )

    fireEvent.click(screen.getByRole('button', { name: 'Resend' }))
    expect(sendVerificationEmailMock).toHaveBeenCalledWith({ email: 'person@example.com', return_url: undefined })

    fireEvent.click(screen.getByRole('button', { name: 'Back and Reconnect' }))
    expect(history.location.pathname).toBe('/oauth/signin')
    expect(history.location.search).toBe('')
  })
})
