import { readFileSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import { homeRel, shellHomePath } from "./home-path.mjs";
import { remoteTransport, shq } from "./remote-ssh.mjs";

const CLAUDE_MODULE_ROOT = join(dirname(fileURLToPath(import.meta.url)), "..");

export function sliceDescriptor({ unit, label, show }) {
  return Object.freeze({
    unit,
    label,
    show: Object.freeze({ ...show }),
    srcRel: homeRel(".claude", "systemd", "user", unit),
    dstRel: homeRel(".config", "systemd", "user", unit),
    repoPath: join(CLAUDE_MODULE_ROOT, "systemd", "user", unit),
  });
}

export function sliceUnitBody(descriptor, path = descriptor.repoPath) {
  return readFileSync(path, "utf8").trimEnd();
}

export function buildSliceVerifyLines(descriptor, { exitOnFail = true } = {}) {
  const src = shellHomePath(descriptor.srcRel);
  const dst = shellHomePath(descriptor.dstRel);
  const unit = shq(descriptor.unit);
  const label = descriptor.label;
  const fail = (code) => (exitOnFail ? `{ echo ${code}; exit 1; }` : `{ echo ${code}; return 1; }`);
  const lines = [
    `src=${src}`,
    `dst=${dst}`,
    `unit=${unit}`,
    "[ -f \"$src\" ] || " + fail(`${label}-src-missing`),
    "[ -L \"$dst\" ] || " + fail(`${label}-not-linked`),
    "src_abs=$(readlink -f \"$src\")",
    "dst_abs=$(readlink -f \"$dst\")",
    "[ \"$dst_abs\" = \"$src_abs\" ] || " + fail(`${label}-link-drift`),
  ];
  for (const [prop, value] of Object.entries(descriptor.show)) {
    lines.push(
      `[ \"$(systemctl --user show \"$unit\" -p ${prop} --value)\" = ${shq(value)} ] || ` +
        fail(`${label}-readback:${prop}`),
    );
  }
  return lines;
}

export function buildSliceApplyScript(descriptor) {
  const src = shellHomePath(descriptor.srcRel);
  const srcDir = shellHomePath(homeRel(".claude", "systemd", "user"));
  const dst = shellHomePath(descriptor.dstRel);
  const dstDir = shellHomePath(homeRel(".config", "systemd", "user"));
  const unit = shq(descriptor.unit);
  const label = descriptor.label;
  const body = Buffer.from(`${sliceUnitBody(descriptor)}\n`, "utf8").toString("base64");
  const verify = buildSliceVerifyLines(descriptor, { exitOnFail: false }).slice(3);
  return [
    "set -euo pipefail",
    `src=${src}`,
    `dst=${dst}`,
    `unit=${unit}`,
    `mkdir -p ${srcDir}`,
    `printf %s ${shq(body)} | base64 -d > \"$src.tmp\" || { echo ${label}-src-write; exit 1; }`,
    "if ! cmp -s \"$src.tmp\" \"$src\" 2>/dev/null; then",
    "  mv \"$src.tmp\" \"$src\"",
    "else",
    "  rm -f \"$src.tmp\"",
    "fi",
    `[ -f \"$src\" ] || { echo ${label}-src-missing; exit 1; }`,
    "src_abs=$(readlink -f \"$src\")",
    "if [ -e \"$dst\" ] && [ ! -L \"$dst\" ]; then",
    `  echo ${label}-collision; exit 1`,
    "fi",
    "if [ -L \"$dst\" ]; then",
    "  if ! dst_abs=$(readlink -f \"$dst\" 2>/dev/null); then",
    `    echo ${label}-collision; exit 1`,
    "  fi",
    "  if [ \"$dst_abs\" != \"$src_abs\" ]; then",
    `    echo ${label}-collision; exit 1`,
    "  fi",
    "fi",
    "changed=0",
    "if [ ! -L \"$dst\" ]; then",
    `  mkdir -p ${dstDir}`,
    "  ln -sfnT \"$src\" \"$dst\"",
    "  changed=1",
    "fi",
    "verify_readback() {",
    ...verify.map((line) => `  ${line}`),
    "}",
    "if [ \"$changed\" -eq 1 ]; then",
    `  systemctl --user daemon-reload || { echo ${label}-reload; exit 1; }`,
    "fi",
    "if verify_readback; then",
    `  echo ${label}-ok`,
    "  exit 0",
    "fi",
    `systemctl --user daemon-reload || { echo ${label}-reload; exit 1; }`,
    `verify_readback || { echo ${label}-readback; exit 1; }`,
    `echo ${label}-ok`,
  ].join("\n");
}

export function applySliceHost(descriptor, cfg, deps = {}) {
  const result = remoteTransport(cfg, buildSliceApplyScript(descriptor), { timeoutSec: 120 }, deps);
  if (result.status !== 0) {
    const detail = String(result.stdout || result.stderr || "").trim().split("\n").pop();
    return { ok: false, host: cfg.host, reason: `${descriptor.label}-failed`, detail, result };
  }
  return { ok: true, host: cfg.host };
}

export function applySliceAllHosts(descriptor, cfg, opts = {}, deps = {}) {
  const hosts = opts.hosts ?? cfg.hosts ?? [];
  const results = [];
  for (const host of hosts) {
    const result = applySliceHost(descriptor, { ...cfg, host }, deps);
    results.push(result);
    if (!result.ok) return { ok: false, results };
  }
  return { ok: true, results };
}
