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

import type { Webhook, WebhookDelivery } from '@/types/webhook'
import { WebhookDeliveriesDrawer } from './WebhookDeliveriesDrawer'

const getWebhookDeliveriesMock = vi.fn()
const messageErrorMock = vi.fn()

vi.mock('antd', () => {
  const Descriptions = ({
    children,
  }: {
    children?: ReactNode
  }) => <div>{children}</div>

  function resolveRowKey<RecordType extends Record<string, unknown>>(
    record: RecordType,
    rowKey: string | ((row: RecordType) => string | number) | undefined,
    index: number,
  ): string {
    if (typeof rowKey === 'function') {
      return String(rowKey(record))
    }
    if (typeof rowKey === 'string') {
      return String(record[rowKey] ?? index)
    }
    return String(index)
  }

  return {
    Button: ({
      children,
      onClick,
    }: {
      children?: ReactNode
      onClick?: () => void
    }) => (
      <button type="button" onClick={onClick}>
        {children}
      </button>
    ),
    Descriptions: Object.assign(Descriptions, {
      Item: ({
        label,
        children,
      }: {
        label?: ReactNode
        children?: ReactNode
      }) => (
        <div>
          <span>{label}</span>
          <span>{children}</span>
        </div>
      ),
    }),
    Drawer: ({
      open,
      title,
      onClose,
      extra,
      children,
    }: {
      open: boolean
      title?: ReactNode
      onClose?: () => void
      extra?: ReactNode
      children?: ReactNode
    }) => (
      open ? (
        <div data-testid="webhook-deliveries-drawer-root">
          <h1>{title}</h1>
          {extra}
          {children}
          <button type="button" onClick={onClose}>drawer close</button>
        </div>
      ) : null
    ),
    Table: ({
      columns,
      dataSource,
      rowKey,
    }: {
      columns: Array<{
        key?: string
        title?: ReactNode
        dataIndex?: string
        render?: (value: unknown, record: Record<string, unknown>, index: number) => ReactNode
      }>
      dataSource?: Array<Record<string, unknown>>
      rowKey?: string | ((row: Record<string, unknown>) => string | number)
    }) => {
      const rows = dataSource ?? []

      return (
        <table>
          <thead>
            <tr>
              {columns.map((column, index) => (
                <th key={column.key ?? column.dataIndex ?? index}>{column.title}</th>
              ))}
            </tr>
          </thead>
          <tbody>
            {rows.map((record, rowIndex) => (
              <tr
                key={resolveRowKey(record, rowKey, rowIndex)}
                data-testid={`delivery-row-${resolveRowKey(record, rowKey, rowIndex)}`}
              >
                {columns.map((column, columnIndex) => {
                  const value = column.dataIndex ? record[column.dataIndex] : undefined
                  const content = column.render ? column.render(value, record, rowIndex) : value
                  return (
                    <td key={column.key ?? column.dataIndex ?? columnIndex}>
                      {content as ReactNode}
                    </td>
                  )
                })}
              </tr>
            ))}
          </tbody>
        </table>
      )
    },
    Tag: ({ children }: { children?: ReactNode }) => <span>{children}</span>,
    Typography: {
      Text: ({ children }: { children?: ReactNode }) => <span>{children}</span>,
    },
    message: {
      error: (content: string) => messageErrorMock(content),
    },
  }
})

vi.mock('@ant-design/icons', () => ({
  CheckCircleOutlined: () => <span>success-icon</span>,
  CloseCircleOutlined: () => <span>error-icon</span>,
  EyeOutlined: () => <span>eye-icon</span>,
  ReloadOutlined: () => <span>reload-icon</span>,
}))

vi.mock('@/services/webhooks', () => ({
  getWebhookDeliveries: (id: number, params: unknown) => getWebhookDeliveriesMock(id, params),
}))

vi.mock('./DeliveryDetailModal', () => ({
  DeliveryDetailModal: ({
    open,
    delivery,
    onClose,
  }: {
    open: boolean
    delivery: Pick<WebhookDelivery, 'id'> | null
    onClose: () => void
  }) => (
    open ? (
      <div data-testid="delivery-detail-modal">
        <span>{delivery ? `detail:${delivery.id}` : 'detail:none'}</span>
        <button type="button" onClick={onClose}>detail close</button>
      </div>
    ) : null
  ),
}))

function buildWebhook(): Webhook {
  return {
    id: 3,
    name: 'Audit Hook',
    url: 'https://audit.example.com/webhook',
    events: ['user.registered'],
    status: 1,
    max_retries: 3,
    timeout_sec: 15,
    created_by: 1,
    created_at: '2026-03-28 09:00:00',
    updated_at: '2026-03-28 09:10:00',
  }
}

function buildDeliveries(): WebhookDelivery[] {
  return [
    {
      id: 11,
      webhook_id: 3,
      event_type: 'user.registered',
      payload: '{"user":"alice"}',
      status_code: 204,
      response_body: '{"ok":true}',
      attempt: 1,
      success: true,
      error: '',
      delivered_at: '2026-03-28 09:31:00',
      created_at: '2026-03-28 09:30:00',
    },
    {
      id: 12,
      webhook_id: 3,
      event_type: 'user.deleted',
      payload: '{"user":"bob"}',
      status_code: 0,
      response_body: '',
      attempt: 2,
      success: false,
      error: 'delivery failed',
      delivered_at: '',
      created_at: '2026-03-28 09:32:00',
    },
  ]
}

describe('WebhookDeliveriesDrawer', () => {
  beforeEach(() => {
    getWebhookDeliveriesMock.mockReset()
    messageErrorMock.mockReset()
  })

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

  it('returns early when there is no webhook selected', () => {
    render(
      <WebhookDeliveriesDrawer
        open
        webhook={null}
        onClose={vi.fn()}
      />,
    )

    expect(getWebhookDeliveriesMock).not.toHaveBeenCalled()
  })

  it('loads deliveries, refreshes, opens detail view, and closes the drawer', async () => {
    const user = userEvent.setup()
    const onClose = vi.fn()

    getWebhookDeliveriesMock.mockResolvedValue(buildDeliveries())

    render(
      <WebhookDeliveriesDrawer
        open
        webhook={buildWebhook()}
        onClose={onClose}
      />,
    )

    await waitFor(() => expect(getWebhookDeliveriesMock).toHaveBeenCalledWith(3, { limit: 50 }))
    expect(screen.getByText('https://audit.example.com/webhook')).toBeInTheDocument()
    expect(screen.getByText('已启用')).toBeInTheDocument()
    expect(screen.getByText('204')).toBeInTheDocument()
    expect(screen.getByText('成功')).toBeInTheDocument()
    expect(screen.getByText('失败')).toBeInTheDocument()
    expect(screen.getByText('delivery failed')).toBeInTheDocument()

    await user.click(within(screen.getByTestId('delivery-row-12')).getByRole('button', { name: '详情' }))
    expect(screen.getByTestId('delivery-detail-modal')).toHaveTextContent('detail:12')

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

    await user.click(screen.getByRole('button', { name: '刷新' }))
    await waitFor(() => expect(getWebhookDeliveriesMock).toHaveBeenCalledTimes(2))

    await user.click(screen.getByRole('button', { name: 'drawer close' }))
    expect(onClose).toHaveBeenCalledTimes(1)
  })

  it('surfaces delivery loading failures', async () => {
    getWebhookDeliveriesMock.mockRejectedValueOnce(new Error('load deliveries failed'))

    render(
      <WebhookDeliveriesDrawer
        open
        webhook={buildWebhook()}
        onClose={vi.fn()}
      />,
    )

    await waitFor(() => expect(messageErrorMock).toHaveBeenCalledWith('load deliveries failed'))
  })
})
