import type { Hono } from "hono";
import { readdirSync, statSync } from "node:fs";
import { join } from "node:path";
import { getWorkspaceRoot } from "./settings";

/** Node returned to the client. Matches the v1 shape exactly. */
export type TreeNode = {
  n: string;
  t: "d" | "f";
  c?: TreeNode[];
  s?: number;
  m?: number;
  p?: string;
  root?: string;
};

const SKIP_DIRS = new Set([
  "node_modules", ".git", ".next", "dist", "build", ".cache", "__pycache__", ".venv",
]);

/** Recursively walks `root`, returning a TreeNode tree. Empty directories are pruned. */
export function buildTree(root: string): TreeNode {
  function walk(path: string, name: string): TreeNode {
    const node: TreeNode = { n: name, t: "d", c: [] };
    let entries: string[] = [];
    try { entries = readdirSync(path); } catch { return node; }
    entries.sort((a, b) => {
      try {
        const aIsDir = statSync(join(path, a)).isDirectory();
        const bIsDir = statSync(join(path, b)).isDirectory();
        if (aIsDir !== bIsDir) return aIsDir ? -1 : 1;
      } catch {}
      return a.toLowerCase().localeCompare(b.toLowerCase());
    });
    for (const e of entries) {
      if (SKIP_DIRS.has(e) || e === ".DS_Store" || e.endsWith(".pyc")) continue;
      const full = join(path, e);
      try {
        const st = statSync(full);
        if (st.isDirectory()) {
          const child = walk(full, e);
          if (child.c!.length) node.c!.push(child);
        } else if (st.isFile()) {
          node.c!.push({
            n: e,
            t: "f",
            s: st.size,
            m: Math.floor(st.mtimeMs / 1000),
            p: full.slice(root.length + 1),
          });
        }
      } catch {}
    }
    return node;
  }
  const tree = walk(root, root.split("/").pop() || "workspace") as TreeNode;
  tree.root = root;
  return tree;
}

/**
 * In-memory tree cache. /tree.json hits the cache (TTL 5s); /api/tree
 * always rebuilds and refreshes the cache — used by manual refresh and
 * the focus auto-refresh in Sidebar.
 *
 * 5s is long enough that rapid clicks (panel toggles, navigation) reuse
 * the cache instantly, and short enough that a file you just dropped
 * via Finder shows up within seconds without explicit refresh.
 */
const TTL_MS = 5_000;
let cache: { builtAt: number; tree: TreeNode } | null = null;

function getTreeCached(): TreeNode {
  if (cache && Date.now() - cache.builtAt < TTL_MS) return cache.tree;
  const tree = buildTree(getWorkspaceRoot());
  cache = { builtAt: Date.now(), tree };
  return tree;
}

function getTreeFresh(): TreeNode {
  const tree = buildTree(getWorkspaceRoot());
  cache = { builtAt: Date.now(), tree };
  return tree;
}

export function registerTreeRoutes(app: Hono) {
  app.get("/tree.json", (c) => c.json(getTreeCached()));
  app.get("/api/tree", (c) => c.json(getTreeFresh()));
}
