/**
 * Footer Display Extension
 *
 * Replaces the built-in pi footer with a single clean line that assembles
 * status from all other extensions:
 *
 *   ~dir  | S ⣿⣶⣀⣀⣀ 34% 2h 55m | W ⣿⣿⣷⣀⣀ 68% ⟳ Fri 09:00 | C ⣿⣀⣀⣀⣀ 20% | Sonnet 4.6 | rust-analyzer | MCP: 1/2
 *
 * Status sources:
 *   usage:update event — set by usage-bars extension       → S/W bars (Claude usage, always shown)
 *   ctx.getContextUsage()                                 → C bar (rendered here)
 *   ctx.model                                             → model short name
 *   "lsp"             — set by lsp-pi extension           → strip "LSP " prefix
 *   "mcp"             — set by pi-mcp-adapter             → strip " servers" suffix
 */

import os from "os";
import path from "path";
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
import { truncateToWidth } from "@mariozechner/pi-tui";

// ---------------------------------------------------------------------------
// Braille gradient bar — used here only for the context (C) bar
// ---------------------------------------------------------------------------
const BRAILLE_GRADIENT = "\u28C0\u28C4\u28E4\u28E6\u28F6\u28F7\u28FF";
const BRAILLE_EMPTY = "\u28C0";
const BAR_WIDTH = 5;

function renderBrailleBar(theme: any, value: number): string {
  const v = Math.max(0, Math.min(100, Math.round(value)));
  const levels = BRAILLE_GRADIENT.length - 1;
  const totalSteps = BAR_WIDTH * levels;
  const filledSteps = Math.round((v / 100) * totalSteps);
  const full = Math.floor(filledSteps / levels);
  const partial = filledSteps % levels;
  const empty = BAR_WIDTH - full - (partial ? 1 : 0);
  const color = v >= 90 ? "error" : v >= 70 ? "warning" : "success";
  const filled = BRAILLE_GRADIENT[BRAILLE_GRADIENT.length - 1]!.repeat(Math.max(0, full));
  const partialChar = partial ? BRAILLE_GRADIENT[partial]! : "";
  const emptyChars = BRAILLE_EMPTY.repeat(Math.max(0, empty));
  return theme.fg(color, filled + partialChar) + theme.fg("dim", emptyChars);
}

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
function stripAnsi(text: string): string {
  return text.replace(/\x1b\[[0-9;]*m/g, "").replace(/\x1b\][^\x07]*\x07/g, "");
}

function getModelShortName(modelId: string): string {
  // claude-haiku-4-5 → "Haiku 4.5",  claude-sonnet-4-6 → "Sonnet 4.6"
  const m = modelId.match(/^claude-([a-z]+)-([\d]+(?:-[\d]+)*)(?:-\d{8})?$/);
  if (m) {
    const family = m[1]!.charAt(0).toUpperCase() + m[1]!.slice(1);
    return `${family} ${m[2]!.replace(/-/g, ".")}`;
  }
  // claude-3-5-sonnet, claude-3-opus, etc.
  const m2 = modelId.match(/^claude-[\d-]+-([a-z]+)/);
  if (m2) return m2[1]!.charAt(0).toUpperCase() + m2[1]!.slice(1);
  return modelId.replace(/^claude-/, "");
}

// Format duration in milliseconds to human readable (e.g., "2h 55m")
function formatDurationMs(ms: number): string {
  if (!Number.isFinite(ms) || ms <= 0) return "now";
  const totalSeconds = Math.floor(ms / 1000);
  const d = Math.floor(totalSeconds / 86400);
  const h = Math.floor((totalSeconds % 86400) / 3600);
  const m = Math.floor((totalSeconds % 3600) / 60);
  if (d > 0 && h > 0) return `${d}d ${h}h`;
  if (d > 0) return `${d}d`;
  if (h > 0 && m > 0) return `${h}h ${m}m`;
  if (h > 0) return `${h}h`;
  if (m > 0) return `${m}m`;
  return "<1m";
}

export default function (pi: ExtensionAPI) {
  let ctx: any = null;
  let tuiRef: any = null;
  let footerDataRef: any = null;
  
  // Track usage data for dynamic S/W bar rendering
  let usageSession: number | null = null;
  let usageWeekly: number | null = null;
  let sessionResetsAt: number | null = null;
  let weeklyResetsAt: number | null = null;

  // ---------------------------------------------------------------------------
  // Footer line builder — called on every render
  // ---------------------------------------------------------------------------
  function buildFooterLine(theme: any): string {
    const sep = theme.fg("dim", "  · ");
    const pipeSep = theme.fg("dim", "  | ");
    const parts: string[] = [];

    const statuses: ReadonlyMap<string, string> =
      footerDataRef?.getExtensionStatuses?.() ?? new Map();

    // 1. Current working directory
    const home = os.homedir();
    const cwd = process.cwd();
    const dir = cwd.startsWith(home)
      ? "~" + path.sep + path.relative(home, cwd)
      : cwd;
    parts.push(theme.fg("dim", dir));

    // 2. S / W usage bars + C bar — joined as one |-separated block
    const usageRaw = statuses.get("usage-bars");
    const contextUsage = ctx?.getContextUsage?.();
    {
      let block: string;

      if (usageSession !== null && usageWeekly !== null) {
        // Build S/W bars directly from stored event data so we can cleanly
        // append the dynamic countdown without trying to parse ANSI strings.
        const session = Math.max(0, Math.min(100, Math.round(usageSession)));
        const weekly  = Math.max(0, Math.min(100, Math.round(usageWeekly)));

        let sPart = theme.fg("muted", "\uF4F5 S ") + renderBrailleBar(theme, session) + " " + theme.fg("dim", `${session}%`);
        let wPart = theme.fg("muted", "\uF4F5 W ") + renderBrailleBar(theme, weekly)  + " " + theme.fg("dim", `${weekly}%`);

        if (sessionResetsAt !== null) {
          const msLeft = sessionResetsAt - Date.now();
          if (msLeft > 0) sPart += " " + theme.fg("dim", formatDurationMs(msLeft));
        }

        if (weeklyResetsAt !== null) {
          const msLeft = weeklyResetsAt - Date.now();
          if (msLeft > 0) wPart += " " + theme.fg("dim", `\u27F3 ${formatDurationMs(msLeft)}`);
        }

        block = sPart + pipeSep + wPart;
      } else {
        // Fallback to raw status for loading / error states
        block = usageRaw ?? "";
      }

      if (contextUsage && contextUsage.percent !== null) {
        const pct = Math.round(contextUsage.percent);
        const chatStatus = statuses.get("chat-claude");
        const isChatActive = !!chatStatus && chatStatus.includes("Claude");
        
        // When chat is active and context is high, show warning indicator
        let cLabel = "C";
        let cColor = "muted";
        if (isChatActive && pct >= 70) {
          cLabel = pct >= 90 ? "C⚠" : "C⚡";
          cColor = pct >= 90 ? "error" : "warning";
        }
        
        const cBar =
          theme.fg(cColor, cLabel + " ") +
          renderBrailleBar(theme, pct) +
          " " +
          theme.fg(pct >= 70 && isChatActive ? "warning" : "dim", `${pct}%`);
        block = block ? block + pipeSep + cBar : cBar;
      }
      if (block) parts.push(block);
    }

    // 3. Model short name
    const modelId = ctx?.model?.id;
    if (modelId) parts.push(theme.fg("dim", getModelShortName(modelId)));

    // 4. LSP — strip "LSP" prefix and activity dot
    const lspRaw = statuses.get("lsp");
    if (lspRaw) {
      const clean = stripAnsi(lspRaw).trim().replace(/^LSP\s*[•·]?\s*/i, "").trim();
      if (clean) parts.push(theme.fg("dim", clean));
    }

    // 5. MCP — strip " servers" suffix
    const mcpRaw = statuses.get("mcp");
    if (mcpRaw) {
      const clean = stripAnsi(mcpRaw).trim().replace(/\s*servers?.*$/, "").trim();
      if (clean) parts.push(theme.fg("dim", clean));
    }

    // 6. Active Claude chat session
    const chatRaw = statuses.get("chat-claude");
    if (chatRaw) {
      parts.push(theme.fg("accent", stripAnsi(chatRaw).trim()));
    }

    return parts.join(sep);
  }

  // ---------------------------------------------------------------------------
  // Footer installation
  // ---------------------------------------------------------------------------
  function installFooter(_ctx: any) {
    if (!_ctx?.hasUI) return;
    _ctx.ui.setFooter((_tui: any, theme: any, footerData: any) => {
      tuiRef = _tui;
      footerDataRef = footerData;
      const unsub = footerData.onBranchChange(() => _tui.requestRender());
      return {
        dispose: unsub,
        invalidate() {},
        render(width: number): string[] {
          return [truncateToWidth(buildFooterLine(theme) || "", width)];
        },
      };
    });
  }

  // ---------------------------------------------------------------------------
  // Event handlers
  // ---------------------------------------------------------------------------
  pi.on("session_start", (_event, _ctx) => {
    ctx = _ctx;
    installFooter(_ctx);
  });

  pi.on("session_shutdown", (_event, _ctx) => {
    if (_ctx?.hasUI) _ctx.ui.setFooter(undefined);
  });

  // Re-render after turns so context usage stays current
  pi.on("turn_end", (_event, _ctx) => {
    ctx = _ctx;
    if (tuiRef) tuiRef.requestRender();
  });

  // Re-render when model changes (updates model name in footer)
  pi.on("model_select", (_event, _ctx) => {
    ctx = _ctx;
    if (tuiRef) tuiRef.requestRender();
  });


  // Listen for usage updates — store raw values so we can build bars + dynamic
  // countdown directly rather than parsing the ANSI status string from usage-bars.
  pi.events.on("usage:update", (data: any) => {
    if (data.session     !== undefined) usageSession   = data.session;
    if (data.weekly      !== undefined) usageWeekly    = data.weekly;
    if (data.sessionResetsAt !== undefined) sessionResetsAt = data.sessionResetsAt;
    if (data.weeklyResetsAt  !== undefined) weeklyResetsAt  = data.weeklyResetsAt;
    if (tuiRef) tuiRef.requestRender();
  });
}
