/** @vitest-environment jsdom */
import { render, screen } from '@testing-library/react'
import '@testing-library/jest-dom/vitest'
import { describe, expect, it } from 'vitest'
import { BurnPanel } from './BurnPanel'
import type { AttemptRecord } from './attempt-record'

function attempt(overrides: Partial<AttemptRecord> = {}): AttemptRecord {
  return {
    attemptId: 'attempt-done',
    task: 't1',
    phase: 'coder',
    seat: 'coder',
    model: 'gpt-test',
    wrapper: '/tmp/agent.sh',
    timeoutSecs: 120,
    promptPath: '/run/attempts/attempt-done.prompt.md',
    promptSha: 'a'.repeat(64),
    causalInput: 't1:initial',
    valid: true,
    replyPath: '/run/attempts/attempt-done.reply.json',
    usage: { inputTokens: 11, outputTokens: 7, model: 'gpt-test' },
    liveness: null,
    activity: {
      toolCalls: 6,
      filesTouched: ['src/app.js'],
      repeatedCommands: [['bash', 'npm test']],
      failureFingerprint: 'coder:coder:gpt-test',
    },
    ...overrides,
  }
}

describe('BurnPanel', () => {
  it('alarms when the same failureFingerprint appears on two attempts', () => {
    const attempts = [
      attempt({
        attemptId: 'attempt-a',
        activity: { failureFingerprint: 'gate:timeout:model-x' },
      }),
      attempt({
        attemptId: 'attempt-b',
        activity: { failureFingerprint: 'gate:timeout:model-x' },
      }),
    ]

    render(<BurnPanel attempts={attempts} budget={8} />)

    expect(screen.getByTestId('burn-panel')).toHaveAttribute('data-repeat-failure', 'true')
    expect(screen.getByTestId('burn-panel')).toHaveAttribute('data-budget-proximity', 'false')
    expect(screen.getByTestId('burn-panel-repeat-failure')).toBeInTheDocument()
    expect(screen.getByTestId('burn-panel-fingerprint-gate:timeout:model-x')).toHaveTextContent('2 attempts')
    expect(screen.queryByTestId('burn-panel-budget-proximity')).not.toBeInTheDocument()
  })

  it('alarms independently when attempt usage reaches at least 75% of budget', () => {
    const attempts = [
      attempt({ attemptId: 'attempt-a', activity: { failureFingerprint: 'fp-a' } }),
      attempt({ attemptId: 'attempt-b', activity: { failureFingerprint: 'fp-b' } }),
      attempt({ attemptId: 'attempt-c', activity: { failureFingerprint: 'fp-c' } }),
    ]

    render(<BurnPanel attempts={attempts} budget={4} />)

    expect(screen.getByTestId('burn-panel')).toHaveAttribute('data-repeat-failure', 'false')
    expect(screen.getByTestId('burn-panel')).toHaveAttribute('data-budget-proximity', 'true')
    expect(screen.queryByTestId('burn-panel-repeat-failure')).not.toBeInTheDocument()
    expect(screen.getByTestId('burn-panel-budget-proximity')).toHaveTextContent('3/4')
  })

  it('names both alarm conditions when repeat failure and budget proximity fire together', () => {
    const attempts = [
      attempt({
        attemptId: 'attempt-a',
        activity: { failureFingerprint: 'gate:compile:model-x' },
      }),
      attempt({
        attemptId: 'attempt-b',
        activity: { failureFingerprint: 'gate:compile:model-x' },
      }),
    ]

    render(<BurnPanel attempts={attempts} budget={2} />)

    expect(screen.getByTestId('burn-panel')).toHaveAttribute('data-repeat-failure', 'true')
    expect(screen.getByTestId('burn-panel')).toHaveAttribute('data-budget-proximity', 'true')
    expect(screen.getByTestId('burn-panel-repeat-failure')).toBeInTheDocument()
    expect(screen.getByTestId('burn-panel-budget-proximity')).toBeInTheDocument()
  })

  it('renders task dispatch and usage totals when no alarm fires', () => {
    const attempts = [
      attempt({
        attemptId: 'attempt-a',
        task: 't1',
        seat: 'coder',
        model: 'gpt-a',
        usage: { inputTokens: 10, outputTokens: 3, model: 'gpt-a' },
        activity: { failureFingerprint: 'fp-a' },
      }),
      attempt({
        attemptId: 'attempt-b',
        task: 't1',
        seat: 'reviewer',
        model: 'gpt-b',
        usage: { unknown: true, model: 'gpt-b' },
        activity: { failureFingerprint: 'fp-b' },
      }),
    ]

    render(<BurnPanel attempts={attempts} budget={8} />)

    expect(screen.getByTestId('burn-panel')).toHaveAttribute('data-repeat-failure', 'false')
    expect(screen.getByTestId('burn-panel')).toHaveAttribute('data-budget-proximity', 'false')
    expect(screen.getByTestId('burn-panel-task-t1')).toHaveTextContent('2/8')
    expect(screen.getByTestId('burn-panel-seat-coder')).toHaveTextContent('10 in · 3 out')
    expect(screen.getByTestId('burn-panel-seat-reviewer')).toHaveTextContent('1 unmetered')
    expect(screen.getByTestId('burn-panel-model-gpt-a')).toHaveTextContent('10 in · 3 out')
    expect(screen.getByTestId('burn-panel-model-gpt-b')).toHaveTextContent('1 unmetered')
  })

  it('does not alarm on plan-wide attempt count when every task stays under its own budget', () => {
    const attempts = [
      attempt({ attemptId: 'attempt-a', task: 't1', activity: { failureFingerprint: 'fp-a' } }),
      attempt({ attemptId: 'attempt-b', task: 't2', activity: { failureFingerprint: 'fp-b' } }),
      attempt({ attemptId: 'attempt-c', task: 't3', activity: { failureFingerprint: 'fp-c' } }),
      attempt({ attemptId: 'attempt-d', task: 't4', activity: { failureFingerprint: 'fp-d' } }),
    ]

    render(<BurnPanel attempts={attempts} budget={4} />)

    expect(screen.getByTestId('burn-panel')).toHaveAttribute('data-budget-proximity', 'false')
    expect(screen.queryByTestId('burn-panel-budget-proximity')).not.toBeInTheDocument()
  })

  it('alarms when any single task reaches 75% of its own budget', () => {
    const attempts = [
      attempt({
        attemptId: 'attempt-a',
        task: 't1',
        taskDispatchCount: 3,
        taskDispatchBudget: 4,
        activity: { failureFingerprint: 'fp-a' },
      }),
      attempt({
        attemptId: 'attempt-b',
        task: 't2',
        taskDispatchCount: 1,
        taskDispatchBudget: 10,
        activity: { failureFingerprint: 'fp-b' },
      }),
    ]

    render(<BurnPanel attempts={attempts} budget={99} />)

    expect(screen.getByTestId('burn-panel')).toHaveAttribute('data-budget-proximity', 'true')
    expect(screen.getByTestId('burn-panel-budget-proximity')).toHaveTextContent('3/4')
  })

  it('accumulates costUsd and cachedTokens in seat and model rollups with honest gaps', () => {
    const attempts = [
      attempt({
        attemptId: 'attempt-a',
        seat: 'coder',
        usage: { inputTokens: 10, outputTokens: 3, model: 'gpt-a', costUsd: 0.01, cachedTokens: 100 },
        activity: { failureFingerprint: 'fp-a' },
      }),
      attempt({
        attemptId: 'attempt-b',
        seat: 'coder',
        usage: { inputTokens: 5, outputTokens: 2, model: 'gpt-a' },
        activity: { failureFingerprint: 'fp-b' },
      }),
    ]

    render(<BurnPanel attempts={attempts} budget={8} />)

    expect(screen.getByTestId('burn-panel-seat-coder')).toHaveTextContent('15 in · 5 out')
    expect(screen.getByTestId('burn-panel-seat-coder')).toHaveTextContent('$0.0100')
    expect(screen.getByTestId('burn-panel-seat-coder')).toHaveTextContent('1 cost not captured')
    expect(screen.getByTestId('burn-panel-seat-coder')).toHaveTextContent('100 cached')
    expect(screen.getByTestId('burn-panel-seat-coder')).toHaveTextContent('1 cached not captured')
    expect(screen.getByTestId('burn-panel-model-gpt-a')).toHaveTextContent('$0.0100')
    expect(screen.getByTestId('burn-panel-model-gpt-a')).toHaveTextContent('100 cached')
  })

  it('renders captured zero cost and cached-token values', () => {
    render(
      <BurnPanel
        attempts={[
          attempt({
            usage: { inputTokens: 10, outputTokens: 3, model: 'gpt-a', costUsd: 0, cachedTokens: 0 },
            activity: { failureFingerprint: 'fp-a' },
          }),
        ]}
        budget={8}
      />,
    )

    const usage = screen.getByTestId('burn-panel-seat-coder')
    expect(usage).toHaveTextContent('$0.0000')
    expect(usage).toHaveTextContent('0 cached')
    expect(usage).not.toHaveTextContent('not captured')
  })

  it('labels absent cost and cached-token values as not captured', () => {
    render(
      <BurnPanel
        attempts={[
          attempt({
            usage: { inputTokens: 10, outputTokens: 3, model: 'gpt-a' },
            activity: { failureFingerprint: 'fp-a' },
          }),
        ]}
        budget={8}
      />,
    )

    const usage = screen.getByTestId('burn-panel-seat-coder')
    expect(usage).toHaveTextContent('1 cost not captured')
    expect(usage).toHaveTextContent('1 cached not captured')
  })

  it('uses each task own taskDispatchBudget and taskDispatchCount', () => {
    const attempts = [
      attempt({
        attemptId: 'attempt-a',
        task: 't1',
        taskDispatchCount: 2,
        taskDispatchBudget: 4,
        activity: { failureFingerprint: 'fp-a' },
      }),
      attempt({
        attemptId: 'attempt-b',
        task: 't1',
        taskDispatchCount: 2,
        taskDispatchBudget: 4,
        activity: { failureFingerprint: 'fp-b' },
      }),
      attempt({
        attemptId: 'attempt-c',
        task: 't2',
        taskDispatchCount: 1,
        taskDispatchBudget: 10,
        activity: { failureFingerprint: 'fp-c' },
      }),
    ]

    render(<BurnPanel attempts={attempts} budget={99} />)

    expect(screen.getByTestId('burn-panel-task-t1')).toHaveTextContent('2/4')
    expect(screen.getByTestId('burn-panel-task-t2')).toHaveTextContent('1/10')
  })

  it('renders nothing for a single healthy attempt', () => {
    render(
      <BurnPanel
        attempts={[
          attempt({
            attemptId: 'attempt-healthy',
            verdict: 'PASS',
            valid: true,
            activity: { toolCalls: 1 },
          }),
        ]}
        budget={2}
      />,
    )

    expect(screen.getByTestId('burn-panel-task-t1')).toHaveTextContent('1/2')
  })
})
