import { readFileSync } from "node:fs";
import { execFileSync } from "node:child_process";
import { existsSync } from "node:fs";
import https from "node:https";

const namespace = process.env.NAMESPACE || "";
const configMap = process.env.CONFIGMAP || "";
const followerIds = parseFollowerIds(process.env.FOLLOWERS_JSON || "[]");
const maxTimingStages = Number(process.env.MAX_TIMING_STAGES || "24");
const tokenPath = "/var/run/secrets/kubernetes.io/serviceaccount/token";
const caPath = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt";
const inCluster = Boolean(process.env.KUBERNETES_SERVICE_HOST && existsSync(tokenPath) && existsSync(caPath));
const host = process.env.KUBERNETES_SERVICE_HOST;
const port = Number(process.env.KUBERNETES_SERVICE_PORT || "443");
const token = inCluster ? readFileSync(tokenPath, "utf8").trim() : "";
const ca = inCluster ? readFileSync(caPath) : null;

function parseFollowerIds(text) {
  try {
    const parsed = JSON.parse(text);
    return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string" && item.length > 0) : [];
  } catch {
    return [];
  }
}

function request(method, path, body, contentType = "application/json") {
  return new Promise((resolve, reject) => {
    const headers = { authorization: `Bearer ${token}` };
    const payload = body === undefined ? null : typeof body === "string" ? body : JSON.stringify(body);
    if (payload !== null) {
      headers["content-type"] = contentType;
      headers["content-length"] = Buffer.byteLength(payload);
    }
    const req = https.request({ host, port, path, method, ca, headers }, (res) => {
      let text = "";
      res.setEncoding("utf8");
      res.on("data", (chunk) => { text += chunk; });
      res.on("end", () => resolve({ status: res.statusCode || 0, text }));
    });
    req.on("error", reject);
    if (payload !== null) req.write(payload);
    req.end();
  });
}

async function readConfigMap() {
  if (!inCluster) return readConfigMapViaKubectl();
  try {
    const result = await request("GET", `/api/v1/namespaces/${encodeURIComponent(namespace)}/configmaps/${encodeURIComponent(configMap)}`);
    if (result.status === 404) return { ok: true, present: false, object: null, error: result.text };
    if (result.status < 200 || result.status >= 300) return { ok: false, present: false, object: null, error: result.text || `kube api GET configmap status ${result.status}` };
    return { ok: true, present: true, object: JSON.parse(result.text), error: "" };
  } catch (error) {
    return { ok: false, present: false, object: null, error: error?.message || String(error) };
  }
}

function readConfigMapViaKubectl() {
  try {
    const stdout = execFileSync("kubectl", ["-n", namespace, "get", "configmap", configMap, "-o", "json"], {
      encoding: "utf8",
      maxBuffer: 16 * 1024 * 1024,
      stdio: ["ignore", "pipe", "pipe"],
    });
    return { ok: true, present: true, object: JSON.parse(stdout), error: "" };
  } catch (error) {
    const stderr = String(error?.stderr || error?.message || "");
    if (/not found/i.test(stderr)) return { ok: true, present: false, object: null, error: stderr };
    return { ok: false, present: false, object: null, error: stderr || "kubectl configmap read failed" };
  }
}

function compactStateText(text, includeCommand) {
  if (typeof text !== "string" || text.length === 0) return null;
  let state;
  try {
    state = JSON.parse(text);
  } catch {
    return null;
  }
  const compact = {
    id: stringOrNull(state.id),
    adapter: stringOrNull(state.adapter),
    enabled: state.enabled === true,
    phase: stringOrNull(state.phase),
    source: compactSource(state.source),
    target: compactTarget(state.target),
    lastTriggeredSha: stringOrNull(state.lastTriggeredSha),
    lastSucceededSha: stringOrNull(state.lastSucceededSha),
    pipelineRun: stringOrNull(state.pipelineRun),
    inFlightJob: stringOrNull(state.inFlightJob),
    controller: compactController(state.controller),
    decision: stringOrNull(state.decision),
    dryRun: state.dryRun === true,
    updatedAt: stringOrNull(state.updatedAt),
    timings: compactTimings(state.timings),
    warnings: arrayStrings(state.warnings).slice(0, 6),
    stateFormat: stringOrNull(state.stateFormat),
    rawStateDiagnostic: rawStateDiagnostic(state, text),
  };
  if (includeCommand) compact.command = compactCommand(state.command);
  return compact;
}

function compactCommand(command) {
  const value = recordOrNull(command);
  if (value === null) return null;
  return {
    mode: stringOrNull(value.mode) ?? stringOrNull(value.status),
    namespace: stringOrNull(value.namespace),
    pipelineRun: stringOrNull(value.pipelineRun),
    sourceCommit: stringOrNull(value.sourceCommit),
    sourceStageRef: stringOrNull(value.sourceStageRef),
    wait: value.wait === true ? true : null,
    pipelineRunCompleted: value.pipelineRunCompleted === true ? true : null,
    stillRunning: value.stillRunning === true ? true : null,
    closeout: compactCloseout(value.closeout),
    payload: compactNativePayload(value.payload),
    exitCode: numberOrNull(value.exitCode),
    timedOut: value.timedOut === true,
    statusAuthority: stringOrNull(value.statusAuthority),
    reconcileTimeline: compactReconcileTimeline(value.reconcileTimeline),
    parsedDownstreamCliOutput: false,
  };
}

function compactReconcileTimeline(reconcileTimeline) {
  const value = recordOrNull(reconcileTimeline);
  if (value === null) return null;
  const steps = arrayRecords(value.steps).slice(-16).map((step) => ({
    follower: stringOrNull(step.follower),
    step: stringOrNull(step.step),
    status: stringOrNull(step.status),
    startedAt: stringOrNull(step.startedAt),
    finishedAt: stringOrNull(step.finishedAt),
    elapsedMs: numberOrNull(step.elapsedMs),
    observedSha: stringOrNull(step.observedSha),
    targetSha: stringOrNull(step.targetSha),
    phase: stringOrNull(step.phase),
    pipelineRun: stringOrNull(step.pipelineRun),
    object: stringOrNull(step.object),
    message: stringOrNull(step.message),
    reason: stringOrNull(step.reason),
    exitCode: numberOrNull(step.exitCode),
  }));
  return {
    startedAt: stringOrNull(value.startedAt),
    finishedAt: stringOrNull(value.finishedAt),
    elapsedMs: numberOrNull(value.elapsedMs),
    controller: value.controller === true,
    dryRun: value.dryRun === true,
    confirm: value.confirm === true,
    wait: value.wait === true,
    followerCount: numberOrNull(value.followerCount),
    followers: arrayStrings(value.followers).slice(0, 8),
    bounded: true,
    omittedStepCount: Math.max(0, arrayRecords(value.steps).length - steps.length),
    steps,
  };
}

function rawStateDiagnostic(state, text) {
  const command = recordOrNull(state.command);
  const reconcileTimeline = recordOrNull(command?.reconcileTimeline);
  return {
    bounded: true,
    valueBytes: Buffer.byteLength(text, "utf8"),
    hasCommand: command !== null,
    commandBytes: jsonBytes(command),
    hasReconcileTimeline: reconcileTimeline !== null,
    reconcileTimelineBytes: jsonBytes(reconcileTimeline),
    reconcileTimelineStepCount: reconcileTimeline === null ? 0 : arrayRecords(reconcileTimeline.steps).length,
    reconcileTimelineStartedAt: stringOrNull(reconcileTimeline?.startedAt),
    reconcileTimelineFinishedAt: stringOrNull(reconcileTimeline?.finishedAt),
    reconcileTimelineElapsedMs: numberOrNull(reconcileTimeline?.elapsedMs),
    missingReason: reconcileTimeline === null ? command === null ? "command missing" : "command.reconcileTimeline missing" : null,
  };
}

function jsonBytes(value) {
  if (value === null) return null;
  try {
    return Buffer.byteLength(JSON.stringify(value), "utf8");
  } catch {
    return null;
  }
}

function compactCloseout(closeout) {
  const value = recordOrNull(closeout);
  if (value === null) return null;
  return {
    ok: value.ok === true,
    completed: value.completed === true,
    timedOut: value.timedOut === true,
    polls: numberOrNull(value.polls),
    elapsedMs: numberOrNull(value.elapsedMs),
    summary: compactNativePayload(value.summary),
    statusAuthority: stringOrNull(value.statusAuthority),
    parsedDownstreamCliOutput: false,
  };
}

function compactNativePayload(payload) {
  const value = recordOrNull(payload);
  if (value === null) return null;
  const refreshEvidence = compactRefreshEvidence(recordOrNull(recordOrNull(value.nativeCapabilities)?.controlPlaneRefresh));
  return {
    gitMirror: compactGitMirror(value.gitMirror),
    reuseConfig: compactReuseConfig(value.reuseConfig),
    tekton: compactTekton(value.tekton),
    pipeline: compactPipeline(value.pipeline),
    taskRuns: compactTaskRuns(value.taskRuns),
    planArtifacts: compactPlanArtifacts(value.planArtifacts),
    argo: compactArgo(value.argo),
    runtime: compactRuntime(value.runtime),
    refreshEvidence,
    refreshEvidenceReason: refreshEvidence === null ? "no stored control-plane-refresh capability evidence; status refresh job is reported separately at top-level refresh" : null,
    errors: arrayStrings(value.errors).slice(0, 5),
    statusAuthority: stringOrNull(value.statusAuthority),
    parsedDownstreamCliOutput: false,
  };
}

function compactReuseConfig(reuseConfig) {
  const value = recordOrNull(reuseConfig);
  if (value === null) return null;
  return {
    ok: value.ok === true,
    present: value.present === true,
    path: stringOrNull(value.path),
    sourceCommit: stringOrNull(value.sourceCommit),
    stageRef: stringOrNull(value.stageRef),
    sha256: stringOrNull(value.sha256),
    serviceCount: numberOrNull(value.serviceCount),
    serviceIds: compactReuseServiceIds(value),
    errors: arrayStrings(value.errors).slice(0, 5),
    valuesRedacted: true,
  };
}

function compactReuseServiceIds(value) {
  const explicit = arrayStrings(value.serviceIds);
  if (explicit.length > 0) return explicit.slice(0, 16);
  return arrayRecords(value.services).map((service) => stringOrNull(service.id)).filter(Boolean).slice(0, 16);
}

function compactGitMirror(gitMirror) {
  const value = recordOrNull(gitMirror);
  if (value === null) return null;
  return {
    ok: value.ok === true,
    sourceSnapshotReady: value.sourceSnapshotReady === true,
    pendingFlush: value.pendingFlush === true,
    githubInSync: value.githubInSync === true,
    sourceBranch: stringOrNull(value.sourceBranch),
    gitopsBranch: stringOrNull(value.gitopsBranch),
    localSource: stringOrNull(value.localSource),
    githubSource: stringOrNull(value.githubSource),
    localGitops: stringOrNull(value.localGitops),
    githubGitops: stringOrNull(value.githubGitops),
  };
}

function compactTekton(tekton) {
  const value = recordOrNull(tekton);
  if (value === null) return null;
  return {
    name: stringOrNull(value.name),
    pipelineRefName: stringOrNull(value.pipelineRefName),
    succeeded: value.succeeded === true ? true : value.succeeded === false ? false : null,
    reason: stringOrNull(value.reason),
    startTime: stringOrNull(value.startTime),
    completionTime: stringOrNull(value.completionTime),
    durationSeconds: numberOrNull(value.durationSeconds),
  };
}

function compactPipeline(pipeline) {
  const value = recordOrNull(pipeline);
  if (value === null) return null;
  return {
    metadata: recordOrNull(value.metadata),
    spec: recordOrNull(value.spec),
  };
}

function compactRefreshEvidence(refresh) {
  const value = recordOrNull(refresh);
  const summary = recordOrNull(value?.summary);
  if (value === null || summary === null) return null;
  return {
    jobName: stringOrNull(value.jobName) ?? stringOrNull(summary.jobName),
    namespace: stringOrNull(value.namespace) ?? stringOrNull(summary.namespace),
    status: stringOrNull(summary.status),
    pipeline: stringOrNull(summary.pipeline) || stringOrNull(recordOrNull(summary.apply)?.pipelineName) || stringOrNull(recordOrNull(summary.render)?.pipelineName),
    sourceCommit: stringOrNull(summary.sourceCommit),
    sourceStageRef: stringOrNull(summary.sourceStageRef),
    elapsedMs: numberOrNull(summary.elapsedMs),
    render: compactRefreshRender(recordOrNull(summary.render)),
    apply: compactRefreshApply(recordOrNull(summary.apply)),
    sourceAuthority: stringOrNull(summary.sourceAuthority),
    statusAuthority: stringOrNull(summary.statusAuthority),
    parsedDownstreamCliOutput: false,
  };
}

function compactRefreshRender(value) {
  if (value === null) return null;
  return {
    pipelineName: stringOrNull(value.pipelineName),
    taskCount: numberOrNull(value.taskCount),
    runtimeReadyTask: compactRefreshRuntimeReady(recordOrNull(value.runtimeReadyTask)),
  };
}

function compactRefreshApply(value) {
  if (value === null) return null;
  return {
    pipelineName: stringOrNull(value.pipelineName),
    namespace: stringOrNull(value.namespace),
    resourceVersion: stringOrNull(value.resourceVersion),
    annotations: compactStringMap(recordOrNull(value.annotations)),
    labels: compactStringMap(recordOrNull(value.labels)),
    degradedReason: stringOrNull(value.degradedReason),
  };
}

function compactRefreshRuntimeReady(value) {
  if (value === null) return null;
  return {
    present: booleanOrNull(value.present),
    name: stringOrNull(value.name),
    runAfter: compactStringArray(value.runAfter, 4),
    when: compactWhenList(value.when, 4),
  };
}

function compactWhenList(value, limit) {
  return Array.isArray(value)
    ? value.map((item) => recordOrNull(item)).filter(Boolean).slice(0, limit).map((item) => ({
        input: stringOrNull(item.input),
        operator: stringOrNull(item.operator),
        values: compactStringArray(item.values, 4),
      }))
    : [];
}

function compactStringMap(value) {
  if (value === null) return null;
  const output = {};
  for (const [key, item] of Object.entries(value).slice(0, 8)) {
    const text = stringOrNull(item);
    if (text !== null) output[key] = text;
  }
  return Object.keys(output).length === 0 ? null : output;
}

function compactArgo(argo) {
  const value = recordOrNull(argo);
  if (value === null) return null;
  return {
    name: stringOrNull(value.name),
    syncStatus: stringOrNull(value.syncStatus),
    healthStatus: stringOrNull(value.healthStatus),
    healthMessage: stringOrNull(value.healthMessage),
    revision: stringOrNull(value.revision),
    operationPhase: stringOrNull(value.operationPhase),
    operationMessage: stringOrNull(value.operationMessage),
    operationStartedAt: stringOrNull(value.operationStartedAt),
    operationFinishedAt: stringOrNull(value.operationFinishedAt),
    operationDurationSeconds: numberOrNull(value.operationDurationSeconds),
    conditions: arrayRecords(value.conditions).slice(0, 5),
    nonReadyResources: arrayRecords(value.nonReadyResources).slice(0, 5),
    ready: value.ready === true,
  };
}

function compactRuntime(runtime) {
  const value = recordOrNull(runtime);
  if (value === null) return null;
  return {
    namespace: stringOrNull(value.namespace),
    ready: value.ready === true,
    targetSha: stringOrNull(value.targetSha),
    expectedSha: stringOrNull(value.expectedSha),
    aligned: value.aligned === true ? true : value.aligned === false ? false : null,
  };
}

function compactTaskRuns(taskRuns) {
  const value = recordOrNull(taskRuns);
  if (value === null) return null;
  const items = arrayRecords(value.items);
  return {
    ok: value.ok === true,
    count: numberOrNull(value.count),
    succeededCount: numberOrNull(value.succeededCount),
    failedCount: numberOrNull(value.failedCount),
    activeCount: numberOrNull(value.activeCount),
    performance: recordOrNull(value.performance),
    failedItems: items.filter((item) => item.status === "False").slice(0, 5).map(compactTaskRunItem),
    activeItems: items.filter((item) => item.status !== "True" && item.status !== "False").slice(0, 5).map(compactTaskRunItem),
    slowItems: arrayRecords(recordOrNull(value.performance)?.slowTaskRuns).slice(0, 5).map(compactTaskRunItem),
  };
}

function compactTaskRunItem(item) {
  return {
    name: stringOrNull(item.name),
    pipelineTask: stringOrNull(item.pipelineTask),
    status: stringOrNull(item.status),
    reason: stringOrNull(item.reason),
    durationSeconds: numberOrNull(item.durationSeconds),
  };
}

function compactPlanArtifacts(planArtifacts) {
  const value = recordOrNull(planArtifacts);
  if (value === null) return null;
  return {
    ok: value.ok === true,
    pipelineRun: stringOrNull(value.pipelineRun),
    eventFound: value.eventFound === true,
    degradedReason: stringOrNull(value.degradedReason),
    sourceCommitId: stringOrNull(value.sourceCommitId),
    affectedServicesCount: arrayStrings(value.affectedServices).length,
    rolloutServicesCount: arrayStrings(value.rolloutServices).length,
    buildServicesCount: arrayStrings(value.buildServices).length,
    reusedServicesCount: arrayStrings(value.reusedServices).length,
    buildSkippedCount: numberOrNull(value.buildSkippedCount),
    summary: stringOrNull(value.summary),
    disclosure: stringOrNull(value.disclosure),
  };
}

function compactSource(source) {
  const value = recordOrNull(source);
  if (value === null) return null;
  return {
    repository: stringOrNull(value.repository),
    branch: stringOrNull(value.branch),
    branchRef: stringOrNull(value.branchRef),
    snapshotPrefix: stringOrNull(value.snapshotPrefix),
    observedSha: stringOrNull(value.observedSha),
  };
}

function compactTarget(target) {
  const value = recordOrNull(target);
  if (value === null) return null;
  return {
    node: stringOrNull(value.node),
    lane: stringOrNull(value.lane),
    namespace: stringOrNull(value.namespace),
    sentinel: stringOrNull(value.sentinel),
    targetSha: stringOrNull(value.targetSha),
  };
}

function compactController(controller) {
  const value = recordOrNull(controller);
  if (value === null) return null;
  return {
    mode: stringOrNull(value.mode),
    stateConfigMap: stringOrNull(value.stateConfigMap),
    leaseName: stringOrNull(value.leaseName),
  };
}

function compactTimings(timings) {
  const value = recordOrNull(timings);
  if (value === null) return null;
  return {
    budgetSeconds: numberOrNull(value.budgetSeconds),
    totalSeconds: numberOrNull(value.totalSeconds),
    totalStatus: stringOrNull(value.totalStatus),
    totalSource: stringOrNull(value.totalSource),
    sourceCommit: stringOrNull(value.sourceCommit),
    startedAt: stringOrNull(value.startedAt),
    finishedAt: stringOrNull(value.finishedAt),
    overBudget: typeof value.overBudget === "boolean" ? value.overBudget : null,
    stages: prioritizedStageTimings(arrayRecords(value.stages)).slice(0, maxTimingStages).map(compactStageTiming),
  };
}

function prioritizedStageTimings(stages) {
  const priority = [];
  const rest = [];
  for (const stage of stages) {
    if (isPriorityTaskStage(stage)) priority.push(stage);
    else rest.push(stage);
  }
  const seen = new Set();
  const out = [];
  for (const stage of [...priority, ...rest]) {
    const key = [
      stringOrNull(stage.stage),
      stringOrNull(stage.status),
      stringOrNull(stage.source),
      stringOrNull(stage.object),
    ].filter((item) => item !== null).join("|");
    if (seen.has(key)) continue;
    seen.add(key);
    out.push(stage);
  }
  return out;
}

function isPriorityTaskStage(stage) {
  const name = stringOrNull(stage.stage) || "";
  if (!name.startsWith("task:")) return false;
  const status = stringOrNull(stage.status) || "";
  const seconds = numberOrNull(stage.seconds);
  return status.startsWith("failed") || status === "running" || (seconds !== null && seconds > 60);
}

function compactStageTiming(stage) {
  return {
    stage: stringOrNull(stage.stage),
    status: stringOrNull(stage.status),
    seconds: numberOrNull(stage.seconds),
    budgetSeconds: numberOrNull(stage.budgetSeconds),
    source: stringOrNull(stage.source),
    object: stringOrNull(stage.object),
  };
}

function recordOrNull(value) {
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
}

function arrayRecords(value) {
  return Array.isArray(value) ? value.filter((item) => recordOrNull(item) !== null) : [];
}

function arrayStrings(value) {
  return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
}

function stringOrNull(value) {
  return typeof value === "string" && value.length > 0 ? value : null;
}

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

function booleanOrNull(value) {
  return value === true ? true : value === false ? false : null;
}

function compactStringArray(value, limit) {
  return Array.isArray(value) ? value.map((item) => stringOrNull(item)).filter(Boolean).slice(0, limit) : [];
}

const result = await readConfigMap();
const errors = [];
const stateByFollower = {};
const valueBytes = {};

if (result.ok && result.present) {
  const data = recordOrNull(result.object?.data) || {};
  const includeCommand = followerIds.length === 1;
  for (const id of followerIds) {
    const text = typeof data[id] === "string" ? data[id] : "";
    if (text.length === 0) continue;
    valueBytes[id] = Buffer.byteLength(text, "utf8");
    const compact = compactStateText(text, includeCommand);
    if (compact === null) errors.push(`${id}: invalid state json`);
    else stateByFollower[id] = compact;
  }
}

if (!result.ok) errors.push(result.error);

process.stdout.write(JSON.stringify({
  ok: result.ok && errors.length === 0,
  present: result.present,
  metadata: result.object === null ? null : {
    name: stringOrNull(result.object?.metadata?.name),
    namespace: stringOrNull(result.object?.metadata?.namespace),
    resourceVersion: stringOrNull(result.object?.metadata?.resourceVersion),
    updatedAt: stringOrNull(result.object?.data?._updatedAt),
    keyCount: Object.keys(recordOrNull(result.object?.data) || {}).length,
  },
  stateByFollower,
  valueBytes,
  errors,
  statusAuthority: "target-node-summary",
  parsedDownstreamCliOutput: false,
}));
