import type { Message } from '@/api/types';

interface Props {
  messages: Message[];
  // v1.11.5: model's full context window from chat.model_context_limit
  // (server-side getModelContext lookup). Lets us render a meaningful
  // zero-state (0 / max, muted) before any assistant message has run.
  // null/undefined means lookup failed — bar still renders, but with an
  // "Context — / —" placeholder rather than misleading 0/0 math.
  modelContextLimit?: number | null;
}

// v1.11.5.1: inline persistent context-usage indicator. Lives in the same
// horizontal row as the agent picker (was a separate row above; user
// pointed at the empty space next to "Code Reviewer ▾  +" and asked for
// the bar there). Caller wraps in a flex container and ContextBar takes
// the remaining width via `flex-1 min-w-0`. Color tiers fire against
// (max - 20k compaction reserve) so the bar warns amber/orange/red at
// the same boundaries the server's auto-compaction triggers.
const COMPACTION_BUFFER = 20_000;

// Take the latest ctx_used and the latest ctx_max INDEPENDENTLY (newest-first).
// They needn't be on the same message: ctx_max is the model's context window — a
// constant per model — while some agents report it only intermittently (the claude
// SDK populates modelUsage.contextWindow on some turns, not all) yet report
// ctx_used every turn. Pairing the latest of each gives a correct used/max even
// when the most recent turn omitted the window. Native BooChat sets both on the
// same assistant message, so this is identical there. Returns null until BOTH a
// used and a positive max have been seen at least once.
function latestPair(messages: Message[]): { used: number; max: number } | null {
  let used: number | null = null;
  let max: number | null = null;
  for (let i = messages.length - 1; i >= 0; i--) {
    const m = messages[i]!;
    if (used === null && m.ctx_used != null) used = m.ctx_used;
    if (max === null && m.ctx_max != null && m.ctx_max > 0) max = m.ctx_max;
    if (used !== null && max !== null) break;
  }
  return used !== null && max !== null ? { used, max } : null;
}

interface ColorTier {
  // Tailwind utility for the label / numbers. Uses literal palette names
  // rather than design tokens because we want three distinct severities
  // (amber → orange → red) and BooCode only defines one warning token
  // (`destructive`). Literal classes keep the gradation explicit.
  text: string;
  bar: string;
}

function tierFor(usablePct: number): ColorTier {
  if (usablePct >= 0.95) return { text: 'text-red-600 dark:text-red-400', bar: 'bg-red-500' };
  if (usablePct >= 0.80) return { text: 'text-orange-600 dark:text-orange-400', bar: 'bg-orange-500' };
  if (usablePct >= 0.60) return { text: 'text-amber-600 dark:text-amber-400', bar: 'bg-amber-500' };
  return { text: 'text-muted-foreground', bar: 'bg-muted-foreground/40' };
}

export function ContextBar({ messages, modelContextLimit }: Props) {
  // Resolve which of the three render branches applies:
  //   1. real pair      — actual usage from the latest assistant message
  //   2. zero-state     — no usage yet but we know the model's limit
  //   3. unknown        — neither usage nor limit; render placeholder
  // The component NEVER returns null per v1.11.5 spec — the bar is
  // persistent so the user knows where it lives.
  const pair = latestPair(messages);
  const usable: number | null = pair
    ? Math.max(0, pair.max - COMPACTION_BUFFER)
    : modelContextLimit && modelContextLimit > 0
      ? Math.max(0, modelContextLimit - COMPACTION_BUFFER)
      : null;

  const used = pair?.used ?? 0;
  const max = pair?.max ?? (modelContextLimit && modelContextLimit > 0 ? modelContextLimit : null);

  // pct/usablePct only meaningful when max is known. The unknown branch
  // sets fill width to 0 and tier to muted regardless.
  const pct = max ? used / max : 0;
  const usablePct = usable && usable > 0 ? used / usable : 0;
  const tier = tierFor(usablePct);

  // Bar fill clamped to [0, 100]. Over-budget cases (usable < used) still
  // show the bar at 100% red rather than overflowing the track visually.
  const fillPct = Math.min(100, Math.max(0, pct * 100));
  const compactionThresholdPct =
    max && usable && usable > 0 ? Math.round((usable / max) * 100) : null;
  const tooltipText =
    compactionThresholdPct !== null
      ? `Auto-compaction at ~${compactionThresholdPct}%`
      : 'Model context unknown.';

  // `flex-1 min-w-0` lets the bar consume the remaining width inside the
  // picker row's flex container while preventing the numbers (whitespace-
  // nowrap) from pushing the bar out of bounds. Two-element row: track on
  // the left, numbers on the right.
  return (
    <div className="flex items-center gap-2 flex-1 min-w-0">
      <div className="flex-1 h-2 rounded-full bg-muted overflow-hidden min-w-0">
        <div
          className={`h-full ${tier.bar} transition-[width] duration-300`}
          style={{ width: `${fillPct}%` }}
        />
      </div>
      <span
        className={`${tier.text} text-[10px] font-mono whitespace-nowrap shrink-0`}
        title={tooltipText}
      >
        {max !== null ? (
          <>
            {/* Absolute counts hidden on very narrow viewports so the
                percentage always has room. Tooltip carries full detail. */}
            <span className="max-[480px]:hidden">
              {used.toLocaleString()} / {max.toLocaleString()}{' '}
            </span>
            ({Math.round(pct * 100)}%)
          </>
        ) : (
          <>— / —</>
        )}
      </span>
    </div>
  );
}
