import process from "node:process";
import path from "node:path";
import { createHash } from "node:crypto";
import { pathToFileURL } from "node:url";

export const DEFAULT_STAGING_BASE_URL = "https://api.truckwash.io:4433";
export const EXPECTED_INSTALL_VERSION = "compose-php-agent-v3";
export const REQUIRED_MANIFEST_ARTIFACTS = [
  "agent.php",
  "lan-worker.php",
  "auto-updater.php",
  "docker-compose.gateway.yml",
  "Dockerfile.edge-agent",
  "Dockerfile.lan-worker",
  "Dockerfile.auto-updater",
  "gateway-launcher.sh",
  "truckwash-edge-gateway-stack.service",
  "truckwash-edge-agent.service",
];
export const INSTALLER_SCRIPT_REQUIRED_SNIPPETS = [
  "/edge-agent/install-token/status",
  "/edge-agent/artifacts/manifest.json",
  "report_install_status",
  'begin_install_phase "VERIFY_TOKEN"',
  'begin_install_phase "VERIFY_ARTIFACTS"',
  'begin_install_phase "WAIT_FOR_CLAIM"',
  'report_install_status "FAILED"',
  "verify_manifest_artifact",
  EXPECTED_INSTALL_VERSION,
];

export function normalizeBaseUrl(url) {
  return String(url || "").trim().replace(/\/+$/, "");
}

export function parseArgs(argv = process.argv.slice(2)) {
  const options = {
    baseUrl: DEFAULT_STAGING_BASE_URL,
    installToken: "",
    help: false,
  };

  for (let index = 0; index < argv.length; index += 1) {
    const arg = argv[index];
    const next = argv[index + 1];

    switch (arg) {
      case "--base-url":
        options.baseUrl = String(next || "").trim() || DEFAULT_STAGING_BASE_URL;
        index += 1;
        break;
      case "--install-token":
        options.installToken = String(next || "").trim();
        index += 1;
        break;
      case "--help":
      case "-h":
        options.help = true;
        break;
      default:
        throw new Error(`Unknown argument: ${arg}`);
    }
  }

  return options;
}

export function buildChecks(baseUrl, installToken) {
  const normalizedBaseUrl = normalizeBaseUrl(baseUrl);

  return [
    {
      name: "Ping",
      url: `${normalizedBaseUrl}/ping`,
    },
    {
      name: "Artifact manifest",
      url: `${normalizedBaseUrl}/edge-agent/artifacts/manifest.json`,
      artifactName: "manifest.json",
    },
    {
      name: "Agent PHP artifact",
      url: `${normalizedBaseUrl}/edge-agent/artifacts/agent.php`,
      artifactName: "agent.php",
    },
    {
      name: "Service unit artifact",
      url: `${normalizedBaseUrl}/edge-agent/artifacts/truckwash-edge-agent.service`,
      artifactName: "truckwash-edge-agent.service",
    },
    {
      name: "Installer script",
      url: `${normalizedBaseUrl}/edge-agent/install.sh?token=${encodeURIComponent(installToken)}`,
    },
  ];
}

export function validateArtifactManifestBody(body) {
  const manifest = JSON.parse(String(body || ""));
  if (manifest.version !== EXPECTED_INSTALL_VERSION) {
    throw new Error(`Artifact manifest version mismatch: expected ${EXPECTED_INSTALL_VERSION}, got ${manifest.version}`);
  }
  if (!Array.isArray(manifest.artifacts)) {
    throw new Error("Artifact manifest is missing artifacts.");
  }

  const byName = new Map(manifest.artifacts.map((artifact) => [artifact?.name, artifact]));
  const missingArtifacts = REQUIRED_MANIFEST_ARTIFACTS.filter((artifactName) => !byName.has(artifactName));
  if (missingArtifacts.length) {
    throw new Error(`Artifact manifest is missing required artifacts: ${missingArtifacts.join(", ")}`);
  }

  return manifest;
}

export function validateArtifactBodyAgainstManifest(manifest, artifactName, body) {
  const artifact = manifest?.artifacts?.find((entry) => entry?.name === artifactName);
  if (!artifact) {
    throw new Error(`Artifact ${artifactName} is missing from manifest.`);
  }

  const buffer = Buffer.isBuffer(body) ? body : Buffer.from(String(body || ""));
  const sha256 = createHash("sha256").update(buffer).digest("hex");
  if (sha256 !== artifact.sha256) {
    throw new Error(`Artifact ${artifactName} hash mismatch: ${sha256} !== ${artifact.sha256}`);
  }
  if (buffer.length !== artifact.bytes) {
    throw new Error(`Artifact ${artifactName} size mismatch: ${buffer.length} !== ${artifact.bytes}`);
  }

  return artifact;
}

export function validateInstallerScriptBody(body) {
  const source = String(body || "");
  const missingSnippets = INSTALLER_SCRIPT_REQUIRED_SNIPPETS.filter((snippet) => !source.includes(snippet));

  if (missingSnippets.length) {
    throw new Error(`Installer script is missing required status wiring: ${missingSnippets.join(", ")}`);
  }

  return INSTALLER_SCRIPT_REQUIRED_SNIPPETS;
}

function printUsage() {
  process.stdout.write(`Usage:
  node scripts/staging-edge-gateway-smoke.mjs --install-token <token> [--base-url <url>]

Options:
  --install-token <token>    Real edge-gateway install token used to validate install.sh.
  --base-url <url>           Public staging base URL. Default: ${DEFAULT_STAGING_BASE_URL}
  --help                     Show this help text.
`);
}

function previewBody(body, limit = 200) {
  const normalized = String(body || "").replace(/\s+/g, " ").trim();
  if (normalized.length <= limit) {
    return normalized;
  }
  return `${normalized.slice(0, limit)}...`;
}

export async function runSmoke({ baseUrl, installToken }) {
  if (String(installToken || "").trim() === "") {
    throw new Error("Missing required --install-token value.");
  }

  const checks = buildChecks(baseUrl, installToken);
  const results = [];
  let artifactManifest = null;

  for (const check of checks) {
    process.stdout.write(`[staging-smoke] GET ${check.url}\n`);
    const response = await fetch(check.url);
    const body = Buffer.from(await response.arrayBuffer());
    const result = {
      ...check,
      status: response.status,
      ok: response.ok,
      bodyPreview: previewBody(body.toString("utf8")),
    };
    results.push(result);

    if (!response.ok) {
      throw new Error(
        `${check.name} failed with HTTP ${response.status} at ${check.url}. ` +
        `Body preview: ${result.bodyPreview || "<empty>"}`
      );
    }

    if (check.name === "Artifact manifest") {
      artifactManifest = validateArtifactManifestBody(body.toString("utf8"));
      result.version = artifactManifest.version;
      result.artifactCount = artifactManifest.artifacts.length;
    }
    if (artifactManifest && check.artifactName && check.artifactName !== "manifest.json") {
      result.verifiedArtifact = validateArtifactBodyAgainstManifest(artifactManifest, check.artifactName, body);
    }
    if (check.name === "Installer script") {
      result.verifiedSnippets = validateInstallerScriptBody(body.toString("utf8"));
    }
  }

  return results;
}

async function main() {
  const options = parseArgs();

  if (options.help) {
    printUsage();
    return;
  }

  const results = await runSmoke(options);
  for (const result of results) {
    process.stdout.write(`[staging-smoke] OK ${result.status} ${result.name}\n`);
  }
}

const isDirectExecution = process.argv[1]
  && import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href;

if (isDirectExecution) {
  main().catch((error) => {
    process.stderr.write(`[staging-smoke] ERROR: ${error instanceof Error ? error.message : String(error)}\n`);
    process.exitCode = 1;
  });
}
