import { test, expect } from '@playwright/test'
import { API_KEY_HEADER, createTestTask, deleteTestTask } from './helpers'

test.describe('Tasks CRUD', () => {
  const cleanup: number[] = []

  test.afterEach(async ({ request }) => {
    for (const id of cleanup) {
      await deleteTestTask(request, id).catch(() => {})
    }
    cleanup.length = 0
  })

  // ── POST /api/tasks ──────────────────────────

  test('POST creates task with minimal fields (title only)', async ({ request }) => {
    const { id, res, body } = await createTestTask(request)
    cleanup.push(id)

    expect(res.status()).toBe(201)
    expect(body.task).toBeDefined()
    expect(body.task.title).toContain('e2e-task-')
    expect(body.task.status).toBe('inbox')
    expect(body.task.priority).toBe('medium')
  })

  test('POST creates task with all fields', async ({ request }) => {
    const { id, res, body } = await createTestTask(request, {
      description: 'Full task',
      status: 'assigned',
      priority: 'high',
      assigned_to: 'agent-x',
      tags: ['e2e', 'test'],
      metadata: { source: 'e2e' },
    })
    cleanup.push(id)

    expect(res.status()).toBe(201)
    expect(body.task.description).toBe('Full task')
    expect(body.task.status).toBe('assigned')
    expect(body.task.priority).toBe('high')
    expect(body.task.assigned_to).toBe('agent-x')
    expect(body.task.tags).toEqual(['e2e', 'test'])
    expect(body.task.metadata).toEqual({ source: 'e2e' })
  })

  test('POST ignores client-supplied created_by and uses authenticated actor', async ({ request }) => {
    const title = `e2e-task-actor-${Date.now()}`
    const res = await request.post('/api/tasks', {
      headers: API_KEY_HEADER,
      data: {
        title,
        created_by: 'spoofed-agent',
      },
    })
    expect(res.status()).toBe(201)
    const body = await res.json()
    const id = Number(body.task.id)
    cleanup.push(id)
    expect(body.task.created_by).not.toBe('spoofed-agent')
    expect(body.task.created_by).toBe('API Access')
  })

  test('POST rejects empty title', async ({ request }) => {
    const res = await request.post('/api/tasks', {
      headers: API_KEY_HEADER,
      data: { title: '' },
    })
    expect(res.status()).toBe(400)
  })

  test('POST rejects duplicate title', async ({ request }) => {
    const { id, body: first } = await createTestTask(request)
    cleanup.push(id)

    const res = await request.post('/api/tasks', {
      headers: API_KEY_HEADER,
      data: { title: first.task.title },
    })
    expect(res.status()).toBe(409)
  })

  // ── GET /api/tasks ───────────────────────────

  test('GET list returns tasks with pagination shape', async ({ request }) => {
    const { id } = await createTestTask(request)
    cleanup.push(id)

    const res = await request.get('/api/tasks', { headers: API_KEY_HEADER })
    expect(res.status()).toBe(200)
    const body = await res.json()
    expect(body).toHaveProperty('tasks')
    expect(body).toHaveProperty('total')
    expect(body).toHaveProperty('page')
    expect(body).toHaveProperty('limit')
    expect(Array.isArray(body.tasks)).toBe(true)
  })

  test('GET list filters by status', async ({ request }) => {
    const { id } = await createTestTask(request, { status: 'review' })
    cleanup.push(id)

    const res = await request.get('/api/tasks?status=review', { headers: API_KEY_HEADER })
    const body = await res.json()
    for (const t of body.tasks) {
      expect(t.status).toBe('review')
    }
  })

  test('GET list filters by priority', async ({ request }) => {
    const { id } = await createTestTask(request, { priority: 'critical' })
    cleanup.push(id)

    const res = await request.get('/api/tasks?priority=critical', { headers: API_KEY_HEADER })
    const body = await res.json()
    for (const t of body.tasks) {
      expect(t.priority).toBe('critical')
    }
  })

  test('GET list respects limit and offset', async ({ request }) => {
    const res = await request.get('/api/tasks?limit=2&offset=0', { headers: API_KEY_HEADER })
    const body = await res.json()
    expect(body.tasks.length).toBeLessThanOrEqual(2)
    expect(body.limit).toBe(2)
  })

  // ── GET /api/tasks/[id] ──────────────────────

  test('GET single returns task by id', async ({ request }) => {
    const { id } = await createTestTask(request)
    cleanup.push(id)

    const res = await request.get(`/api/tasks/${id}`, { headers: API_KEY_HEADER })
    expect(res.status()).toBe(200)
    const body = await res.json()
    expect(body.task).toBeDefined()
    expect(body.task.id).toBe(id)
  })

  test('GET single returns 404 for missing task', async ({ request }) => {
    const res = await request.get('/api/tasks/999999', { headers: API_KEY_HEADER })
    expect(res.status()).toBe(404)
  })

  test('GET single returns 400 for non-numeric id', async ({ request }) => {
    const res = await request.get('/api/tasks/abc', { headers: API_KEY_HEADER })
    expect(res.status()).toBe(400)
  })

  // ── PUT /api/tasks/[id] ──────────────────────

  test('PUT updates task fields', async ({ request }) => {
    const { id } = await createTestTask(request)
    cleanup.push(id)

    const res = await request.put(`/api/tasks/${id}`, {
      headers: API_KEY_HEADER,
      data: { title: 'Updated title', priority: 'high' },
    })
    expect(res.status()).toBe(200)
    const body = await res.json()
    expect(body.task.title).toBe('Updated title')
    expect(body.task.priority).toBe('high')
  })

  test('PUT returns 404 for missing task', async ({ request }) => {
    const res = await request.put('/api/tasks/999999', {
      headers: API_KEY_HEADER,
      data: { title: 'no-op' },
    })
    expect(res.status()).toBe(404)
  })

  test('PUT with empty body still succeeds (Zod defaults fill fields)', async ({ request }) => {
    const { id } = await createTestTask(request)
    cleanup.push(id)

    const res = await request.put(`/api/tasks/${id}`, {
      headers: API_KEY_HEADER,
      data: {},
    })
    // Zod's partial schema fills defaults (status, priority, tags, metadata),
    // so there are always fields to update — API returns 200, not 400
    expect(res.status()).toBe(200)
  })

  test('PUT returns 403 when moving to done without Aegis approval', async ({ request }) => {
    const { id } = await createTestTask(request)
    cleanup.push(id)

    const res = await request.put(`/api/tasks/${id}`, {
      headers: API_KEY_HEADER,
      data: { status: 'done' },
    })
    expect(res.status()).toBe(403)
    const body = await res.json()
    expect(body.error).toContain('Aegis')
  })

  // ── DELETE /api/tasks/[id] ───────────────────

  test('DELETE removes task', async ({ request }) => {
    const { id } = await createTestTask(request)

    const res = await request.delete(`/api/tasks/${id}`, { headers: API_KEY_HEADER })
    expect(res.status()).toBe(200)
    const body = await res.json()
    expect(body.success).toBe(true)
  })

  test('DELETE returns 404 for missing task', async ({ request }) => {
    const res = await request.delete('/api/tasks/999999', { headers: API_KEY_HEADER })
    expect(res.status()).toBe(404)
  })

  // ── Full lifecycle ───────────────────────────

  test('full lifecycle: create → read → update → delete → confirm gone', async ({ request }) => {
    // Create
    const { id, res: createRes } = await createTestTask(request, { description: 'lifecycle test' })
    expect(createRes.status()).toBe(201)

    // Read
    const readRes = await request.get(`/api/tasks/${id}`, { headers: API_KEY_HEADER })
    expect(readRes.status()).toBe(200)
    const readBody = await readRes.json()
    expect(readBody.task.description).toBe('lifecycle test')

    // Update
    const updateRes = await request.put(`/api/tasks/${id}`, {
      headers: API_KEY_HEADER,
      data: { status: 'in_progress', priority: 'critical' },
    })
    expect(updateRes.status()).toBe(200)
    const updateBody = await updateRes.json()
    expect(updateBody.task.status).toBe('in_progress')
    expect(updateBody.task.priority).toBe('critical')

    // Delete
    const deleteRes = await request.delete(`/api/tasks/${id}`, { headers: API_KEY_HEADER })
    expect(deleteRes.status()).toBe(200)

    // Confirm gone
    const goneRes = await request.get(`/api/tasks/${id}`, { headers: API_KEY_HEADER })
    expect(goneRes.status()).toBe(404)
  })
})
