#!/usr/bin/env node

import {
  createHash,
  createPublicKey,
  timingSafeEqual,
  verify as cryptoVerify,
} from "node:crypto";
import { readFile, writeFile } from "node:fs/promises";
import { basename, resolve } from "node:path";
import { pathToFileURL } from "node:url";

const REGISTRY_VERSION = "hummint-signing-key-registry-v1";
const SNAPSHOT_VERSION = "hummint-key-registry-snapshot-v1";
const ROOT_VERSION = "hummint-registry-root-v1";
const ROOT_SNAPSHOT_VERSION = "hummint-root-trust-snapshot-v1";
const ROOT_SIGNATURE_VERSION = "hummint-registry-signature-v1";
const SHA256 = /^[a-f0-9]{64}$/;
const KEY_HINT = /^hummint-[a-f0-9]{12}$/;
const ROOT_KEY_HINT = /^hummint-root-[a-f0-9]{16}$/;
const MAX_BUNDLE_BYTES = 1_000_000;
const MAX_REGISTRY_BYTES = 500_000;
const MAX_ROOT_SIGNATURE_AGE_MS = 10 * 60 * 1000;
const MAX_ROOT_SIGNATURE_FUTURE_SKEW_MS = 5 * 60 * 1000;

export function stableStringify(value) {
  return JSON.stringify(sortJson(value));
}

export function validateOfflineRootKey(value) {
  const snapshot =
    isRecord(value) && value.format === ROOT_SNAPSHOT_VERSION ? value : null;
  const rootValue = snapshot ? snapshot.root_key : value;
  if (
    !isRecord(rootValue) ||
    rootValue.version !== ROOT_VERSION ||
    rootValue.algorithm !== "Ed25519" ||
    typeof rootValue.key_hint !== "string" ||
    !ROOT_KEY_HINT.test(rootValue.key_hint) ||
    typeof rootValue.public_key_pem !== "string"
  ) {
    return invalid("invalid_root_key", "Trust anchor root non valido.");
  }

  let publicKey;
  try {
    publicKey = createPublicKey(rootValue.public_key_pem);
  } catch {
    return invalid("invalid_root_key", "Chiave pubblica root PEM non valida.");
  }
  if (publicKey.asymmetricKeyType !== "ed25519") {
    return invalid("invalid_root_key", "Tipo di chiave root non supportato.");
  }
  const der = publicKey.export({ format: "der", type: "spki" });
  const expectedHint = `hummint-root-${createHash("sha256")
    .update(der)
    .digest("hex")
    .slice(0, 16)}`;
  if (rootValue.key_hint !== expectedHint) {
    return invalid(
      "root_key_fingerprint_mismatch",
      "Fingerprint e chiave root non coincidono."
    );
  }

  const rootKey = {
    version: ROOT_VERSION,
    algorithm: "Ed25519",
    key_hint: rootValue.key_hint,
    public_key_pem: publicKey
      .export({ format: "pem", type: "spki" })
      .toString(),
  };
  const rootKeySha256 = sha256Hex(stableStringify(rootKey));
  if (
    snapshot &&
    (
      typeof snapshot.root_key_sha256 !== "string" ||
      !safeEqualHex(snapshot.root_key_sha256, rootKeySha256)
    )
  ) {
    return invalid(
      "root_snapshot_modified",
      "Lo snapshot della root non corrisponde al proprio hash."
    );
  }

  return {
    ok: true,
    root_key: rootKey,
    root_key_sha256: rootKeySha256,
  };
}

export function createRootTrustSnapshot(
  rootValue,
  sourceUrl,
  retrievedAt = new Date().toISOString()
) {
  const validation = validateOfflineRootKey(rootValue);
  if (!validation.ok) throw new Error(validation.reason);
  if (!isHttpsUrl(sourceUrl) || !validDate(retrievedAt)) {
    throw new Error("Origine o data del trust anchor non valide");
  }
  return {
    format: ROOT_SNAPSHOT_VERSION,
    source_url: sourceUrl,
    retrieved_at: retrievedAt,
    root_key_sha256: validation.root_key_sha256,
    root_key: validation.root_key,
  };
}

export function validateOfflineRegistry(value, options = {}) {
  const snapshot = isRecord(value) && value.format === SNAPSHOT_VERSION
    ? value
    : null;
  const registryValue = snapshot ? snapshot.registry : value;
  const trustValue = snapshot
    ? recordOrNull(snapshot.registry_trust)
    : recordOrNull(value?.trust);
  if (
    !isRecord(registryValue) ||
    registryValue.version !== REGISTRY_VERSION ||
    typeof registryValue.active_key_hint !== "string" ||
    !Array.isArray(registryValue.keys) ||
    registryValue.keys.length < 1 ||
    registryValue.keys.length > 33
  ) {
    return invalid("invalid_registry", "Registro delle chiavi non valido.");
  }

  const seen = new Set();
  const keys = [];
  for (const valueKey of registryValue.keys) {
    const normalized = normalizeRegistryKey(valueKey);
    if (!normalized.ok) return normalized;
    if (seen.has(normalized.key.key_hint)) {
      return invalid("duplicate_registry_key", "Il registro contiene una chiave duplicata.");
    }
    seen.add(normalized.key.key_hint);
    keys.push(normalized.key);
  }

  const activeKeys = keys.filter((key) => key.status === "active");
  if (
    activeKeys.length !== 1 ||
    activeKeys[0].key_hint !== registryValue.active_key_hint
  ) {
    return invalid(
      "invalid_active_key",
      "Il registro deve contenere una sola chiave attiva."
    );
  }

  const registry = {
    version: REGISTRY_VERSION,
    active_key_hint: registryValue.active_key_hint,
    keys,
  };
  const registrySha256 = sha256Hex(stableStringify(registry));
  if (
    snapshot &&
    (
      typeof snapshot.registry_sha256 !== "string" ||
      !safeEqualHex(snapshot.registry_sha256, registrySha256)
    )
  ) {
    return invalid(
      "registry_snapshot_modified",
      "Lo snapshot del registro non corrisponde al proprio hash."
    );
  }

  const registrySignature =
    trustValue?.status === "root_signed" && isRecord(trustValue.signature)
      ? trustValue.signature
      : null;
  let rootSignatureValid = null;
  let rootKeyHint = null;
  let rootSignatureSignedAt = null;
  if (options.rootKey) {
    const rootValidation = validateOfflineRootKey(options.rootKey);
    if (!rootValidation.ok) return rootValidation;
    rootKeyHint = rootValidation.root_key.key_hint;
    if (!registrySignature) {
      return invalid(
        "registry_root_signature_missing",
        "Il registro non contiene una firma root."
      );
    }
    const rootSignatureValidation = verifyRegistryRootSignature(
      registry,
      registrySignature,
      rootValidation.root_key
    );
    if (!rootSignatureValidation.ok) return rootSignatureValidation;
    rootSignatureValid = true;
    rootSignatureSignedAt = rootSignatureValidation.signed_at;
  } else if (options.requireRootSignature) {
    return invalid(
      "root_key_required",
      "La verifica root richiede un trust anchor locale."
    );
  }

  return {
    ok: true,
    registry,
    registry_sha256: registrySha256,
    registry_signature: registrySignature,
    root_signature_valid: rootSignatureValid,
    root_signature_signed_at: rootSignatureSignedAt,
    root_key_hint: rootKeyHint,
    snapshot: snapshot
      ? {
          source_url: stringOrNull(snapshot.source_url),
          retrieved_at: stringOrNull(snapshot.retrieved_at),
        }
      : null,
  };
}

export function createRegistrySnapshot(
  registryValue,
  sourceUrl,
  retrievedAt = new Date().toISOString(),
  options = {}
) {
  const validation = validateOfflineRegistry(registryValue, options);
  if (!validation.ok) {
    throw new Error(validation.reason);
  }
  if (!isHttpsUrl(sourceUrl) || !validDate(retrievedAt)) {
    throw new Error("Origine o data dello snapshot non valide");
  }
  if (validation.root_signature_valid) {
    const signatureTime = Date.parse(validation.root_signature_signed_at);
    const retrievedTime = Date.parse(retrievedAt);
    if (
      signatureTime < retrievedTime - MAX_ROOT_SIGNATURE_AGE_MS ||
      signatureTime > retrievedTime + MAX_ROOT_SIGNATURE_FUTURE_SKEW_MS
    ) {
      throw new Error(
        "La firma root del registro e troppo distante dall'ora di acquisizione"
      );
    }
  }

  return {
    format: SNAPSHOT_VERSION,
    source_url: sourceUrl,
    retrieved_at: retrievedAt,
    registry_sha256: validation.registry_sha256,
    registry: validation.registry,
    registry_trust: validation.registry_signature
      ? {
          status: "root_signed",
          signature: validation.registry_signature,
        }
      : {
          status: "unsigned",
          signature: null,
        },
  };
}

export function verifyOfflineBundle(candidate, registryValue, options = {}) {
  if (!isRecord(candidate)) {
    return invalid("invalid_bundle", "Bundle JSON non valido.");
  }

  const registryValidation = validateOfflineRegistry(registryValue, options);
  if (!registryValidation.ok) return registryValidation;

  const signature = candidate.signature;
  if (
    !isRecord(signature) ||
    signature.algorithm !== "Ed25519" ||
    signature.canonicalization !== "stable-json-v1" ||
    !validDate(signature.signed_at) ||
    typeof signature.key_hint !== "string" ||
    !KEY_HINT.test(signature.key_hint) ||
    typeof signature.payload_sha256 !== "string" ||
    !SHA256.test(signature.payload_sha256) ||
    typeof signature.signature !== "string" ||
    !/^[A-Za-z0-9+/]{86}==$/.test(signature.signature)
  ) {
    return invalid("invalid_signature_metadata", "Metadati della firma non validi.");
  }

  const payload = { ...candidate };
  delete payload.signature;
  const expectedPayloadSha256 = sha256Hex(stableStringify(payload));
  if (!safeEqualHex(signature.payload_sha256, expectedPayloadSha256)) {
    return {
      ...invalid(
        "payload_hash_mismatch",
        "Payload modificato: l'hash firmato non corrisponde."
      ),
      payload_sha256: signature.payload_sha256,
      expected_payload_sha256: expectedPayloadSha256,
    };
  }

  const verificationKey = registryValidation.registry.keys.find(
    (key) => key.key_hint === signature.key_hint
  );
  if (!verificationKey) {
    return invalid("unknown_signing_key", "Chiave di firma sconosciuta.");
  }
  if (verificationKey.status === "revoked") {
    return {
      ...invalid("revoked_signing_key", "Chiave di firma revocata."),
      key_hint: verificationKey.key_hint,
      key_status: verificationKey.status,
    };
  }

  const signedAt = Date.parse(signature.signed_at);
  if (
    (verificationKey.valid_from &&
      signedAt < Date.parse(verificationKey.valid_from)) ||
    (verificationKey.valid_until &&
      signedAt > Date.parse(verificationKey.valid_until))
  ) {
    return {
      ...invalid(
        "signature_outside_key_window",
        "Firma emessa fuori dal periodo di validita della chiave."
      ),
      key_hint: verificationKey.key_hint,
      key_status: verificationKey.status,
    };
  }

  const signatureMetadata = {
    algorithm: signature.algorithm,
    canonicalization: signature.canonicalization,
    signed_at: signature.signed_at,
    key_hint: signature.key_hint,
    payload_sha256: signature.payload_sha256,
  };
  const signedPayload = stableStringify({
    payload,
    signature: signatureMetadata,
  });
  let signatureValid = false;
  try {
    signatureValid = cryptoVerify(
      null,
      Buffer.from(signedPayload),
      createPublicKey(verificationKey.public_key_pem),
      Buffer.from(signature.signature, "base64")
    );
  } catch {
    signatureValid = false;
  }
  if (!signatureValid) {
    return invalid("invalid_signature", "Firma Ed25519 non valida.");
  }

  const semantics = validateKnownBundle(payload);
  if (!semantics.ok) {
    return {
      ...semantics,
      signature_valid: true,
      payload_sha256: expectedPayloadSha256,
      key_hint: verificationKey.key_hint,
      key_status: verificationKey.status,
      registry_sha256: registryValidation.registry_sha256,
    };
  }

  return {
    ok: true,
    signature_valid: true,
    semantic_valid: true,
    format: payload.version,
    payload_sha256: expectedPayloadSha256,
    key_hint: verificationKey.key_hint,
    key_status: verificationKey.status,
    signed_at: signature.signed_at,
    registry_sha256: registryValidation.registry_sha256,
    registry_root_signature_valid:
      registryValidation.root_signature_valid === true,
    root_key_hint: registryValidation.root_key_hint,
    checks: [
      "payload_hash",
      "ed25519_signature",
      "key_registry",
      ...(registryValidation.root_signature_valid
        ? ["registry_root_signature"]
        : []),
      ...semantics.checks,
    ],
  };
}

function validateKnownBundle(value) {
  if (value.version === "hummint-proof-bundle-v2") {
    return validateStrongBundle(value);
  }
  if (value.version === "hummint-companion-media-bundle-v1") {
    return validateMediaBundle(value);
  }
  return invalid(
    "unsupported_bundle_format",
    "Formato proof pack Hummint non supportato."
  );
}

function validateStrongBundle(value) {
  const subject = recordOrNull(value.subject);
  const certificate = recordOrNull(value.certificate);
  const evidence = recordOrNull(value.evidence);
  const privacy = recordOrNull(value.privacy);
  if (!subject || !certificate || !evidence || !privacy) {
    return invalid("missing_strong_sections", "Sezioni del proof pack Forte mancanti.");
  }

  const exportedAt = stringOrNull(value.exported_at);
  const generatedAt = stringOrNull(subject.certificate_generated_at);
  const processHash = stringOrNull(certificate.process_hash);
  const publicSlug = stringOrNull(certificate.public_slug);
  const contentHash = nullableString(certificate.content_hash);
  const checkpointHead = nullableString(certificate.checkpoint_head);
  const sessions = integerOrNull(certificate.sessions);
  const activeMinutes = integerOrNull(certificate.active_minutes);
  const checkpoints = integerOrNull(certificate.checkpoints);
  if (
    !validDate(exportedAt) ||
    !validDate(generatedAt) ||
    Date.parse(generatedAt) > Date.parse(exportedAt) ||
    !processHash ||
    !SHA256.test(processHash) ||
    !publicSlug ||
    !/^[A-Za-z0-9_-]{1,128}$/.test(publicSlug) ||
    !validNullableSha256(contentHash) ||
    !validNullableSha256(checkpointHead) ||
    !validNonNegativeInteger(sessions) ||
    !validNonNegativeInteger(activeMinutes) ||
    !validNonNegativeInteger(checkpoints) ||
    value.bundle_id !== `hummint-${publicSlug}-${processHash.slice(0, 12)}` ||
    (checkpoints === 0) !== (checkpointHead === null)
  ) {
    return invalid(
      "strong_metadata_mismatch",
      "Metadati, hash o contatori del proof pack Forte incoerenti."
    );
  }

  if (
    privacy.intermediate_drafts_included !== false ||
    privacy.pasted_content_included !== false ||
    privacy.final_text_included !== false
  ) {
    return invalid("strong_scope_mismatch", "Perimetro privacy Forte incoerente.");
  }

  const summary = recordOrNull(evidence.summary_v2);
  const growth = Array.isArray(evidence.growth_curve)
    ? evidence.growth_curve
    : null;
  if (!growth || growth.length > 40) {
    return invalid("strong_growth_mismatch", "Curva di crescita Forte non valida.");
  }
  let previousTime = 0;
  for (const point of growth) {
    if (
      !isRecord(point) ||
      !validDate(point.t) ||
      !validNonNegativeInteger(point.c) ||
      Date.parse(point.t) < previousTime ||
      Date.parse(point.t) > Date.parse(generatedAt)
    ) {
      return invalid("strong_growth_mismatch", "Curva di crescita Forte incoerente.");
    }
    previousTime = Date.parse(point.t);
  }

  if (summary) {
    const activity = recordOrNull(summary.activity);
    const process = recordOrNull(summary.process);
    const coverage = recordOrNull(summary.coverage);
    const imports = recordOrNull(summary.imports);
    if (
      summary.version !== "hummint-evidence-v2" ||
      !activity ||
      !process ||
      !coverage ||
      !imports ||
      integerOrNull(activity.sessions) !== sessions ||
      !validNonNegativeInteger(activity.active_seconds) ||
      Math.round(activity.active_seconds / 60) !== activeMinutes ||
      integerOrNull(process.checkpoints) !== checkpoints ||
      coverage.level !== evidence.level
    ) {
      return invalid(
        "strong_summary_mismatch",
        "Evidence summary e certificato Forte non coincidono."
      );
    }

    if (growth.length > 0) {
      if (
        process.first_character_count !== growth[0].c ||
        process.final_character_count !== growth[growth.length - 1].c
      ) {
        return invalid(
          "strong_process_mismatch",
          "Crescita e riepilogo del processo Forte non coincidono."
        );
      }
    }

    const disclosure = evidence.ai_disclosure;
    const observedEvents = integerOrNull(imports.observed_events);
    const importedCharacters = integerOrNull(imports.imported_characters);
    const undeclaredEvents = integerOrNull(imports.undeclared_events);
    if (
      !validNonNegativeInteger(observedEvents) ||
      !validNonNegativeInteger(importedCharacters) ||
      !validNonNegativeInteger(undeclaredEvents) ||
      (disclosure === null &&
        (observedEvents !== 0 ||
          importedCharacters !== 0 ||
          undeclaredEvents !== 0)) ||
      (isRecord(disclosure) &&
        (disclosure.total_import_events !== observedEvents ||
          disclosure.imported_characters !== importedCharacters ||
          disclosure.undeclared_events !== undeclaredEvents))
    ) {
      return invalid(
        "strong_disclosure_mismatch",
        "Disclosure e riepilogo import Forte non coincidono."
      );
    }
  }

  return {
    ok: true,
    checks: [
      "strong_metadata",
      "strong_evidence_summary",
      "strong_growth_and_disclosure",
    ],
  };
}

function validateMediaBundle(value) {
  const subject = recordOrNull(value.subject);
  const proof = recordOrNull(value.proof);
  const proofChain = recordOrNull(proof?.chain);
  const receipt = recordOrNull(proof?.receipt);
  const receiptChain = recordOrNull(receipt?.chain);
  const session = recordOrNull(receipt?.session);
  const page = recordOrNull(session?.page);
  const stats = recordOrNull(session?.stats);
  const association = recordOrNull(value.association);
  const evidence = recordOrNull(value.evidence);
  if (
    !subject ||
    !proof ||
    !proofChain ||
    !receipt ||
    !receiptChain ||
    !session ||
    !page ||
    !stats ||
    !association ||
    !evidence
  ) {
    return invalid("missing_media_sections", "Sezioni del proof pack Media mancanti.");
  }

  const sessionId = stringOrNull(session.id);
  const receiptSha256 = stringOrNull(proof.receipt_sha256);
  if (
    receipt.format !== "hummint-companion-proof" ||
    receipt.version !== 2 ||
    !sessionId ||
    subject.session_id !== sessionId ||
    subject.source_site !== page.site ||
    subject.started_at !== session.startedAt ||
    !receiptSha256 ||
    !SHA256.test(receiptSha256) ||
    sha256Hex(stableStringify(receipt)) !== receiptSha256
  ) {
    return invalid("media_receipt_mismatch", "Ricevuta e metadati Media non coincidono.");
  }

  if (!Array.isArray(session.checkpoints) || session.checkpoints.length > 500) {
    return invalid("media_checkpoint_mismatch", "Checkpoint Media non validi.");
  }
  let previousHash = "genesis";
  for (let index = 0; index < session.checkpoints.length; index += 1) {
    const checkpoint = session.checkpoints[index];
    if (
      !isRecord(checkpoint) ||
      checkpoint.format !== "hummint-companion-checkpoint-v2" ||
      checkpoint.sessionId !== sessionId ||
      checkpoint.sequence !== index + 1 ||
      checkpoint.previousHash !== previousHash ||
      typeof checkpoint.hash !== "string" ||
      !SHA256.test(checkpoint.hash)
    ) {
      return invalid("media_checkpoint_mismatch", "Catena checkpoint Media discontinua.");
    }
    const checkpointPayload = { ...checkpoint };
    delete checkpointPayload.hash;
    if (sha256Hex(stableStringify(checkpointPayload)) !== checkpoint.hash) {
      return invalid("media_checkpoint_modified", "Un checkpoint Media e stato modificato.");
    }
    previousHash = checkpoint.hash;
  }

  if (
    receiptChain.valid !== true ||
    receiptChain.checkpointCount !== session.checkpoints.length ||
    receiptChain.finalHash !== previousHash ||
    proofChain.checkpoint_count !== session.checkpoints.length ||
    proofChain.final_hash !== previousHash ||
    value.bundle_id !==
      `hummint-media-${sessionId}-${receiptSha256.slice(0, 12)}`
  ) {
    return invalid("media_chain_mismatch", "Riepilogo e catena Media non coincidono.");
  }

  if (
    association.type !== "author-declared-final-text" ||
    typeof association.content_sha256 !== "string" ||
    !SHA256.test(association.content_sha256) ||
    !validNonNegativeInteger(association.character_count) ||
    association.character_count < 30 ||
    evidence.level !== "medium" ||
    evidence.source !== "browser-companion" ||
    evidence.typed_characters !== stats.typedCharacters ||
    evidence.pasted_characters !== stats.pastedCharacters ||
    evidence.active_seconds !== stats.activeSeconds ||
    evidence.checkpoints !== session.checkpoints.length
  ) {
    return invalid(
      "media_association_mismatch",
      "Associazione o riepilogo Media non coincidono con la ricevuta."
    );
  }

  return {
    ok: true,
    checks: [
      "media_receipt_hash",
      "media_checkpoint_chain",
      "media_association",
    ],
  };
}

function verifyRegistryRootSignature(registry, signature, rootKey) {
  if (
    !isRecord(signature) ||
    signature.version !== ROOT_SIGNATURE_VERSION ||
    signature.algorithm !== "Ed25519" ||
    signature.canonicalization !== "stable-json-v1" ||
    !validDate(signature.signed_at) ||
    signature.root_key_hint !== rootKey.key_hint ||
    typeof signature.registry_sha256 !== "string" ||
    !SHA256.test(signature.registry_sha256) ||
    typeof signature.signature !== "string" ||
    !/^[A-Za-z0-9+/]{86}==$/.test(signature.signature)
  ) {
    return invalid(
      "invalid_registry_root_signature",
      "Metadati della firma root del registro non validi."
    );
  }

  const registrySha256 = sha256Hex(stableStringify(registry));
  if (!safeEqualHex(signature.registry_sha256, registrySha256)) {
    return invalid(
      "registry_root_hash_mismatch",
      "Il registro non corrisponde all'hash firmato dalla root."
    );
  }

  const signatureMetadata = {
    version: signature.version,
    algorithm: signature.algorithm,
    canonicalization: signature.canonicalization,
    signed_at: signature.signed_at,
    root_key_hint: signature.root_key_hint,
    registry_sha256: signature.registry_sha256,
  };
  const signedPayload = stableStringify({
    registry,
    signature: signatureMetadata,
  });
  let valid = false;
  try {
    valid = cryptoVerify(
      null,
      Buffer.from(signedPayload),
      createPublicKey(rootKey.public_key_pem),
      Buffer.from(signature.signature, "base64")
    );
  } catch {
    valid = false;
  }
  return valid
    ? { ok: true, signed_at: signature.signed_at }
    : invalid(
        "invalid_registry_root_signature",
        "Firma root del registro non valida."
      );
}

function normalizeRegistryKey(value) {
  if (
    !isRecord(value) ||
    value.algorithm !== "Ed25519" ||
    typeof value.key_hint !== "string" ||
    !KEY_HINT.test(value.key_hint) ||
    typeof value.public_key_pem !== "string" ||
    !["active", "retired", "revoked"].includes(value.status)
  ) {
    return invalid("invalid_registry_key", "Chiave del registro non valida.");
  }

  let publicKey;
  try {
    publicKey = createPublicKey(value.public_key_pem);
  } catch {
    return invalid("invalid_registry_key", "Chiave pubblica PEM non valida.");
  }
  if (publicKey.asymmetricKeyType !== "ed25519") {
    return invalid("invalid_registry_key", "Tipo di chiave pubblica non supportato.");
  }
  const der = publicKey.export({ format: "der", type: "spki" });
  const expectedHint = `hummint-${createHash("sha256")
    .update(der)
    .digest("hex")
    .slice(0, 12)}`;
  if (value.key_hint !== expectedHint) {
    return invalid(
      "registry_key_fingerprint_mismatch",
      "Fingerprint e chiave pubblica non coincidono."
    );
  }

  const validFrom = optionalDate(value.valid_from);
  const validUntil = optionalDate(value.valid_until);
  const revokedAt = optionalDate(value.revoked_at);
  if (
    validFrom === null ||
    validUntil === null ||
    revokedAt === null ||
    (validFrom && validUntil && Date.parse(validFrom) > Date.parse(validUntil)) ||
    (validFrom && revokedAt && Date.parse(validFrom) > Date.parse(revokedAt)) ||
    (value.status === "active" && (validUntil || revokedAt)) ||
    (value.status === "retired" && (!validUntil || revokedAt)) ||
    (value.status === "revoked" && !revokedAt)
  ) {
    return invalid(
      "invalid_registry_key_timeline",
      "Stato e date della chiave non sono coerenti."
    );
  }

  return {
    ok: true,
    key: {
      algorithm: "Ed25519",
      key_hint: value.key_hint,
      public_key_pem: publicKey
        .export({ format: "pem", type: "spki" })
        .toString(),
      status: value.status,
      ...(validFrom ? { valid_from: validFrom } : {}),
      ...(validUntil ? { valid_until: validUntil } : {}),
      ...(revokedAt ? { revoked_at: revokedAt } : {}),
    },
  };
}

function sortJson(value) {
  if (Array.isArray(value)) return value.map(sortJson);
  if (value === null || typeof value !== "object") return value;
  return Object.keys(value)
    .sort()
    .reduce((result, key) => {
      if (typeof value[key] !== "undefined") {
        result[key] = sortJson(value[key]);
      }
      return result;
    }, {});
}

function sha256Hex(value) {
  return createHash("sha256").update(value).digest("hex");
}

function safeEqualHex(a, b) {
  if (
    typeof a !== "string" ||
    typeof b !== "string" ||
    a.length !== b.length ||
    !SHA256.test(a) ||
    !SHA256.test(b)
  ) {
    return false;
  }
  return timingSafeEqual(Buffer.from(a, "hex"), Buffer.from(b, "hex"));
}

function optionalDate(value) {
  if (typeof value === "undefined") return undefined;
  return validDate(value) ? value : null;
}

function validDate(value) {
  return (
    typeof value === "string" &&
    value.length <= 40 &&
    Number.isFinite(Date.parse(value))
  );
}

function validNonNegativeInteger(value) {
  return (
    typeof value === "number" &&
    Number.isSafeInteger(value) &&
    value >= 0
  );
}

function validNullableSha256(value) {
  return value === null || (typeof value === "string" && SHA256.test(value));
}

function nullableString(value) {
  return value === null || typeof value === "string" ? value : null;
}

function integerOrNull(value) {
  return typeof value === "number" && Number.isSafeInteger(value)
    ? value
    : null;
}

function stringOrNull(value) {
  return typeof value === "string" ? value : null;
}

function recordOrNull(value) {
  return isRecord(value) ? value : null;
}

function isRecord(value) {
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
}

function isHttpsUrl(value) {
  if (typeof value !== "string") return false;
  try {
    return new URL(value).protocol === "https:";
  } catch {
    return false;
  }
}

function invalid(code, reason) {
  return { ok: false, code, reason };
}

async function readJsonFile(path, maxBytes) {
  const buffer = await readFile(path);
  if (buffer.byteLength > maxBytes) {
    throw new Error(`File troppo grande: ${path}`);
  }
  try {
    return JSON.parse(buffer.toString("utf8"));
  } catch {
    throw new Error(`JSON non valido: ${path}`);
  }
}

async function fetchHttpsJson(url, label, maxBytes) {
  if (!isHttpsUrl(url)) {
    throw new Error(`${label} richiede un URL HTTPS`);
  }
  const response = await fetch(url, {
    headers: { Accept: "application/json" },
    redirect: "error",
  });
  if (!response.ok) {
    throw new Error(`Download ${label} fallito: HTTP ${response.status}`);
  }
  const text = await response.text();
  if (Buffer.byteLength(text) > maxBytes) {
    throw new Error(`${label} remoto troppo grande`);
  }
  try {
    return JSON.parse(text);
  } catch {
    throw new Error(`${label} remoto non contiene JSON valido`);
  }
}

async function writeSnapshotFiles(outputPath, value, checksum, force) {
  const writeOptions = force ? { flag: "w" } : { flag: "wx" };
  await writeFile(outputPath, `${JSON.stringify(value, null, 2)}\n`, writeOptions);
  await writeFile(
    `${outputPath}.sha256`,
    `${checksum}  ${basename(outputPath)}\n`,
    writeOptions
  );
}

async function snapshotRoot(url, outputPath, force) {
  const rootValue = await fetchHttpsJson(
    url,
    "trust anchor",
    MAX_REGISTRY_BYTES
  );
  const snapshot = createRootTrustSnapshot(
    rootValue,
    url,
    new Date().toISOString()
  );
  await writeSnapshotFiles(
    outputPath,
    snapshot,
    snapshot.root_key_sha256,
    force
  );
  return snapshot;
}

async function snapshotRegistry(url, outputPath, force, rootValue) {
  const registryValue = await fetchHttpsJson(
    url,
    "registro",
    MAX_REGISTRY_BYTES
  );
  const snapshot = createRegistrySnapshot(
    registryValue,
    url,
    new Date().toISOString(),
    rootValue
      ? { rootKey: rootValue, requireRootSignature: true }
      : {}
  );
  await writeSnapshotFiles(
    outputPath,
    snapshot,
    snapshot.registry_sha256,
    force
  );
  return snapshot;
}

function usage() {
  return [
    "Hummint offline proof-bundle verifier",
    "",
    "Acquisisci una volta il trust anchor root:",
    "  node hummint-offline-verifier.mjs trust-root <https-url> <root.json> [--force]",
    "",
    "Acquisisci e conserva il registro pubblico:",
    "  node hummint-offline-verifier.mjs snapshot <https-url> <registry.json> --root <root.json> [--force]",
    "",
    "Verifica senza rete:",
    "  node hummint-offline-verifier.mjs verify <proof-bundle.json> <registry.json> --root <root.json>",
  ].join("\n");
}

async function main(argv) {
  const [command, ...args] = argv;
  if (command === "trust-root") {
    const parsed = parseCliOptions(args, false);
    const [url, outputPath] = parsed.positionals;
    if (!url || !outputPath || parsed.positionals.length !== 2) {
      throw new Error(usage());
    }
    const snapshot = await snapshotRoot(url, outputPath, parsed.force);
    console.log(JSON.stringify({
      ok: true,
      trust_anchor: resolve(outputPath),
      checksum: resolve(`${outputPath}.sha256`),
      root_key_sha256: snapshot.root_key_sha256,
      root_key_hint: snapshot.root_key.key_hint,
    }, null, 2));
    return;
  }
  if (command === "snapshot") {
    const parsed = parseCliOptions(args, true);
    const [url, outputPath] = parsed.positionals;
    if (!url || !outputPath || parsed.positionals.length !== 2) {
      throw new Error(usage());
    }
    const rootValue = parsed.rootPath
      ? await readJsonFile(parsed.rootPath, MAX_REGISTRY_BYTES)
      : null;
    const snapshot = await snapshotRegistry(
      url,
      outputPath,
      parsed.force,
      rootValue
    );
    console.log(JSON.stringify({
      ok: true,
      snapshot: resolve(outputPath),
      checksum: resolve(`${outputPath}.sha256`),
      registry_sha256: snapshot.registry_sha256,
      active_key_hint: snapshot.registry.active_key_hint,
      keys: snapshot.registry.keys.length,
      registry_root_signature_valid: Boolean(rootValue),
    }, null, 2));
    return;
  }
  if (command === "verify") {
    const parsed = parseCliOptions(args, true);
    const [bundlePath, registryPath] = parsed.positionals;
    if (!bundlePath || !registryPath || parsed.positionals.length !== 2) {
      throw new Error(usage());
    }
    const [bundle, registry, rootValue] = await Promise.all([
      readJsonFile(bundlePath, MAX_BUNDLE_BYTES),
      readJsonFile(registryPath, MAX_REGISTRY_BYTES),
      parsed.rootPath
        ? readJsonFile(parsed.rootPath, MAX_REGISTRY_BYTES)
        : Promise.resolve(null),
    ]);
    const result = verifyOfflineBundle(
      bundle,
      registry,
      rootValue
        ? { rootKey: rootValue, requireRootSignature: true }
        : {}
    );
    console.log(JSON.stringify(result, null, 2));
    if (!result.ok) process.exitCode = 2;
    return;
  }
  throw new Error(usage());
}

function parseCliOptions(args, allowRoot) {
  const positionals = [];
  let force = false;
  let rootPath = null;
  for (let index = 0; index < args.length; index += 1) {
    const item = args[index];
    if (item === "--force") {
      force = true;
    } else if (item === "--root" && allowRoot) {
      const value = args[index + 1];
      if (!value || value.startsWith("--")) throw new Error(usage());
      rootPath = value;
      index += 1;
    } else if (item.startsWith("--")) {
      throw new Error(usage());
    } else {
      positionals.push(item);
    }
  }
  return { positionals, force, rootPath };
}

const entryUrl = process.argv[1]
  ? pathToFileURL(resolve(process.argv[1])).href
  : "";
if (entryUrl === import.meta.url) {
  main(process.argv.slice(2)).catch((error) => {
    console.error(error instanceof Error ? error.message : String(error));
    process.exitCode = 1;
  });
}
