import type { ReactElement, ReactNode } from 'react'
import { render, screen, waitFor, within } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { message } from 'antd'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'

import type { Webhook } from '@/types/webhook'
import { WebhooksPage } from './WebhooksPage'

const originalGetComputedStyle = window.getComputedStyle
const listWebhooksMock = vi.fn<() => Promise<{ data: Webhook[]; total: number; page: number; page_size: number }>>()
const deleteWebhookMock = vi.fn<(id: number) => Promise<void>>()
const updateWebhookStatusMock = vi.fn<(id: number, status: 0 | 1) => Promise<void>>()

vi.mock('antd', async () => {
  const actual = await vi.importActual<typeof import('antd')>('antd')

  return {
    ...actual,
    Popconfirm: ({
      children,
      title,
      onConfirm,
    }: {
      children: ReactElement
      title?: ReactNode
      onConfirm?: () => void
    }) => (
      <div>
        {children}
        <button type="button" onClick={() => onConfirm?.()}>
          {typeof title === 'string' ? title : 'confirm action'}
        </button>
      </div>
    ),
  }
})

vi.mock('@/services/webhooks', () => ({
  listWebhooks: () => listWebhooksMock(),
  deleteWebhook: (id: number) => deleteWebhookMock(id),
  updateWebhookStatus: (id: number, status: 0 | 1) => updateWebhookStatusMock(id, status),
}))

vi.mock('./WebhookFormModal', () => ({
  WebhookFormModal: ({
    open,
    webhook,
    onSuccess,
    onClose,
  }: {
    open: boolean
    webhook: Pick<Webhook, 'name'> | null
    onSuccess: () => void
    onClose: () => void
  }) => {
    if (!open) {
      return null
    }

    return (
      <div data-testid="webhook-form-modal">
        <span>{webhook ? `edit:${webhook.name}` : 'create'}</span>
        <button onClick={onSuccess}>form success</button>
        <button onClick={onClose}>form close</button>
      </div>
    )
  },
}))

vi.mock('./WebhookDeliveriesDrawer', () => ({
  WebhookDeliveriesDrawer: ({
    open,
    webhook,
    onClose,
  }: {
    open: boolean
    webhook: Pick<Webhook, 'name'> | null
    onClose: () => void
  }) => {
    if (!open) {
      return null
    }

    return (
      <div data-testid="webhook-deliveries-drawer">
        <span>{webhook ? `drawer:${webhook.name}` : 'drawer:none'}</span>
        <button onClick={onClose}>drawer close</button>
      </div>
    )
  },
}))

const seedWebhooks: Webhook[] = [
  {
    id: 1,
    name: 'Alpha Hook',
    url: 'https://alpha.example.com/webhook',
    events: ['user.registered'],
    status: 1,
    max_retries: 3,
    timeout_sec: 10,
    created_by: 100,
    created_at: '2026-03-27 20:00:00',
    updated_at: '2026-03-27 20:00:00',
  },
  {
    id: 2,
    name: 'Beta Hook',
    url: 'https://beta.example.com/webhook',
    events: ['user.login'],
    status: 0,
    max_retries: 5,
    timeout_sec: 15,
    created_by: 200,
    created_at: '2026-03-27 20:05:00',
    updated_at: '2026-03-27 20:05:00',
  },
]

function cloneWebhook(webhook: Webhook): Webhook {
  return {
    ...webhook,
    events: [...webhook.events],
  }
}

function getRowByCellText(text: string): HTMLElement {
  const cell = screen.getByText(text)
  const row = cell.closest('tr')
  if (!row) {
    throw new Error(`unable to resolve table row for ${text}`)
  }
  return row as HTMLElement
}

describe('WebhooksPage', () => {
  let currentWebhooks: Webhook[]

  beforeEach(() => {
    currentWebhooks = seedWebhooks.map(cloneWebhook)

    listWebhooksMock.mockReset()
    deleteWebhookMock.mockReset()
    updateWebhookStatusMock.mockReset()

    listWebhooksMock.mockImplementation(async () => ({
      data: currentWebhooks.map(cloneWebhook),
      total: currentWebhooks.length,
      page: 1,
      page_size: 20,
    }))
    deleteWebhookMock.mockImplementation(async (id: number) => {
      currentWebhooks = currentWebhooks.filter((webhook) => webhook.id !== id)
    })
    updateWebhookStatusMock.mockImplementation(async (id: number, status: 0 | 1) => {
      currentWebhooks = currentWebhooks.map((webhook) => (
        webhook.id === id ? { ...webhook, status } : webhook
      ))
    })

    vi.spyOn(window, 'getComputedStyle').mockImplementation((element) => (
      originalGetComputedStyle.call(window, element)
    ))
    vi.spyOn(message, 'success').mockImplementation(() => undefined as never)
    vi.spyOn(message, 'error').mockImplementation(() => undefined as never)
  })

  afterEach(() => {
    vi.restoreAllMocks()
  })

  it('loads, filters, resets, and refreshes the webhook list', async () => {
    const user = userEvent.setup()

    render(<WebhooksPage />)

    expect(await screen.findByText('Alpha Hook')).toBeInTheDocument()
    expect(screen.getByText('Beta Hook')).toBeInTheDocument()

    await user.type(screen.getByPlaceholderText('Webhook 名称或 URL'), 'beta')

    await waitFor(() => expect(screen.queryByText('Alpha Hook')).not.toBeInTheDocument())
    expect(screen.getByText('Beta Hook')).toBeInTheDocument()

    await user.click(screen.getByRole('button', { name: /重\s*置/ }))

    await waitFor(() => expect(screen.getByText('Alpha Hook')).toBeInTheDocument())
    expect(screen.getByText('Beta Hook')).toBeInTheDocument()

    await user.click(screen.getByRole('button', { name: /刷新/ }))

    await waitFor(() => expect(listWebhooksMock).toHaveBeenCalledTimes(2))
  })

  it('opens create, edit, and deliveries overlays and refetches after modal success', async () => {
    const user = userEvent.setup()

    render(<WebhooksPage />)

    expect(await screen.findByText('Alpha Hook')).toBeInTheDocument()

    await user.click(screen.getByRole('button', { name: /创建 Webhook/ }))
    expect(screen.getByTestId('webhook-form-modal')).toHaveTextContent('create')

    await user.click(screen.getByRole('button', { name: 'form success' }))

    await waitFor(() => expect(listWebhooksMock).toHaveBeenCalledTimes(2))
    await waitFor(() => expect(screen.queryByTestId('webhook-form-modal')).not.toBeInTheDocument())

    const alphaRow = getRowByCellText('Alpha Hook')

    await user.click(within(alphaRow).getByRole('button', { name: /编辑/ }))
    expect(screen.getByTestId('webhook-form-modal')).toHaveTextContent('edit:Alpha Hook')

    await user.click(screen.getByRole('button', { name: 'form close' }))
    await waitFor(() => expect(screen.queryByTestId('webhook-form-modal')).not.toBeInTheDocument())

    await user.click(within(alphaRow).getByRole('button', { name: /记录/ }))
    expect(screen.getByTestId('webhook-deliveries-drawer')).toHaveTextContent('drawer:Alpha Hook')

    await user.click(screen.getByRole('button', { name: 'drawer close' }))
    await waitFor(() => expect(screen.queryByTestId('webhook-deliveries-drawer')).not.toBeInTheDocument())
  })

  it('updates webhook status and deletes webhooks with a refreshed list', async () => {
    const user = userEvent.setup()

    render(<WebhooksPage />)

    expect(await screen.findByText('Alpha Hook')).toBeInTheDocument()

    await user.click(within(getRowByCellText('Alpha Hook')).getByRole('button', { name: /^禁用$/ }))
    await user.click(within(getRowByCellText('Alpha Hook')).getByRole('button', { name: '确定禁用该 Webhook 吗？' }))

    await waitFor(() => expect(updateWebhookStatusMock).toHaveBeenCalledWith(1, 0))
    expect(message.success).toHaveBeenCalledWith('Webhook 状态已更新')
    expect(within(getRowByCellText('Alpha Hook')).getByRole('button', { name: '启用' })).toBeInTheDocument()

    await user.click(within(getRowByCellText('Beta Hook')).getByRole('button', { name: /^delete 删除$|^删除$/ }))
    await user.click(within(getRowByCellText('Beta Hook')).getByRole('button', { name: '确定删除该 Webhook 吗？' }))

    await waitFor(() => expect(deleteWebhookMock).toHaveBeenCalledWith(2))
    expect(message.success).toHaveBeenCalledWith('Webhook 已删除')
    await waitFor(() => expect(screen.queryByText('Beta Hook')).not.toBeInTheDocument())
  })

  it('renders the page error state and retries loading', async () => {
    const user = userEvent.setup()

    listWebhooksMock.mockReset()
    listWebhooksMock.mockRejectedValueOnce(new Error('列表拉取失败'))
    listWebhooksMock.mockImplementation(async () => ({
      data: currentWebhooks.map(cloneWebhook),
      total: currentWebhooks.length,
      page: 1,
      page_size: 20,
    }))

    render(<WebhooksPage />)

    expect(await screen.findByText('列表拉取失败')).toBeInTheDocument()

    await user.click(screen.getByRole('button', { name: /重新加载/ }))

    await waitFor(() => expect(screen.getByText('Alpha Hook')).toBeInTheDocument())
    expect(listWebhooksMock).toHaveBeenCalledTimes(2)
  })

  it('shows an error message when status updates fail', async () => {
    const user = userEvent.setup()

    updateWebhookStatusMock.mockRejectedValueOnce(new Error('状态更新失败'))

    render(<WebhooksPage />)

    expect(await screen.findByText('Alpha Hook')).toBeInTheDocument()

    await user.click(within(getRowByCellText('Alpha Hook')).getByRole('button', { name: /^禁用$/ }))
    await user.click(within(getRowByCellText('Alpha Hook')).getByRole('button', { name: '确定禁用该 Webhook 吗？' }))

    await waitFor(() => expect(message.error).toHaveBeenCalledWith('状态更新失败'))
    expect(listWebhooksMock).toHaveBeenCalledTimes(1)
  })
})
