import { isUsageUnknown, type AttemptRecord } from './attempt-record'

function failureFingerprintCounts(attempts: AttemptRecord[]) {
  const counts = new Map<string, number>()
  for (const attempt of attempts) {
    const fingerprint = attempt.activity?.failureFingerprint
    if (!fingerprint) continue
    counts.set(fingerprint, (counts.get(fingerprint) ?? 0) + 1)
  }
  return counts
}

function isBudgetProximityAlarm(attemptsUsed: number, budget: number): boolean {
  if (budget <= 0) return false
  return attemptsUsed / budget >= 0.75
}

type TaskDispatch = {
  task: string
  count: number
  budget: number
}

function taskDispatches(attempts: AttemptRecord[], fallbackBudget: number): TaskDispatch[] {
  const byTask = new Map<string, { dispatchCounts: number[]; attemptCount: number; budget?: number }>()
  for (const attempt of attempts) {
    const task = attempt.task ?? 'not recorded'
    const entry = byTask.get(task) ?? { dispatchCounts: [], attemptCount: 0 }
    entry.attemptCount += 1
    if (typeof attempt.taskDispatchCount === 'number') {
      entry.dispatchCounts.push(attempt.taskDispatchCount)
    }
    if (typeof attempt.taskDispatchBudget === 'number') {
      entry.budget = attempt.taskDispatchBudget
    }
    byTask.set(task, entry)
  }
  return [...byTask].map(([task, entry]) => ({
    task,
    count: entry.dispatchCounts.length > 0 ? Math.max(...entry.dispatchCounts) : entry.attemptCount,
    budget: entry.budget ?? fallbackBudget,
  }))
}

type UsageSummary = {
  inputTokens: number
  outputTokens: number
  unmetered: number
  notCaptured: number
  costUsd: number
  costUsdCaptured: boolean
  costUsdNotCaptured: number
  cachedTokens: number
  cachedTokensCaptured: boolean
  cachedTokensNotCaptured: number
}

function emptyUsageSummary(): UsageSummary {
  return {
    inputTokens: 0,
    outputTokens: 0,
    unmetered: 0,
    notCaptured: 0,
    costUsd: 0,
    costUsdCaptured: false,
    costUsdNotCaptured: 0,
    cachedTokens: 0,
    cachedTokensCaptured: false,
    cachedTokensNotCaptured: 0,
  }
}

function usageSummary(attempts: AttemptRecord[], groupBy: (attempt: AttemptRecord) => string) {
  const summaries = new Map<string, UsageSummary>()
  for (const attempt of attempts) {
    const key = groupBy(attempt)
    const summary = summaries.get(key) ?? emptyUsageSummary()
    if (!attempt.usage) summary.notCaptured += 1
    else if (isUsageUnknown(attempt.usage)) summary.unmetered += 1
    else {
      summary.inputTokens += attempt.usage.inputTokens
      summary.outputTokens += attempt.usage.outputTokens
      if (typeof attempt.usage.costUsd === 'number') {
        summary.costUsd += attempt.usage.costUsd
        summary.costUsdCaptured = true
      }
      else summary.costUsdNotCaptured += 1
      if (typeof attempt.usage.cachedTokens === 'number') {
        summary.cachedTokens += attempt.usage.cachedTokens
        summary.cachedTokensCaptured = true
      }
      else summary.cachedTokensNotCaptured += 1
    }
    summaries.set(key, summary)
  }
  return summaries
}

function formatCostUsd(costUsd: number) {
  return new Intl.NumberFormat('en-US', {
    style: 'currency',
    currency: 'USD',
    minimumFractionDigits: 4,
    maximumFractionDigits: 4,
  }).format(costUsd)
}

function usageLabel(summary: UsageSummary) {
  const values = [`${summary.inputTokens} in · ${summary.outputTokens} out`]
  if (summary.costUsdCaptured) values.push(formatCostUsd(summary.costUsd))
  if (summary.costUsdNotCaptured > 0) values.push(`${summary.costUsdNotCaptured} cost not captured`)
  if (summary.cachedTokensCaptured) values.push(`${summary.cachedTokens} cached`)
  if (summary.cachedTokensNotCaptured > 0) values.push(`${summary.cachedTokensNotCaptured} cached not captured`)
  if (summary.unmetered > 0) values.push(`${summary.unmetered} unmetered`)
  if (summary.notCaptured > 0) values.push(`${summary.notCaptured} not captured`)
  return values.join(' · ')
}

export function BurnPanel(props: {
  attempts: AttemptRecord[]
  budget: number
}) {
  const fingerprints = failureFingerprintCounts(props.attempts)
  const repeatedFingerprints = [...fingerprints].filter(([, count]) => count >= 2)
  const repeatFailure = repeatedFingerprints.length > 0
  const dispatchesByTask = taskDispatches(props.attempts, props.budget)
  const alarmingTasks = dispatchesByTask.filter((dispatch) => isBudgetProximityAlarm(dispatch.count, dispatch.budget))
  const budgetProximity = alarmingTasks.length > 0
  const usageBySeat = usageSummary(props.attempts, (attempt) => attempt.seat ?? 'not recorded')
  const usageByModel = usageSummary(props.attempts, (attempt) => attempt.usage?.model ?? attempt.model ?? 'not recorded')

  if (props.attempts.length === 0) return null

  return (
    <div
      data-testid="burn-panel"
      data-repeat-failure={repeatFailure ? 'true' : 'false'}
      data-budget-proximity={budgetProximity ? 'true' : 'false'}
      className={[
        'rounded-lg border px-4 py-3',
        repeatFailure || budgetProximity
          ? 'border-danger border-l-4 border-l-danger bg-danger/5'
          : 'border-border bg-surface',
      ].join(' ')}
    >
      <p className={['text-sm font-semibold', repeatFailure || budgetProximity ? 'text-danger' : 'text-fg'].join(' ')}>Burn</p>
      <ul className="mt-2 space-y-1 text-sm text-fg">
        {repeatFailure && (
          <li data-testid="burn-panel-repeat-failure">
            Repeat failure signature on multiple attempts
          </li>
        )}
        {budgetProximity && (
          <li data-testid="burn-panel-budget-proximity">
            Attempt budget at or above 75% (
            {alarmingTasks.map((dispatch) => `${dispatch.count}/${dispatch.budget}`).join(', ')}
            )
          </li>
        )}
      </ul>
      <div className="mt-3 grid gap-3 text-sm text-fg">
        <div>
          <p className="font-semibold text-fg-subtle">Task dispatches</p>
          <ul className="mt-1 space-y-1">
            {dispatchesByTask.map(({ task, count, budget }) => (
              <li key={task} data-testid={`burn-panel-task-${task}`}>
                {task}: {count}/{budget}
              </li>
            ))}
          </ul>
        </div>
        <div>
          <p className="font-semibold text-fg-subtle">Usage by seat</p>
          <ul className="mt-1 space-y-1">
            {[...usageBySeat].map(([seat, summary]) => (
              <li key={seat} data-testid={`burn-panel-seat-${seat}`}>{seat}: {usageLabel(summary)}</li>
            ))}
          </ul>
        </div>
        <div>
          <p className="font-semibold text-fg-subtle">Usage by model</p>
          <ul className="mt-1 space-y-1">
            {[...usageByModel].map(([model, summary]) => (
              <li key={model} data-testid={`burn-panel-model-${model}`}>{model}: {usageLabel(summary)}</li>
            ))}
          </ul>
        </div>
        {repeatedFingerprints.length > 0 && (
          <div>
            <p className="font-semibold text-fg-subtle">Repeated fingerprints</p>
            <ul className="mt-1 space-y-1">
              {repeatedFingerprints.map(([fingerprint, count]) => (
                <li key={fingerprint} data-testid={`burn-panel-fingerprint-${fingerprint}`}>
                  {fingerprint}: {count} attempts
                </li>
              ))}
            </ul>
          </div>
        )}
      </div>
    </div>
  )
}
