import {
  closeSync,
  existsSync,
  fstatSync,
  lstatSync,
  mkdirSync,
  mkdtempSync,
  openSync,
  readFileSync,
  readdirSync,
  rmSync,
  writeFileSync,
} from "node:fs";
import { constants } from "node:fs";
import { tmpdir } from "node:os";
import { join, resolve } from "node:path";
import { spawnSync } from "node:child_process";
import { seatCheckoutShellPath, seatProfileShellPath, shellPathAssign } from "./home-path.mjs";
import { buildStdinBlobReceiveScript } from "./seat-transfer.mjs";

const O_NOFOLLOW = constants.O_NOFOLLOW ?? 0x10000;
const ENCODED_DIR_RE = /^[A-Za-z0-9-]+$/;

export const RESUME_ENCODE_PY = `import re, sys
p = sys.argv[1]
e = re.sub(r"[^A-Za-z0-9]", "-", p)
if not re.fullmatch(r"[A-Za-z0-9-]+", e):
    sys.exit(2)
print(e)
`;

export function parseResumeSessionId(argv) {
  if (!argv?.length) return null;
  if (argv[0] === "resume" && argv.length >= 2 && !argv[1].startsWith("-")) return argv[1];
  if (argv[0] === "--resume" && argv.length >= 2) return argv[1];
  return null;
}

export function encodeClaudeProjectDir(absPath) {
  const resolved = resolve(absPath);
  const encoded = resolved.replace(/[^A-Za-z0-9]/g, "-");
  if (!ENCODED_DIR_RE.test(encoded)) {
    throw new Error(`seat-resume: project dir encoding failed (${resolved})`);
  }
  return encoded;
}

function findResumeTranscriptPaths(laptopAccountHome, sessionId, deps = {}) {
  const exists = deps.existsSync ?? existsSync;
  const readdir = deps.readdirSync ?? readdirSync;
  const projectsDir = join(laptopAccountHome, "projects");
  if (!exists(projectsDir)) return [];
  const matches = [];
  for (const entry of readdir(projectsDir)) {
    const transcriptPath = join(projectsDir, entry, `${sessionId}.jsonl`);
    if (exists(transcriptPath)) matches.push(transcriptPath);
  }
  return matches;
}

function readRegularFileViaDescriptor(path, deps = {}) {
  const lstat = deps.lstatSync ?? lstatSync;
  const lst = lstat(path);
  if (lst.isSymbolicLink()) return { ok: false, reason: "symlink" };
  const fd = openSync(path, constants.O_RDONLY | O_NOFOLLOW);
  try {
    const st = fstatSync(fd);
    if (!st.isFile()) return { ok: false, reason: "not-regular-file" };
    return { ok: true, content: readFileSync(fd) };
  } finally {
    closeSync(fd);
  }
}

export function resolveResumeTranscript(laptopAccountHome, sessionId, deps = {}) {
  const matches = findResumeTranscriptPaths(laptopAccountHome, sessionId, deps);
  if (matches.length === 0) return { ok: false, reason: "not-found" };
  if (matches.length > 1) return { ok: false, reason: "ambiguous" };
  const read = readRegularFileViaDescriptor(matches[0], deps);
  if (!read.ok) return read;
  return { ok: true, content: read.content };
}

export function collectResumeTaskFiles(laptopAccountHome, sessionId, deps = {}) {
  const exists = deps.existsSync ?? existsSync;
  const lstat = deps.lstatSync ?? lstatSync;
  const readdir = deps.readdirSync ?? readdirSync;
  const tasksDir = join(laptopAccountHome, "tasks", sessionId);
  if (!exists(tasksDir)) return { ok: true, files: [] };
  const st = lstat(tasksDir);
  if (st.isSymbolicLink()) return { ok: false, reason: "tasks-symlink" };
  if (!st.isDirectory()) return { ok: false, reason: "tasks-not-dir" };
  const files = [];
  for (const name of readdir(tasksDir)) {
    if (name.includes("/")) return { ok: false, reason: "task-name-invalid" };
    const path = join(tasksDir, name);
    const lst = lstat(path);
    if (lst.isSymbolicLink()) return { ok: false, reason: "task-symlink" };
    if (lst.isDirectory()) continue;
    if (!lst.isFile()) return { ok: false, reason: "task-not-regular-file" };
    const read = readRegularFileViaDescriptor(path, deps);
    if (!read.ok) return read;
    files.push({ name, content: read.content });
  }
  return { ok: true, files };
}

export function buildResumeTransfer(laptopAccountHome, sessionId, deps = {}) {
  const transcript = resolveResumeTranscript(laptopAccountHome, sessionId, deps);
  if (!transcript.ok) {
    if (transcript.reason === "not-found") {
      return { ok: false, message: `seat-remote: resume transcript not found (${sessionId})` };
    }
    if (transcript.reason === "ambiguous") {
      return { ok: false, message: `seat-remote: resume transcript ambiguous (${sessionId})` };
    }
    return { ok: false, message: `seat-remote: resume transcript rejected (${transcript.reason})` };
  }
  const tasks = collectResumeTaskFiles(laptopAccountHome, sessionId, deps);
  if (!tasks.ok) {
    return { ok: false, message: `seat-remote: resume tasks rejected (${tasks.reason})` };
  }
  const blob = buildResumeTransferBlob(transcript.content, tasks.files, deps);
  if (!blob) return { ok: false, message: "seat-remote: resume transfer tar failed" };
  return { ok: true, blob, sessionId };
}

export function buildResumeTransferBlob(transcriptContent, taskFiles, deps = {}) {
  const spawn = deps.spawnSync ?? spawnSync;
  const tmpDir = mkdtempSync(join(tmpdir(), "seat-resume-"));
  try {
    writeFileSync(join(tmpDir, "resume-transcript"), transcriptContent);
    const entries = ["resume-transcript"];
    if (taskFiles.length) {
      const tasksDir = join(tmpDir, "resume-tasks");
      mkdirSync(tasksDir, { recursive: true });
      for (const file of taskFiles) {
        writeFileSync(join(tasksDir, file.name), file.content);
      }
      entries.push("resume-tasks");
    }
    const result = spawn("tar", ["-czf", "-", "-C", tmpDir, ...entries], {
      encoding: "buffer",
      maxBuffer: 64 * 1024 * 1024,
    });
    if (result.status !== 0) return null;
    return result.stdout;
  } finally {
    rmSync(tmpDir, { recursive: true, force: true });
  }
}

export function buildResumeRestoreFromStdin(identity, sessionId) {
  const profile = seatProfileShellPath(identity.accountSlug, identity.seatId);
  const checkout = seatCheckoutShellPath(identity.seatId);
  return buildStdinBlobReceiveScript([
    "set -euo pipefail",
    shellPathAssign("profile", profile),
    shellPathAssign("checkout", checkout),
    '[ -d "$profile" ] || { echo resume-profile-missing; exit 1; }',
    'checkout_real=$(cd "$checkout" && pwd -P)',
    "encoded=$(python3 - \"$checkout_real\" <<'PY'",
    RESUME_ENCODE_PY.trim(),
    "PY",
    ")",
    "staging=$(mktemp -d)",
    'trap \'rm -rf "$staging"\' EXIT',
    'tar -xzf "$tmp" -C "$staging"',
    '[ -f "$staging/resume-transcript" ] || { echo resume-transcript-missing; exit 1; }',
    'proj_dir="$profile/projects/$encoded"',
    'mkdir -p "$proj_dir"',
    'chmod 700 "$proj_dir"',
    `install -m 600 "$staging/resume-transcript" "$proj_dir/${sessionId}.jsonl"`,
    'if [ -d "$staging/resume-tasks" ]; then',
    `  task_dir="$profile/tasks/${sessionId}"`,
    '  mkdir -p "$task_dir"',
    '  chmod 700 "$task_dir"',
    '  for f in "$staging/resume-tasks"/*; do',
    '    [ -f "$f" ] || continue',
    '    base=$(basename "$f")',
    '    case "$base" in */*) echo "resume-task-nested:$base"; exit 1 ;; esac',
    '    install -m 600 "$f" "$task_dir/$base"',
    '  done',
    "fi",
  ]);
}

export function resumeTranscriptDestPath(profileDir, checkoutPath, sessionId) {
  const encoded = encodeClaudeProjectDir(checkoutPath);
  return join(profileDir, "projects", encoded, `${sessionId}.jsonl`);
}
