import type { ReactNode } from 'react'
import { render, screen } from '@testing-library/react'
import { describe, expect, it, vi } from 'vitest'

import { OperationLogDetailDrawer } from './OperationLogDetailDrawer'
import type { OperationLog } from '@/types/operation-log'

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

  return {
    Drawer: ({
      children,
      title,
      open,
      onClose,
    }: {
      children?: ReactNode
      title?: string
      open?: boolean
      onClose?: () => void
    }) => (
      <div data-testid="drawer" data-open={open}>
        <div data-testid="drawer-title">{title}</div>
        <button onClick={onClose}>close</button>
        {children}
      </div>
    ),
    Descriptions: Object.assign(Descriptions, {
      Item: ({
        label,
        children,
      }: {
        label?: ReactNode
        children?: ReactNode
      }) => (
        <div>
          <span>{label}</span>
          <span>{children}</span>
        </div>
      ),
    }),
    Tag: ({ children, color }: { children?: ReactNode; color?: string }) => (
      <span data-testid="tag" data-color={color}>
        {children}
      </span>
    ),
    Typography: {
      Paragraph: ({ children }: { children?: ReactNode }) => <div>{children}</div>,
      Text: ({ children }: { children?: ReactNode }) => <span>{children}</span>,
    },
  }
})

vi.mock('dayjs', () => ({
  default: () => ({
    format: () => '2024-01-15 10:30:00',
  }),
}))

describe('OperationLogDetailDrawer', () => {
  it('renders nothing when log is null', () => {
    render(<OperationLogDetailDrawer open={true} log={null} onClose={vi.fn()} />)

    expect(screen.queryByTestId('drawer')).not.toBeInTheDocument()
  })

  it('renders drawer when log is provided and open is true', () => {
    const mockLog: OperationLog = {
      id: 1,
      user_id: 10,
      operation_type: 'user',
      operation_name: 'update_user',
      request_method: 'PUT',
      request_path: '/api/users/1',
      request_params: '{}',
      response_status: 200,
      ip: '192.168.1.1',
      user_agent: 'Mozilla/5.0',
      created_at: '2024-01-15T10:30:00Z',
    }

    render(<OperationLogDetailDrawer open={true} log={mockLog} onClose={vi.fn()} />)

    expect(screen.getByTestId('drawer')).toHaveAttribute('data-open', 'true')
    expect(screen.getByTestId('drawer-title')).toHaveTextContent('操作日志详情')
  })

  it('renders log details correctly', () => {
    const mockLog: OperationLog = {
      id: 42,
      user_id: 15,
      operation_type: 'role',
      operation_name: 'create_role',
      request_method: 'POST',
      request_path: '/api/roles',
      request_params: '{"name":"admin"}',
      response_status: 201,
      ip: '10.0.0.1',
      user_agent: 'Chrome/120.0',
      created_at: '2024-01-15T10:30:00Z',
    }

    render(<OperationLogDetailDrawer open={true} log={mockLog} onClose={vi.fn()} />)

    expect(screen.getByText('42')).toBeInTheDocument()
    expect(screen.getByText('15')).toBeInTheDocument()
    expect(screen.getByText('role')).toBeInTheDocument()
    expect(screen.getByText('create_role')).toBeInTheDocument()
    expect(screen.getByText('POST')).toBeInTheDocument()
    expect(screen.getByText('201')).toBeInTheDocument()
  })

  it('shows success tag for 2xx response status', () => {
    const mockLog: OperationLog = {
      id: 1,
      user_id: 10,
      request_method: 'GET',
      request_path: '/api/test',
      response_status: 200,
      ip: '192.168.1.1',
      created_at: '2024-01-15T10:30:00Z',
    }

    render(<OperationLogDetailDrawer open={true} log={mockLog} onClose={vi.fn()} />)

    const tags = screen.getAllByTestId('tag')
    const statusTag = tags.find(tag => tag.getAttribute('data-color') === 'success')
    expect(statusTag).toBeDefined()
  })

  it('shows error tag for non-2xx response status', () => {
    const mockLog: OperationLog = {
      id: 1,
      user_id: 10,
      request_method: 'POST',
      request_path: '/api/test',
      response_status: 500,
      ip: '192.168.1.1',
      created_at: '2024-01-15T10:30:00Z',
    }

    render(<OperationLogDetailDrawer open={true} log={mockLog} onClose={vi.fn()} />)

    const tags = screen.getAllByTestId('tag')
    const statusTag = tags.find(tag => tag.getAttribute('data-color') === 'error')
    expect(statusTag).toBeDefined()
  })

  it('strips HTML tags from request_params to prevent XSS', () => {
    const mockLog: OperationLog = {
      id: 1,
      user_id: 10,
      request_method: 'POST',
      request_path: '/api/test',
      request_params: '<script>alert("xss")</script>',
      response_status: 200,
      ip: '192.168.1.1',
      created_at: '2024-01-15T10:30:00Z',
    }

    render(<OperationLogDetailDrawer open={true} log={mockLog} onClose={vi.fn()} />)

    // HTML tags are stripped to prevent XSS, so <script> should not be present
    expect(screen.queryByText('<script>')).not.toBeInTheDocument()
    // But the content inside tags becomes plain text after stripping
    expect(screen.getByText('alert("xss")')).toBeInTheDocument()
  })

  it('handles null user_id gracefully', () => {
    const mockLog: OperationLog = {
      id: 1,
      user_id: null,
      request_method: 'GET',
      request_path: '/api/test',
      response_status: 200,
      ip: '192.168.1.1',
      created_at: '2024-01-15T10:30:00Z',
    }

    render(<OperationLogDetailDrawer open={true} log={mockLog} onClose={vi.fn()} />)

    expect(screen.getByTestId('drawer')).toHaveAttribute('data-open', 'true')
  })
})
