import { describe, it, expect, beforeEach, afterEach } from 'vitest'
import { render, screen, waitFor } from '@testing-library/react'
import { http, HttpResponse } from 'msw'
import { setupServer } from 'msw/node'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'

import ScheduledTasks from '@/pages/ScheduledTasks'

// ── Mock data ──────────────────────────────────────────────────

const mockTasks = [
  {
    id: 'task-001',
    name: '每日早报',
    schedule: '0 8 * * *',
    schedule_type: 'cron',
    target: { type: 'agent', id: 'daily-news' },
    enabled: true,
    description: '每天早上8点推送新闻',
    last_run: '2026-04-07T08:00:00Z',
    next_run: '2026-04-08T08:00:00Z',
    run_count: 30,
    last_result: null,
    last_error: null,
    last_duration_ms: 1500,
    created_at: '2026-03-01T00:00:00Z',
  },
  {
    id: 'task-002',
    name: '定时采集',
    schedule: '30m',
    schedule_type: 'interval',
    target: { type: 'hand', id: 'collector' },
    enabled: false,
    description: null,
    last_run: null,
    next_run: null,
    run_count: 0,
    last_result: null,
    last_error: null,
    last_duration_ms: null,
    created_at: '2026-04-01T00:00:00Z',
  },
]

const server = setupServer()

beforeEach(() => {
  server.listen({ onUnhandledRequest: 'bypass' })
})

afterEach(() => {
  server.close()
})

function renderWithProviders(ui: React.ReactElement) {
  const queryClient = new QueryClient({
    defaultOptions: { queries: { retry: false } },
  })
  return render(
    <QueryClientProvider client={queryClient}>
      {ui}
    </QueryClientProvider>,
  )
}

describe('ScheduledTasks', () => {
  it('renders page header', () => {
    server.use(
      http.get('*/api/v1/scheduler/tasks', () => HttpResponse.json([])),
    )
    renderWithProviders(<ScheduledTasks />)
    expect(screen.getByText('定时任务')).toBeInTheDocument()
  })

  it('shows loading spinner', async () => {
    server.use(
      http.get('*/api/v1/scheduler/tasks', async () => {
        await new Promise(resolve => setTimeout(resolve, 500))
        return HttpResponse.json(mockTasks)
      }),
    )
    renderWithProviders(<ScheduledTasks />)
    expect(document.querySelector('.ant-spin')).toBeTruthy()
  })

  it('displays tasks in table', async () => {
    server.use(
      http.get('*/api/v1/scheduler/tasks', () => HttpResponse.json(mockTasks)),
    )
    renderWithProviders(<ScheduledTasks />)

    await waitFor(() => {
      expect(screen.getByText('每日早报')).toBeInTheDocument()
    })
    expect(screen.getByText('定时采集')).toBeInTheDocument()
    expect(screen.getByText('0 8 * * *')).toBeInTheDocument()
    expect(screen.getByText('30m')).toBeInTheDocument()
  })

  it('displays Chinese schedule type labels', async () => {
    server.use(
      http.get('*/api/v1/scheduler/tasks', () => HttpResponse.json(mockTasks)),
    )
    renderWithProviders(<ScheduledTasks />)

    await waitFor(() => {
      // schedule_type labels: cron → "Cron", interval → "间隔"
      expect(screen.getByText('Cron')).toBeInTheDocument()
    })
    expect(screen.getByText('间隔')).toBeInTheDocument()
  })

  it('displays target type with English labels', async () => {
    server.use(
      http.get('*/api/v1/scheduler/tasks', () => HttpResponse.json(mockTasks)),
    )
    renderWithProviders(<ScheduledTasks />)

    await waitFor(() => {
      // target type labels: agent → "Agent", hand → "Hand"
      expect(screen.getByText('Agent')).toBeInTheDocument()
    })
    expect(screen.getByText('Hand')).toBeInTheDocument()
  })

  it('displays target IDs', async () => {
    server.use(
      http.get('*/api/v1/scheduler/tasks', () => HttpResponse.json(mockTasks)),
    )
    renderWithProviders(<ScheduledTasks />)

    await waitFor(() => {
      expect(screen.getByText('daily-news')).toBeInTheDocument()
    })
    expect(screen.getByText('collector')).toBeInTheDocument()
  })

  it('displays enabled switches', async () => {
    server.use(
      http.get('*/api/v1/scheduler/tasks', () => HttpResponse.json(mockTasks)),
    )
    renderWithProviders(<ScheduledTasks />)

    await waitFor(() => {
      const switches = document.querySelectorAll('.ant-switch')
      expect(switches.length).toBeGreaterThanOrEqual(2)
    })
  })

  it('displays run count', async () => {
    server.use(
      http.get('*/api/v1/scheduler/tasks', () => HttpResponse.json(mockTasks)),
    )
    renderWithProviders(<ScheduledTasks />)

    await waitFor(() => {
      expect(screen.getByText('每日早报')).toBeInTheDocument()
    })
    // run_count: 30 is displayed in tabular-nums span
    expect(screen.getByText('30')).toBeInTheDocument()
  })

  it('shows last error in red when present', async () => {
    const tasksWithError = [{ ...mockTasks[0], last_error: 'Connection timeout' }]
    server.use(
      http.get('*/api/v1/scheduler/tasks', () => HttpResponse.json(tasksWithError)),
    )
    renderWithProviders(<ScheduledTasks />)

    await waitFor(() => {
      const errorEl = screen.getByText('Connection timeout')
      expect(errorEl).toHaveClass('text-red-500')
    })
  })

  it('shows dash for null last error', async () => {
    server.use(
      http.get('*/api/v1/scheduler/tasks', () => HttpResponse.json(mockTasks)),
    )
    renderWithProviders(<ScheduledTasks />)

    await waitFor(() => {
      expect(screen.getByText('每日早报')).toBeInTheDocument()
    })
  })

  it('shows empty state when no tasks', async () => {
    server.use(
      http.get('*/api/v1/scheduler/tasks', () => HttpResponse.json([])),
    )
    renderWithProviders(<ScheduledTasks />)

    await waitFor(() => {
      const empties = screen.getAllByText('暂无数据')
      expect(empties.length).toBeGreaterThanOrEqual(1)
    })
  })

  it('shows error state on API failure', async () => {
    server.use(
      http.get('*/api/v1/scheduler/tasks', () => {
        return HttpResponse.json(
          { error: 'internal_error', message: '数据库错误' },
          { status: 500 },
        )
      }),
    )
    renderWithProviders(<ScheduledTasks />)

    await waitFor(() => {
      expect(screen.getByText('加载失败')).toBeInTheDocument()
    })
  })

  it('has 新建任务 button', async () => {
    server.use(
      http.get('*/api/v1/scheduler/tasks', () => HttpResponse.json([])),
    )
    renderWithProviders(<ScheduledTasks />)

    await waitFor(() => {
      expect(screen.getByText('新建任务')).toBeInTheDocument()
    })
  })

  it('renders action column with buttons', async () => {
    server.use(
      http.get('*/api/v1/scheduler/tasks', () => HttpResponse.json(mockTasks)),
    )
    renderWithProviders(<ScheduledTasks />)

    await waitFor(() => {
      expect(screen.getByText('每日早报')).toBeInTheDocument()
    })

    // 操作 column header should be rendered
    expect(screen.getByText('操作')).toBeInTheDocument()
  })

  it('color-codes schedule type tags', async () => {
    server.use(
      http.get('*/api/v1/scheduler/tasks', () => HttpResponse.json(mockTasks)),
    )
    renderWithProviders(<ScheduledTasks />)

    await waitFor(() => {
      const cronTag = screen.getByText('Cron').closest('.ant-tag')
      expect(cronTag?.className).toMatch(/blue/)
    })
    const intervalTag = screen.getByText('间隔').closest('.ant-tag')
    expect(intervalTag?.className).toMatch(/green/)
  })

  it('color-codes target type tags', async () => {
    server.use(
      http.get('*/api/v1/scheduler/tasks', () => HttpResponse.json(mockTasks)),
    )
    renderWithProviders(<ScheduledTasks />)

    await waitFor(() => {
      const agentTag = screen.getByText('Agent').closest('.ant-tag')
      expect(agentTag?.className).toMatch(/purple/)
    })
    const handTag = screen.getByText('Hand').closest('.ant-tag')
    expect(handTag?.className).toMatch(/cyan/)
  })
})
