import { createHash } from "node:crypto";

export interface PacRegressionConsumerBinding {
  readonly id: string;
  readonly repositoryRef: string;
}

export interface PacRegressionDiagnosisInput {
  readonly targetId: string;
  readonly consumerId: string;
  readonly sourceRepository: string;
  readonly limitPerConsumer: number;
  readonly rows: readonly Record<string, unknown>[];
  readonly consumerBindings: readonly PacRegressionConsumerBinding[];
}

type TerminalState = "success" | "failure" | "pending";

function record(value: unknown): Record<string, unknown> {
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value as Record<string, unknown> : {};
}

function records(value: unknown): Record<string, unknown>[] {
  return Array.isArray(value) ? value.map(record) : [];
}

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

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

function safeSummary(value: unknown): string | null {
  const source = text(value);
  if (source === null) return null;
  return source
    .replace(/(authorization\s*[:=]\s*)(?:bearer\s+)?\S+/giu, "$1[REDACTED]")
    .replace(/(token|password|secret)(\s*[:=]\s*)\S+/giu, "$1$2[REDACTED]")
    .replace(/https?:\/\/[^\s/]+@/giu, "https://[REDACTED]@")
    .replace(/([?&][^=\s]+)=([^&\s]+)/gu, "$1=[REDACTED]")
    .replace(/\s+/gu, " ")
    .trim()
    .slice(0, 480);
}

function safeFailureRecord(value: unknown): Record<string, unknown> | null {
  const source = record(value);
  if (Object.keys(source).length === 0) return null;
  const safeToken = (item: unknown): string | null => {
    const candidate = text(item);
    return candidate !== null && /^[A-Za-z0-9_.-]{1,100}$/u.test(candidate) ? candidate : null;
  };
  return {
    type: safeToken(source.type),
    code: safeToken(source.code),
    phase: safeToken(source.phase) ?? safeToken(source.stage),
    exitCode: number(source.exitCode),
    reason: safeSummary(source.reason),
    message: safeSummary(source.message),
    stderrSummary: safeSummary(source.stderrSummary),
    valuesPrinted: false,
  };
}

function terminalState(row: Record<string, unknown>): TerminalState {
  if (row.deliveryAuthorityEligible !== true) return "pending";
  if (row.status === "True") return "success";
  if (row.status === "False") return "failure";
  if (Object.keys(record(row.firstBreak)).length > 0 || Object.keys(record(row.error)).length > 0) return "failure";
  return "pending";
}

function normalizedReason(value: unknown): string | null {
  const source = safeSummary(value);
  if (source === null) return null;
  return source
    .toLowerCase()
    .replace(/\b[0-9a-f]{40,64}\b/gu, "<sha>")
    .replace(/\b[0-9a-f]{8}-[0-9a-f-]{27,}\b/gu, "<uuid>")
    .replace(/\b\d+\b/gu, "<n>")
    .replace(/\s+/gu, " ")
    .trim()
    .slice(0, 240);
}

export function pacFailureFingerprint(row: Record<string, unknown>): Record<string, unknown> | null {
  if (terminalState(row) !== "failure") return null;
  const firstBreak = record(row.firstBreak);
  const error = record(row.error);
  const failedTask = record(record(row.taskRuns).failed);
  const failedStep = record(failedTask.failedStep);
  const code = text(firstBreak.code) ?? text(error.code) ?? "pipeline-run-failed";
  const phase = text(firstBreak.phase) ?? text(firstBreak.stage) ?? text(error.phase) ?? text(failedTask.name) ?? "pipeline-run";
  const errorType = text(error.type);
  const exitCode = number(error.exitCode) ?? number(failedStep.exitCode);
  const reason = normalizedReason(firstBreak.reason)
    ?? normalizedReason(error.stderrSummary)
    ?? normalizedReason(error.message)
    ?? normalizedReason(error.reason)
    ?? normalizedReason(failedStep.reason)
    ?? normalizedReason(row.reason);
  const signature = [code, phase, errorType ?? "-", exitCode ?? "-", reason ?? "-"].join("|");
  return {
    id: `sha256:${createHash("sha256").update(signature).digest("hex").slice(0, 16)}`,
    code,
    phase,
    errorType,
    exitCode,
    reason,
    valuesPrinted: false,
  };
}

function rowSummary(row: Record<string, unknown>): Record<string, unknown> | null {
  if (Object.keys(row).length === 0) return null;
  return {
    id: row.id ?? row.pipelineRun ?? null,
    consumer: row.consumer ?? null,
    status: row.status ?? null,
    reason: row.reason ?? null,
    triggeredAt: row.triggeredAt ?? null,
    completionTime: row.completionTime ?? null,
    durationSeconds: row.durationSeconds ?? null,
    sourceCommit: row.commit ?? null,
    branch: row.branch ?? null,
    traceId: row.traceId ?? null,
    firstBreak: safeFailureRecord(row.firstBreak),
    error: safeFailureRecord(row.error),
    fingerprint: pacFailureFingerprint(row),
    valuesPrinted: false,
  };
}

function time(value: unknown): number {
  const parsed = typeof value === "string" ? Date.parse(value) : Number.NaN;
  return Number.isFinite(parsed) ? parsed : 0;
}

function compareUrl(repository: string, base: string | null, head: string | null): string | null {
  if (!/^[^/]+\/[^/]+$/u.test(repository) || base === null || head === null) return null;
  if (!/^[0-9a-f]{40}$/iu.test(base) || !/^[0-9a-f]{40}$/iu.test(head) || base === head) return null;
  return `https://github.com/${repository}/compare/${base}...${head}`;
}

export function diagnosePacRegression(input: PacRegressionDiagnosisInput): Record<string, unknown> {
  const selectedRows = input.rows
    .filter((row) => row.consumer === input.consumerId && row.deliveryAuthorityEligible === true)
    .sort((left, right) => time(right.triggeredAt) - time(left.triggeredAt));
  const terminalRows = selectedRows.filter((row) => terminalState(row) !== "pending");
  const latestTerminal = terminalRows[0] ?? null;
  const activeRegression = latestTerminal !== null && terminalState(latestTerminal) === "failure";
  const failureStreak: Record<string, unknown>[] = [];
  let lastSuccess: Record<string, unknown> | null = null;
  if (activeRegression) {
    for (const row of terminalRows) {
      if (terminalState(row) === "failure") {
        failureStreak.push(row);
        continue;
      }
      lastSuccess = row;
      break;
    }
  } else if (latestTerminal !== null && terminalState(latestTerminal) === "success") {
    lastSuccess = latestTerminal;
  }
  const firstFailure = failureStreak.at(-1) ?? null;
  const anchorFingerprint = firstFailure === null ? null : pacFailureFingerprint(firstFailure);
  const anchorFingerprintId = text(record(anchorFingerprint).id);
  const comparisonWindowStart = lastSuccess === null ? 0 : time(lastSuccess.triggeredAt);
  const matchingFailures = anchorFingerprintId === null ? [] : input.rows.filter((row) => {
    if (row.deliveryAuthorityEligible !== true || terminalState(row) !== "failure") return false;
    if (comparisonWindowStart > 0 && time(row.triggeredAt) < comparisonWindowStart) return false;
    return text(record(pacFailureFingerprint(row)).id) === anchorFingerprintId;
  });
  const affectedConsumers = [...new Set(matchingFailures.map((row) => text(row.consumer)).filter((value): value is string => value !== null))].sort();
  const selectedBinding = input.consumerBindings.find((consumer) => consumer.id === input.consumerId) ?? null;
  const repositoryOwners = selectedBinding === null
    ? []
    : input.consumerBindings.filter((consumer) => consumer.repositoryRef === selectedBinding.repositoryRef).map((consumer) => consumer.id).sort();
  const scope = affectedConsumers.length > 1 || repositoryOwners.length > 1
    ? "public-service"
    : activeRegression && affectedConsumers.length === 1
      ? "consumer-private-candidate"
      : "unknown";
  const baseCommit = text(lastSuccess?.commit);
  const failedCommit = text(firstFailure?.commit);
  const sourceCompareUrl = compareUrl(input.sourceRepository, baseCommit, failedCommit);
  const windowComplete = !activeRegression || lastSuccess !== null;
  const state = latestTerminal === null
    ? "no-terminal-evidence"
    : !activeRegression
      ? "healthy"
      : windowComplete
        ? "active-regression"
        : "active-regression-baseline-missing";
  const confidence = !activeRegression || firstFailure === null
    ? "none"
    : sourceCompareUrl !== null && failureStreak.length >= 2 && anchorFingerprintId !== null
      ? "high"
      : sourceCompareUrl !== null
        ? "medium"
        : "low";
  const firstFailureId = text(firstFailure?.id) ?? text(firstFailure?.pipelineRun);
  return {
    state,
    incident: activeRegression,
    mutation: false,
    target: input.targetId,
    consumer: input.consumerId,
    window: {
      complete: windowComplete,
      limitPerConsumer: input.limitPerConsumer,
      selectedRows: selectedRows.length,
      terminalRows: terminalRows.length,
      failureStreak: failureStreak.length,
      lastKnownGood: rowSummary(lastSuccess ?? {}),
      firstFailed: rowSummary(firstFailure ?? {}),
      latestTerminal: rowSummary(latestTerminal ?? {}),
      valuesPrinted: false,
    },
    commonFailure: {
      fingerprint: anchorFingerprint,
      matchingRuns: matchingFailures.length,
      affectedConsumers,
      valuesPrinted: false,
    },
    sourceRange: {
      repository: input.sourceRepository,
      baseCommit,
      failedCommit,
      compareUrl: sourceCompareUrl,
      candidateCommit: sourceCompareUrl === null ? null : failedCommit,
      valuesPrinted: false,
    },
    scopeAssessment: {
      scope,
      confirmed: scope === "public-service" && affectedConsumers.length > 1,
      repositoryRef: selectedBinding?.repositoryRef ?? null,
      repositoryOwners,
      reason: affectedConsumers.length > 1
        ? "同一失败指纹影响多个 consumer，按公共服务面处理。"
        : repositoryOwners.length > 1
          ? "选中 consumer 使用由多个 consumer 共享的 repositoryRef，按公共服务面处理。"
          : scope === "consumer-private-candidate"
            ? "当前只观察到单 consumer 失败；仍需用 changed-file 证据确认私有配置归属。"
            : "现有证据不足以确认配置归属。",
      valuesPrinted: false,
    },
    rollbackCandidate: {
      state: sourceCompareUrl === null ? "insufficient-evidence" : "review-required",
      commit: sourceCompareUrl === null ? null : failedCommit,
      confidence,
      automatic: false,
      requiresReview: true,
      authorizationRequired: scope !== "consumer-private-candidate",
      reason: sourceCompareUrl === null
        ? "必须先取得最后成功与首次失败之间的不同 source commit。"
        : "只提供精确回退候选；必须审查变更和不可逆状态，并通过单一职责 PR 回退。",
      valuesPrinted: false,
    },
    next: {
      debugFirstFailure: firstFailureId === null
        ? null
        : `bun scripts/cli.ts platform-infra pipelines-as-code debug-step --target ${input.targetId} --consumer ${input.consumerId} --id ${firstFailureId}`,
      expandWindow: windowComplete || input.limitPerConsumer >= 50
        ? null
        : `bun scripts/cli.ts platform-infra pipelines-as-code diagnose-regression --target ${input.targetId} --consumer ${input.consumerId} --limit ${Math.min(50, Math.max(input.limitPerConsumer + 10, input.limitPerConsumer * 2))}`,
      windowLimitReached: !windowComplete && input.limitPerConsumer >= 50,
      valuesPrinted: false,
    },
    evidence: {
      matchingFailures: matchingFailures.map((row) => rowSummary(row)),
      valuesPrinted: false,
    },
    valuesPrinted: false,
  };
}

function value(value: unknown): string {
  if (value === null || value === undefined || value === "") return "-";
  return String(value);
}

export function renderPacRegressionDiagnosis(result: Record<string, unknown>): string {
  const window = record(result.window);
  const lastKnownGood = record(window.lastKnownGood);
  const firstFailed = record(window.firstFailed);
  const commonFailure = record(result.commonFailure);
  const fingerprint = record(commonFailure.fingerprint);
  const sourceRange = record(result.sourceRange);
  const scope = record(result.scopeAssessment);
  const rollback = record(result.rollbackCandidate);
  const next = record(result.next);
  const observation = record(result.observation);
  const historyErrors = records(result.historyErrors);
  const firstReadError = historyErrors[0] ?? {};
  return [
    "PLATFORM-INFRA PIPELINES-AS-CODE REGRESSION DIAGNOSIS",
    `STATE: ${value(result.state)}  INCIDENT: ${value(result.incident)}  MUTATION: false`,
    `TARGET: ${value(result.target)}  CONSUMER: ${value(result.consumer)}`,
    `READ: ${value(observation.status)} elapsed-ms=${value(observation.elapsedMs)} errors=${historyErrors.length}`,
    ...(historyErrors.length === 0 ? [] : [`READ_ERROR: context=${value(firstReadError.context)} error=${value(firstReadError.error ?? firstReadError.stderr)}`]),
    `WINDOW: complete=${value(window.complete)} rows=${value(window.selectedRows)} terminal=${value(window.terminalRows)} failure-streak=${value(window.failureStreak)}`,
    `LAST_KNOWN_GOOD: ${value(lastKnownGood.id)} commit=${value(lastKnownGood.sourceCommit)}`,
    `FIRST_FAILED: ${value(firstFailed.id)} commit=${value(firstFailed.sourceCommit)} trace=${value(firstFailed.traceId)}`,
    `FINGERPRINT: ${value(fingerprint.id)} code=${value(fingerprint.code)} phase=${value(fingerprint.phase)} matches=${value(commonFailure.matchingRuns)}`,
    `AFFECTED_CONSUMERS: ${Array.isArray(commonFailure.affectedConsumers) ? commonFailure.affectedConsumers.join(",") || "-" : "-"}`,
    `SOURCE_RANGE: ${value(sourceRange.baseCommit)}...${value(sourceRange.failedCommit)}`,
    `COMPARE: ${value(sourceRange.compareUrl)}`,
    `SCOPE: ${value(scope.scope)} confirmed=${value(scope.confirmed)} authorization-required=${value(rollback.authorizationRequired)}`,
    `ROLLBACK_CANDIDATE: ${value(rollback.state)} commit=${value(rollback.commit)} confidence=${value(rollback.confidence)} automatic=false`,
    `SCOPE_REASON: ${value(scope.reason)}`,
    "NEXT:",
    `  debug-first-failure: ${value(next.debugFirstFailure)}`,
    `  expand-window: ${value(next.expandWindow)}`,
    `  window-limit-reached: ${value(next.windowLimitReached)}`,
  ].join("\n") + "\n";
}
