import { existsSync, lstatSync, readlinkSync, realpathSync } from "node:fs";
import { join, resolve } from "node:path";
import { SEAT_ACCOUNT_ALLOWLIST } from "./seat-constants.mjs";

export function validateAllowlistTreeEntry(accountHome, name, deps = {}) {
  if (!SEAT_ACCOUNT_ALLOWLIST.includes(name)) {
    return { ok: false, reason: "not-allowlisted" };
  }
  const lstat = deps.lstatSync ?? lstatSync;
  const readlink = deps.readlinkSync ?? readlinkSync;
  const realpath = deps.realpathSync ?? realpathSync;
  const root = resolve(accountHome);
  const path = join(root, name);
  let st;
  try {
    st = lstat(path);
  } catch {
    return { ok: false, reason: "missing" };
  }
  if (st.isSymbolicLink()) {
    return validateSymlinkTarget(root, path, readlink, realpath, new Set());
  }
  if (st.isFile() || st.isDirectory()) return { ok: true, kind: st.isDirectory() ? "dir" : "file" };
  return { ok: false, reason: "special-file" };
}

function validateSymlinkTarget(root, path, readlink, realpath, seen) {
  if (seen.has(path)) return { ok: false, reason: "symlink-cycle" };
  seen.add(path);
  let target = readlink(path);
  if (!target) return { ok: false, reason: "symlink-empty" };
  const resolved = target.startsWith("/") ? resolve(target) : resolve(dirname(path), target);
  if (!resolved.startsWith(root + "/") && resolved !== root) {
    return { ok: false, reason: "symlink-escape" };
  }
  try {
    const st = lstatSync(resolved);
    if (st.isSymbolicLink()) return validateSymlinkTarget(root, resolved, readlink, realpath, seen);
    if (st.isFile() || st.isDirectory()) return { ok: true, kind: "symlink" };
    return { ok: false, reason: "special-file" };
  } catch {
    return { ok: false, reason: "symlink-broken" };
  }
}

function dirname(p) {
  const idx = p.lastIndexOf("/");
  return idx > 0 ? p.slice(0, idx) : "/";
}

export function validateAccountAllowlistTree(accountHome, deps = {}) {
  const exists = deps.existsSync ?? existsSync;
  const errors = [];
  for (const name of SEAT_ACCOUNT_ALLOWLIST) {
    const path = join(accountHome, name);
    if (!exists(path)) continue;
    const check = validateAllowlistTreeEntry(accountHome, name, deps);
    if (!check.ok) errors.push(`${name}:${check.reason}`);
  }
  return { ok: errors.length === 0, errors };
}

export function collectValidatedAllowlistMembers(accountHome, deps = {}) {
  const check = validateAccountAllowlistTree(accountHome, deps);
  if (!check.ok) return check;
  const exists = deps.existsSync ?? existsSync;
  const members = [];
  for (const name of SEAT_ACCOUNT_ALLOWLIST) {
    if (name === ".claude.json") continue;
    if (exists(join(accountHome, name))) members.push(name);
  }
  return { ok: true, members };
}
