"use client";

import { AlertTriangle, Clock, Flame, TrendingDown, Zap } from "lucide-react";
import type {
  ModelUsageEntry,
  RuntimeUsageResponse,
  TopSession,
} from "@/api/generated/model";
import { DashboardSection } from "./DashboardSection";
import { DashboardEmptyState } from "./DashboardEmptyState";

// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------

export interface AggregatedRuntimeUsage {
  totalCostUsd: number;
  totalTokens: number;
  totalOutputTokens: number;
  totalCalls: number;
  // Legacy (backwards compat)
  tokenLimit: number | null;
  tokenPct: number | null;
  costLimitUsd: number | null;
  // Typed limits (Phase 4)
  outputTokenLimit: number | null;
  outputTokenLimitPct: number | null;
  messageLimit: number | null;
  messagePct: number | null;
  limitKind: string;  // which limit drove the time-to-limit prediction
  resetInMs: number;
  resetsAt: string;
  timeToLimitMs: number | null;
  safe: boolean;
  tokensPerMinute: number;
  outputTokensPerMinute: number;
  costUsdPerMinute: number;
  perModel: Record<string, ModelUsageEntry>;
  topSessions: TopSession[];
}

export interface ProviderNativeUsageWindow {
  key: string;
  label: string;
  pctUsed: number | null;
  remainingMs: number | null;
  remainingLabel: string | null;
  source: string;
  confidence: string;
  provider: string;
  gatewayLabel: string;
}

// ---------------------------------------------------------------------------
// Aggregation helper (called from dashboard page)
// ---------------------------------------------------------------------------

export function aggregateRuntimeUsage(
  responses: RuntimeUsageResponse[],
): AggregatedRuntimeUsage {
  const valid = responses.filter(Boolean);
  if (valid.length === 0) {
    return {
      totalCostUsd: 0,
      totalTokens: 0,
      totalOutputTokens: 0,
      totalCalls: 0,
      tokenLimit: null,
      tokenPct: null,
      costLimitUsd: null,
      outputTokenLimit: null,
      outputTokenLimitPct: null,
      messageLimit: null,
      messagePct: null,
      limitKind: "total_tokens",
      resetInMs: 0,
      resetsAt: "",
      timeToLimitMs: null,
      safe: true,
      tokensPerMinute: 0,
      outputTokensPerMinute: 0,
      costUsdPerMinute: 0,
      perModel: {},
      topSessions: [],
    };
  }

  let totalCostUsd = 0;
  let totalTokens = 0;
  let totalOutputTokens = 0;
  let totalCalls = 0;
  let tokensPerMinute = 0;
  let outputTokensPerMinute = 0;
  let costUsdPerMinute = 0;
  let resetInMs = valid[0].window.reset_in_ms;
  let resetsAt = valid[0].window.resets_at;
  const perModel: Record<string, ModelUsageEntry> = {};
  const allSessions: TopSession[] = [];

  for (const r of valid) {
    totalCostUsd          += r.current.total_cost_usd;
    totalTokens           += r.current.total_tokens;
    totalOutputTokens     += r.current.total_output_tokens ?? 0;
    totalCalls            += r.current.total_calls;
    tokensPerMinute       += r.burn_rate.tokens_per_minute;
    outputTokensPerMinute += r.burn_rate.output_tokens_per_minute ?? 0;
    costUsdPerMinute      += r.burn_rate.cost_usd_per_minute;

    if (r.window.reset_in_ms < resetInMs) {
      resetInMs = r.window.reset_in_ms;
      resetsAt  = r.window.resets_at;
    }

    for (const [key, entry] of Object.entries(r.per_model)) {
      if (perModel[key]) {
        const e = perModel[key];
        perModel[key] = {
          ...e,
          input_tokens:       e.input_tokens       + entry.input_tokens,
          output_tokens:      e.output_tokens       + entry.output_tokens,
          cache_read_tokens:  e.cache_read_tokens   + entry.cache_read_tokens,
          cache_write_tokens: e.cache_write_tokens  + entry.cache_write_tokens,
          total_tokens:       e.total_tokens        + entry.total_tokens,
          cost_usd:           e.cost_usd            + entry.cost_usd,
          calls:              e.calls               + entry.calls,
        };
      } else {
        perModel[key] = { ...entry };
      }
    }

    allSessions.push(...r.top_sessions);
  }

  // Legacy token limit
  const limits    = valid.map(r => r.current.token_limit).filter((v): v is number => v !== null);
  const tokenLimit    = limits.length > 0 ? limits.reduce((a, b) => a + b, 0) : null;
  const tokenPct      = tokenLimit ? Math.min(100, Math.round(totalTokens * 100 / tokenLimit)) : null;
  const costLimits    = valid.map(r => r.current.cost_limit_usd).filter((v): v is number => v !== null);
  const costLimitUsd  = costLimits.length > 0 ? costLimits.reduce((a, b) => a + b, 0) : null;

  // Typed limits
  const outLimits  = valid.map(r => r.current.output_token_limit).filter((v): v is number => v !== null);
  const outputTokenLimit    = outLimits.length > 0 ? outLimits.reduce((a, b) => a + b, 0) : null;
  const outputTokenLimitPct = outputTokenLimit
    ? Math.min(100, Math.round(totalOutputTokens * 100 / outputTokenLimit))
    : null;

  const msgLimits  = valid.map(r => r.current.message_limit).filter((v): v is number => v !== null);
  const messageLimit = msgLimits.length > 0 ? msgLimits.reduce((a, b) => a + b, 0) : null;
  const messagePct   = messageLimit ? Math.min(100, Math.round(totalCalls * 100 / messageLimit)) : null;

  // Re-derive time-to-limit using the most constrained matching limit
  let timeToLimitMs: number | null = null;
  let safe = true;
  let limitKind = "total_tokens";
  const candidates: Array<{ ms: number; kind: string }> = [];

  if (outputTokenLimit !== null && outputTokensPerMinute > 0) {
    const rem = outputTokenLimit - totalOutputTokens;
    if (rem <= 0) return { totalCostUsd: Math.round(totalCostUsd * 1e8) / 1e8, totalTokens, totalOutputTokens, totalCalls, tokenLimit, tokenPct, costLimitUsd, outputTokenLimit, outputTokenLimitPct, messageLimit, messagePct, limitKind: "output_tokens", resetInMs, resetsAt, timeToLimitMs: 0, safe: false, tokensPerMinute: Math.round(tokensPerMinute * 100) / 100, outputTokensPerMinute: Math.round(outputTokensPerMinute * 100) / 100, costUsdPerMinute: Math.round(costUsdPerMinute * 1e8) / 1e8, perModel, topSessions: [...allSessions].sort((a, b) => b.cost_usd - a.cost_usd).slice(0, 10) };
    candidates.push({ ms: Math.round(rem / outputTokensPerMinute * 60_000), kind: "output_tokens" });
  }
  if (tokenLimit !== null && tokensPerMinute > 0) {
    const rem = tokenLimit - totalTokens;
    if (rem <= 0) return { totalCostUsd: Math.round(totalCostUsd * 1e8) / 1e8, totalTokens, totalOutputTokens, totalCalls, tokenLimit, tokenPct, costLimitUsd, outputTokenLimit, outputTokenLimitPct, messageLimit, messagePct, limitKind: "total_tokens", resetInMs, resetsAt, timeToLimitMs: 0, safe: false, tokensPerMinute: Math.round(tokensPerMinute * 100) / 100, outputTokensPerMinute: Math.round(outputTokensPerMinute * 100) / 100, costUsdPerMinute: Math.round(costUsdPerMinute * 1e8) / 1e8, perModel, topSessions: [...allSessions].sort((a, b) => b.cost_usd - a.cost_usd).slice(0, 10) };
    candidates.push({ ms: Math.round(rem / tokensPerMinute * 60_000), kind: "total_tokens" });
  }
  if (candidates.length > 0) {
    const tightest = candidates.reduce((a, b) => a.ms < b.ms ? a : b);
    timeToLimitMs = tightest.ms;
    limitKind = tightest.kind;
    safe = timeToLimitMs > resetInMs;
  }

  const topSessions = [...allSessions]
    .sort((a, b) => b.cost_usd - a.cost_usd)
    .slice(0, 10);

  return {
    totalCostUsd: Math.round(totalCostUsd * 1e8) / 1e8,
    totalTokens,
    totalOutputTokens,
    totalCalls,
    tokenLimit,
    tokenPct,
    costLimitUsd,
    outputTokenLimit,
    outputTokenLimitPct,
    messageLimit,
    messagePct,
    limitKind,
    resetInMs,
    resetsAt,
    timeToLimitMs,
    safe,
    tokensPerMinute: Math.round(tokensPerMinute * 100) / 100,
    outputTokensPerMinute: Math.round(outputTokensPerMinute * 100) / 100,
    costUsdPerMinute: Math.round(costUsdPerMinute * 1e8) / 1e8,
    perModel,
    topSessions,
  };
}

// ---------------------------------------------------------------------------
// Formatters
// ---------------------------------------------------------------------------

function fmtMs(ms: number | null): string {
  if (ms === null) return "—";
  if (ms <= 0) return "0m";
  if (ms < 60_000) return "< 1m";
  const totalMin = Math.floor(ms / 60_000);
  if (totalMin < 60) return `${totalMin}m`;
  const h = Math.floor(totalMin / 60);
  const m = totalMin % 60;
  return m > 0 ? `${h}h ${m}m` : `${h}h`;
}

function fmtCost(usd: number): string {
  if (usd === 0) return "$0.00";
  if (usd < 0.001) return `$${usd.toFixed(6)}`;
  if (usd < 0.01)  return `$${usd.toFixed(4)}`;
  return `$${usd.toFixed(2)}`;
}

function fmtTokens(n: number): string {
  if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;
  if (n >= 1_000)     return `${(n / 1_000).toFixed(1)}k`;
  return n.toString();
}

function modelLabel(key: string): string {
  // "anthropic/claude-sonnet-4-6" → "claude-sonnet-4-6"
  return key.includes("/") ? key.split("/")[1] : key;
}

function providerLabel(key: string): string {
  return key.includes("/") ? key.split("/")[0] : key;
}

// ---------------------------------------------------------------------------
// Sub-components
// ---------------------------------------------------------------------------

interface StatCardProps {
  label: string;
  value: string;
  sub?: string;
  tone?: "default" | "warning" | "danger" | "success";
  icon: React.ReactNode;
}

function StatCard({ label, value, sub, tone = "default", icon }: StatCardProps) {
  const toneClass = {
    default: "text-[color:var(--text)]",
    warning: "text-[color:var(--warning)]",
    danger:  "text-[color:var(--danger)]",
    success: "text-[color:var(--success)]",
  }[tone];

  return (
    <div className="flex flex-col gap-1 rounded-lg border border-[color:var(--border)] bg-[color:var(--surface-muted)] p-3">
      <div className="flex items-center gap-1.5 text-[11px] font-semibold uppercase tracking-wider text-muted">
        {icon}
        {label}
      </div>
      <p className={`font-heading text-xl font-semibold leading-tight ${toneClass}`}>{value}</p>
      {sub && <p className="text-[11px] text-muted">{sub}</p>}
    </div>
  );
}

// ---------------------------------------------------------------------------
// Main component
// ---------------------------------------------------------------------------

interface RuntimeUsageSectionProps {
  usage: AggregatedRuntimeUsage | null;
  providerUsageWindows: ProviderNativeUsageWindow[];
  isLoading: boolean;
  hasGateways: boolean;
}

export function RuntimeUsageSection({
  usage,
  providerUsageWindows,
  isLoading,
  hasGateways,
}: RuntimeUsageSectionProps) {
  if (!hasGateways) return null;

  const hasRuntimeData = Boolean(
    usage && (usage.totalCalls > 0 || Object.keys(usage.perModel).length > 0),
  );
  const safenessTone = usage
    ? usage.safe
      ? "success"
      : "danger"
    : "default";

  const modelRows = usage
    ? Object.entries(usage.perModel).sort((a, b) => b[1].cost_usd - a[1].cost_usd)
    : [];
  const providerRows = [...providerUsageWindows].sort((a, b) => {
    const order: Record<string, number> = {
      current_session: 0,
      weekly_all_models: 1,
      weekly_sonnet: 2,
      extra_usage: 3,
    };
    const left = order[a.key] ?? 99;
    const right = order[b.key] ?? 99;
    return left === right ? a.gatewayLabel.localeCompare(b.gatewayLabel) : left - right;
  });
  const noData = !hasRuntimeData && providerRows.length === 0;

  return (
    <DashboardSection title="Runtime Usage">
      {isLoading && !usage ? (
        <DashboardEmptyState message="Loading usage data…" />
      ) : noData ? (
        <DashboardEmptyState message="No usage data yet. Usage appears after the first model call." />
      ) : hasRuntimeData || providerRows.length > 0 ? (
        <div className="space-y-3">
          {providerRows.length > 0 && (
            <div className="space-y-2 rounded-lg border border-[color:var(--border)] bg-[color:var(--surface-muted)] p-3">
              <p className="text-[11px] font-semibold uppercase tracking-wider text-muted">
                Provider-native usage windows
              </p>
              <div className="space-y-2">
                {providerRows.map((window, index) => {
                  const pctUsed = window.pctUsed ?? 0;
                  const barTone =
                    pctUsed >= 90
                      ? "bg-[color:var(--danger)]"
                      : pctUsed >= 75
                        ? "bg-[color:var(--warning)]"
                        : "bg-[color:var(--success)]";
                  const resetText =
                    window.remainingLabel
                    ?? (window.remainingMs !== null ? fmtMs(window.remainingMs) : "—");
                  const isEstimate = window.source !== "provider_native" || window.confidence !== "high";
                  return (
                    <div
                      key={`${window.gatewayLabel}-${window.provider}-${window.key}-${index}`}
                      className="rounded-md border border-[color:var(--border)] bg-[color:var(--surface)] p-2"
                    >
                      <div className="mb-1 flex items-center justify-between gap-2">
                        <p className="text-xs font-medium text-strong">
                          {window.label}
                          {isEstimate ? " (estimate)" : ""}
                        </p>
                        <p className="text-xs tabular-nums text-muted">
                          {window.pctUsed === null ? "—" : `${Math.round(window.pctUsed)}% used`}
                        </p>
                      </div>
                      <div className="h-2 w-full overflow-hidden rounded-full bg-[color:var(--surface-muted)]">
                        <div
                          className={`h-full rounded-full transition-all duration-300 ${barTone}`}
                          style={{ width: `${Math.max(0, Math.min(100, pctUsed))}%` }}
                        />
                      </div>
                      <p className="mt-1 text-[11px] text-muted">
                        Resets in {resetText} · {window.provider} · {window.gatewayLabel}
                      </p>
                    </div>
                  );
                })}
              </div>
            </div>
          )}

          {usage && hasRuntimeData && (
            <div className="grid grid-cols-2 gap-2 sm:grid-cols-4">
              <StatCard
                label="5h Spend"
                value={fmtCost(usage.totalCostUsd)}
                sub={`${fmtTokens(usage.totalTokens)} tokens`}
                tone={usage.costLimitUsd && usage.totalCostUsd / usage.costLimitUsd > 0.8 ? "warning" : "default"}
                icon={<Zap className="h-3 w-3" />}
              />
              <StatCard
                label="Reset In"
                value={fmtMs(usage.resetInMs)}
                sub={usage.resetsAt ? new Date(usage.resetsAt).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }) : undefined}
                icon={<Clock className="h-3 w-3" />}
              />
              <StatCard
                label="Time to Limit"
                value={usage.timeToLimitMs === null ? "—" : usage.timeToLimitMs === 0 ? "At limit" : fmtMs(usage.timeToLimitMs)}
                sub={(() => {
                  if (usage.outputTokenLimit) {
                    return `${usage.outputTokenLimitPct ?? 0}% of ${fmtTokens(usage.outputTokenLimit)} out`;
                  }
                  if (usage.tokenLimit) {
                    return `${usage.tokenPct ?? 0}% of ${fmtTokens(usage.tokenLimit)}`;
                  }
                  if (usage.messageLimit) {
                    return `${usage.messagePct ?? 0}% of ${usage.messageLimit} msgs`;
                  }
                  return undefined;
                })()}
                tone={safenessTone}
                icon={<TrendingDown className="h-3 w-3" />}
              />
              <StatCard
                label="Burn Rate"
                value={usage.costUsdPerMinute > 0 ? `${fmtCost(usage.costUsdPerMinute)}/m` : "—"}
                sub={(() => {
                  if (usage.outputTokensPerMinute > 0 && usage.outputTokenLimit) {
                    return `${fmtTokens(usage.outputTokensPerMinute)} out-tok/m`;
                  }
                  if (usage.tokensPerMinute > 0) {
                    return `${fmtTokens(usage.tokensPerMinute)} tok/m`;
                  }
                  return undefined;
                })()}
                icon={<Flame className="h-3 w-3" />}
              />
            </div>
          )}

          {/* Unpriced models warning */}
          {usage && hasRuntimeData && modelRows.some(([, e]) => e.unpriced) && (
            <div className="flex items-start gap-2 rounded-lg border border-[color:var(--warning)]/35 bg-[color:rgba(251,191,36,0.10)] px-3 py-2 text-xs text-[color:var(--warning)]">
              <AlertTriangle className="mt-0.5 h-3.5 w-3.5 shrink-0" />
              <span>Some models have no pricing config — costs may be under-reported.</span>
            </div>
          )}

          {/* Per-model breakdown */}
          {usage && hasRuntimeData && modelRows.length > 0 && (
            <div className="overflow-hidden rounded-lg border border-[color:var(--border)]">
              <table className="w-full text-xs">
                <thead>
                  <tr className="border-b border-[color:var(--border)] bg-[color:var(--surface-muted)]">
                    <th className="px-3 py-2 text-left font-semibold text-muted">Model</th>
                    <th className="px-3 py-2 text-right font-semibold text-muted">Tokens out</th>
                    <th className="px-3 py-2 text-right font-semibold text-muted">Cost</th>
                    <th className="px-3 py-2 text-right font-semibold text-muted">Calls</th>
                    {(usage.outputTokenLimit !== null || usage.tokenLimit !== null) && (
                      <th className="px-3 py-2 text-right font-semibold text-muted">
                        {usage.outputTokenLimit !== null ? "% out limit" : "% limit"}
                      </th>
                    )}
                  </tr>
                </thead>
                <tbody>
                  {modelRows.map(([key, entry]) => (
                    <tr
                      key={key}
                      className="border-b border-[color:var(--border)] last:border-0 bg-[color:var(--surface)]"
                    >
                      <td className="px-3 py-2">
                        <div className="font-medium text-strong">{modelLabel(key)}</div>
                        <div className="text-[11px] text-muted">{providerLabel(key)}{entry.unpriced ? " · unpriced" : ""}</div>
                      </td>
                      <td className="px-3 py-2 text-right tabular-nums text-muted">
                        {fmtTokens(entry.output_tokens)}
                      </td>
                      <td className="px-3 py-2 text-right tabular-nums text-strong">
                        {entry.unpriced ? <span className="text-[color:var(--warning)]">—*</span> : fmtCost(entry.cost_usd)}
                      </td>
                      <td className="px-3 py-2 text-right tabular-nums text-muted">{entry.calls}</td>
                      {usage.outputTokenLimit !== null && (
                        <td className="px-3 py-2 text-right tabular-nums text-muted">
                          {Math.round(entry.output_tokens * 100 / usage.outputTokenLimit)}%
                        </td>
                      )}
                      {usage.outputTokenLimit === null && usage.tokenLimit !== null && (
                        <td className="px-3 py-2 text-right tabular-nums text-muted">
                          {Math.round(entry.total_tokens * 100 / usage.tokenLimit)}%
                        </td>
                      )}
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          )}
        </div>
      ) : null}
    </DashboardSection>
  );
}
