import { expect, test } from '@playwright/test'
import type { Item } from '../src/lib/collector-types'
import { startFixtureCollector, type FixtureCollector } from './fixture-collector'
import { ADAPTER_STATUSES, ALL_PANELS } from './fixtures/collector-fixtures'

const NOW = Date.parse('2026-07-17T12:00:00.000Z')
const iso = (ms: number) => new Date(ms).toISOString()

const DECISION_ITEMS: Item[] = [
  {
    id: 'decision-drizzle-pin',
    source: 'harness',
    project: 'alexcodeplace/multideal',
    severity: 'act',
    kind: 'decision',
    title: 'Pin drizzle-orm at 0.44 or bump to 0.45?',
    detail: 'factory dep-provisioning · phase 3/4 · task drizzle-pin-decision · engine blocked on answer',
    ts: iso(NOW - 42 * 60_000),
    actions: [
      {
        verb: 'harness.decision.answer',
        args: { runId: 'dep-provisioning', decisionId: 'drizzle-pin', choice: 'pin-044' },
        label: 'Pin 0.44 until M2 lands',
        recommended: true,
      },
      {
        verb: 'harness.decision.answer',
        args: { runId: 'dep-provisioning', decisionId: 'drizzle-pin', choice: 'bump-045' },
        label: 'Bump to 0.45 now',
      },
    ],
    decision: {
      question:
        'Lockfile has drizzle-orm 0.44.2. 0.45.0 released 3 days ago and changes the pgTable generic signature — 14 call sites in packages/db would need the new form.',
      options: [
        { label: 'Pin 0.44 until M2 lands', recommended: true },
        { label: 'Bump to 0.45 now' },
      ],
      freeText: true,
      context: JSON.stringify({
        kvs: [
          { label: 'plan', value: 'factory-dep-provisioning' },
          { label: 'asked by', value: 'wave-3 implementer' },
          { label: 'impact if wrong', value: 'redo M2 plan (~2h agent)' },
        ],
        data: 'affected: packages/db/src/schema/*.ts (14 sites) · migration journal: 131 sql / 77 entries (broken, M2 pending)',
        journal: 'runstate/factory-dep-provisioning.log.jsonl:412',
      }),
      waitingSince: iso(NOW - 42 * 60_000),
    },
  },
  {
    id: 'decision-stripe-gateway',
    source: 'harness',
    project: 'zync.is',
    severity: 'act',
    kind: 'decision',
    title: 'Payment gateway: Stripe or PayPlus? (P0.3 — blocks Z3)',
    detail: 'factory preflight · one gets built, the other two adapter stubs get deleted',
    ts: iso(NOW - 2 * 86_400_000),
    actions: [
      {
        verb: 'harness.decision.answer',
        args: { runId: 'zync-preflight', decisionId: 'gateway', choice: 'stripe' },
        label: 'Stripe (port from multideal)',
        recommended: true,
      },
      {
        verb: 'harness.decision.answer',
        args: { runId: 'zync-preflight', decisionId: 'gateway', choice: 'payplus' },
        label: 'PayPlus',
      },
    ],
    decision: {
      question: 'Z3 needs a payment gateway before checkout work lands. Stripe is proven on multideal; PayPlus is local-market but greenfield.',
      options: [
        { label: 'Stripe (port from multideal)', recommended: true },
        { label: 'PayPlus' },
      ],
      freeText: true,
      context: JSON.stringify({
        kvs: [{ label: 'plan', value: 'plan-preflight' }],
      }),
      waitingSince: iso(NOW - 2 * 86_400_000),
    },
  },
  {
    id: 'halt-dep-provisioning',
    source: 'harness',
    project: 'dep-provisioning',
    severity: 'act',
    kind: 'decision',
    title: 'dep-provisioning halted',
    detail: 'pin drizzle 0.44 or bump?',
    ts: iso(NOW - 18 * 60_000),
    actions: [],
  },
]

test.describe.configure({ mode: 'serial' })

let collector: FixtureCollector

test.beforeAll(async () => {
  collector = await startFixtureCollector({ panels: ALL_PANELS, adapters: ADAPTER_STATUSES, items: DECISION_ITEMS })
})

test.afterAll(async () => {
  await collector.close()
})

test('expands and collapses a decision row in place', async ({ page }) => {
  await page.goto('/decisions')

  const row = page.locator('[data-decision-row-id="decision-drizzle-pin"]')
  await expect(row).toHaveAttribute('data-decision-expanded', 'false')
  await expect(row.getByText('Full question from agent')).toHaveCount(0)

  await row.getByText('▼ expand').click()
  await expect(row).toHaveAttribute('data-decision-expanded', 'true')
  await expect(row.getByText('Full question from agent')).toBeVisible()
  await expect(row.getByText('runstate/factory-dep-provisioning.log.jsonl:412')).toBeVisible()

  await row.getByText('▲ collapse').click()
  await expect(row).toHaveAttribute('data-decision-expanded', 'false')
  await expect(row.getByText('Full question from agent')).toHaveCount(0)
})

test('option answer posts the exact payload to the collector mock', async ({ page }) => {
  const before = collector.answerCalls.length
  await page.goto('/decisions')

  const row = page.locator('[data-decision-row-id="decision-drizzle-pin"]')
  await row.getByRole('button', { name: /Pin 0.44 until M2 lands/ }).click()

  await expect.poll(() => collector.answerCalls.length).toBe(before + 1)
  expect(collector.answerCalls.at(-1)).toEqual({
    id: 'decision-drizzle-pin',
    body: { choice: 'pin-044' },
  })
  await expect(row).toHaveCount(0)
})

test('free-text answer posts the typed choice payload', async ({ page }) => {
  const before = collector.answerCalls.length
  await page.goto('/decisions')

  const row = page.locator('[data-decision-row-id="decision-stripe-gateway"]')
  await row.locator('[data-decision-free-text]').fill('Use Stripe sandbox keys from multideal')
  await row.getByRole('button', { name: 'Send' }).click()

  await expect.poll(() => collector.answerCalls.length).toBe(before + 1)
  expect(collector.answerCalls.at(-1)).toEqual({
    id: 'decision-stripe-gateway',
    body: { choice: 'Use Stripe sandbox keys from multideal' },
  })
})

test('failure path shows an error and preserves the typed answer', async ({ page }) => {
  await page.goto('/decisions')

  const row = page.locator('[data-decision-row-id="halt-dep-provisioning"]')
  const text = 'Pin 0.44.2 and regenerate the M2 plan after go-live'
  collector.failAnswerFor('halt-dep-provisioning')

  await row.locator('[data-decision-free-text]').fill(text)
  await row.getByRole('button', { name: 'Send' }).click()

  await expect(row.locator('[data-decision-error]')).toHaveText('Answer failed — try again')
  await expect(row.locator('[data-decision-free-text]')).toHaveValue(text)
  await expect(row).toHaveAttribute('data-decision-answer-state', 'error')
})
