import { readFileSync, statSync } from "node:fs";
import { hostname } from "node:os";
import { SEAT_NETNS_DIR, SEAT_UNIT_PREFIX, SEAT_UNIT_SUFFIX } from "./seat-constants.mjs";
import { isSafeSeatId } from "./seat-identity.mjs";

export function seatHostMatchesLocal(seatHost, deps = {}) {
  const local = deps.hostname?.() ?? hostname();
  return seatHost === local || seatHost === local.split(".")[0];
}

export function seatScopeUnit(seatId) {
  return `${SEAT_UNIT_PREFIX}${seatId}${SEAT_UNIT_SUFFIX}`;
}

export function seatNetnsPath(seatId) {
  return `${SEAT_NETNS_DIR}/overdeck-seat-${seatId}`;
}

export function netnsStatIdentity(path, statFn = statSync) {
  const st = statFn(path);
  return `${st.dev}:${st.ino}`;
}

function defaultReaders(deps = {}) {
  return {
    readProcSelfCgroup: deps.readProcSelfCgroup ?? (() => readFileSync("/proc/self/cgroup", "utf8")),
    readProcSelfNetNs: deps.readProcSelfNetNs ?? (() => netnsStatIdentity("/proc/self/ns/net")),
    readNetnsInode: deps.readNetnsInode ?? ((path) => netnsStatIdentity(path)),
    statNetns: deps.statNetns ?? ((path) => statSync(path)),
    hostname: deps.hostname ?? hostname,
  };
}

export function attestSeatProcess(env = process.env, deps = {}) {
  const seatId = String(env.OVERDECK_SEAT_ID || "").trim();
  const seatHost = String(env.OVERDECK_SEAT_HOST || "").trim();
  const { readProcSelfCgroup, readProcSelfNetNs, readNetnsInode, statNetns, hostname: hostFn } = defaultReaders(deps);

  if (!seatHost) {
    return { ok: false, reason: "seat-attestation-failed", error: "seat-attest: missing OVERDECK_SEAT_HOST" };
  }
  if (!seatId || !isSafeSeatId(seatId)) {
    return { ok: false, reason: "seat-attestation-failed", error: "seat-attest: invalid OVERDECK_SEAT_ID" };
  }
  if (!seatHostMatchesLocal(seatHost, { hostname: hostFn })) {
    return { ok: false, reason: "seat-host-mismatch", error: `seat-remote: OVERDECK_SEAT_HOST mismatch (${seatHost})` };
  }

  const scopeUnit = seatScopeUnit(seatId);
  let cgroup;
  try {
    cgroup = readProcSelfCgroup();
  } catch (err) {
    return { ok: false, reason: "seat-attestation-failed", error: `seat-attest: cgroup unreadable (${err.message})` };
  }
  if (!cgroup.includes(scopeUnit)) {
    return { ok: false, reason: "seat-attestation-failed", error: `seat-attest: scope cgroup mismatch (want ${scopeUnit})` };
  }

  const netnsPath = seatNetnsPath(seatId);
  let netnsStat;
  try {
    netnsStat = statNetns(netnsPath);
  } catch (err) {
    return { ok: false, reason: "seat-attestation-failed", error: `seat-attest: netns path missing (${netnsPath})` };
  }
  if (netnsStat.uid !== 0) {
    return { ok: false, reason: "seat-attestation-failed", error: `seat-attest: netns owner mismatch (${netnsPath})` };
  }

  let selfInode;
  let wantInode;
  try {
    selfInode = readProcSelfNetNs();
    wantInode = readNetnsInode(netnsPath);
  } catch (err) {
    return { ok: false, reason: "seat-attestation-failed", error: `seat-attest: netns inode unreadable (${err.message})` };
  }
  if (selfInode !== wantInode) {
    return { ok: false, reason: "seat-attestation-failed", error: "seat-attest: netns inode mismatch" };
  }

  return { ok: true, seatId, seatHost, scopeUnit, netnsPath };
}

export function shouldSkipRemoteForSeatHost(env = process.env, deps = {}) {
  const seatHost = env.OVERDECK_SEAT_HOST;
  if (!seatHost) return { skip: false };
  const attestation = attestSeatProcess(env, deps);
  if (!attestation.ok) {
    return { skip: false, error: attestation.error, reason: attestation.reason };
  }
  return { skip: true, reason: "seat-local-host" };
}

export function isAttestedSeatProcess(env = process.env, deps = {}) {
  return attestSeatProcess(env, deps).ok;
}
