import type { ExtensionContext, Theme } from "@singularity-forge/pi-coding-agent";
import { truncateToWidth, visibleWidth, matchesKey, Key } from "@singularity-forge/pi-tui";
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
import { join, dirname } from "node:path";
import { homedir } from "node:os";

const LIMIT = 20;

interface Data {
  version: number;
  history: string[];
}

function stashPath(): string {
  return join(homedir(), ".sf", "agent", "prompt-history.json");
}

export function readStash(): string[] {
  try {
    const path = stashPath();
    if (!existsSync(path)) return [];
    const d = JSON.parse(readFileSync(path, "utf-8")) as Data;
    return d.history.filter((h): h is string => typeof h === "string" && h.trim().length > 0);
  } catch {
    return [];
  }
}

export function writeStash(history: string[]): void {
  try {
    const path = stashPath();
    mkdirSync(dirname(path), { recursive: true });
    writeFileSync(
      path,
      JSON.stringify({ version: 1, history: history.slice(0, LIMIT) }, null, 2) + "\n",
      "utf-8",
    );
  } catch {
    /* non-fatal */
  }
}

export function pushStash(history: string[], text: string): void {
  const t = text.trim();
  if (!t || history[0] === t) return;
  history.unshift(t);
  if (history.length > LIMIT) {
    history.length = LIMIT;
  }
}

function preview(text: string, maxWidth: number): string {
  const c = text.replace(/\s+/g, " ").trim();
  return c ? truncateToWidth(c, maxWidth, "…") : "(empty)";
}

class StashOverlay {
  private tui: { requestRender: () => void };
  private theme: Theme;
  private done: (s: string | null) => void;
  private items: string[];
  private sel = 0;
  private cacheW = 0;
  private cacheL: string[] = [];

  constructor(
    tui: { requestRender: () => void },
    theme: Theme,
    items: string[],
    done: (s: string | null) => void,
  ) {
    this.tui = tui;
    this.theme = theme;
    this.items = items;
    this.done = done;
  }

  handleInput(data: string): void {
    if (matchesKey(data, Key.escape) || matchesKey(data, Key.ctrl("c"))) {
      this.done(null);
      return;
    }
    if (matchesKey(data, Key.return) || matchesKey(data, Key.enter)) {
      this.done(this.items[this.sel] ?? null);
      return;
    }
    if (matchesKey(data, Key.down) || data === "j") {
      this.sel = Math.min(this.items.length - 1, this.sel + 1);
      this.invalidate();
      this.tui.requestRender();
      return;
    }
    if (matchesKey(data, Key.up) || data === "k") {
      this.sel = Math.max(0, this.sel - 1);
      this.invalidate();
      this.tui.requestRender();
      return;
    }
    if (data >= "1" && data <= "9") {
      const idx = parseInt(data, 10) - 1;
      if (idx >= 0 && idx < this.items.length) {
        this.done(this.items[idx] ?? null);
      }
    }
  }

  invalidate(): void {
    this.cacheW = 0;
  }

  render(width: number): string[] {
    if (this.cacheW === width) return this.cacheL;

    const th = this.theme;
    const bw = Math.min(84, width - 4);
    const iw = bw - 4;

    const pad = (s: string) => s + " ".repeat(Math.max(0, width - visibleWidth(s)));
    const box = (s: string) => {
      const len = visibleWidth(s);
      return th.fg("dim", "│ ") + s + " ".repeat(Math.max(0, bw - 2 - len)) + th.fg("dim", " │");
    };

    const lines: string[] = [];
    lines.push(pad(th.fg("dim", "╭" + "─".repeat(bw) + "╮")));
    lines.push(pad(box(th.bold(th.fg("accent", "📜 Prompt History")))));
    lines.push(pad(th.fg("dim", "├" + "─".repeat(bw) + "┤")));
    lines.push(pad(box(th.fg("dim", "↑/jk navigate • 1-9 quick pick • Enter insert • Esc cancel"))));
    lines.push(pad(box("")));

    for (let i = 0; i < this.items.length; i++) {
      const item = this.items[i]!;
      const p = preview(item, iw - 8);
      const ptr = i === this.sel ? th.fg("accent", "❯ ") : "  ";
      const num = i < 9 ? th.fg("dim", `${i + 1}`) : " ";
      const label = i === this.sel ? th.fg("accent", p) : p;
      lines.push(pad(box(`${ptr}${num}. ${label}`)));
    }

    lines.push(pad(box("")));
    lines.push(pad(th.fg("dim", "├" + "─".repeat(bw) + "┤")));
    lines.push(pad(box(th.fg("dim", `${this.items.length} stashed prompts`))));
    lines.push(pad(th.fg("dim", "╰" + "─".repeat(bw) + "╯")));
    lines.push("");

    this.cacheL = lines;
    this.cacheW = width;
    return lines;
  }
}

export async function openStashOverlay(ctx: ExtensionContext): Promise<void> {
  if (!ctx.hasUI) {
    ctx.ui.notify("Prompt history requires interactive mode", "error");
    return;
  }
  const items = readStash();
  if (!items.length) {
    ctx.ui.notify("No stashed prompts yet. Send a message to build history.", "info");
    return;
  }
  const selected = await ctx.ui.custom<string | null>((tui, theme, _kb, done) => {
    const o = new StashOverlay(tui, theme, items, done);
    return {
      render: (w) => o.render(w),
      invalidate: () => o.invalidate(),
      handleInput: (d) => o.handleInput(d),
    };
  }, {
    overlay: true,
    overlayOptions: {
      width: "90%",
      minWidth: 60,
      maxHeight: "85%",
      anchor: "center",
      backdrop: true,
    },
  });
  if (selected) {
    ctx.ui.setEditorText(selected);
    ctx.ui.notify("Inserted prompt from history", "info");
  }
}
