// SPEC: PJ2026-01060703 CI/CD branch follower bounded evidence helpers.
// Responsibility: compact pipeline/runtime-ready and refresh evidence for status/drill-down output.

export function compactRefreshEvidence(value: Record<string, unknown> | null): Record<string, unknown> | null {
  if (value === null) return null;
  const summary = asOptionalRecord(value.summary);
  if (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(asOptionalRecord(summary.apply)?.pipelineName) ?? stringOrNull(asOptionalRecord(summary.render)?.pipelineName),
    sourceCommit: stringOrNull(summary.sourceCommit),
    sourceStageRef: stringOrNull(summary.sourceStageRef),
    elapsedMs: numberOrNull(summary.elapsedMs),
    render: compactRefreshRender(asOptionalRecord(summary.render)),
    apply: compactRefreshApply(asOptionalRecord(summary.apply)),
    sourceAuthority: stringOrNull(summary.sourceAuthority),
    statusAuthority: stringOrNull(summary.statusAuthority),
    parsedDownstreamCliOutput: false,
  };
}

export function followerEvidenceSummary(input: {
  observedSha: string | null;
  livePayload: Record<string, unknown> | null;
  storedCommand: Record<string, unknown> | null;
  fallbackStoredCommand?: Record<string, unknown> | null;
}): Record<string, unknown> | null {
  const livePayload = input.livePayload;
  const storedPayload = asOptionalRecord(input.storedCommand?.payload);
  const fallbackStoredPayload = asOptionalRecord(input.fallbackStoredCommand?.payload);
  const tekton = firstRecord(asOptionalRecord(livePayload?.tekton), asOptionalRecord(storedPayload?.tekton), asOptionalRecord(fallbackStoredPayload?.tekton));
  const pipeline = firstRecord(asOptionalRecord(livePayload?.pipeline), asOptionalRecord(storedPayload?.pipeline), asOptionalRecord(fallbackStoredPayload?.pipeline));
  const refresh = firstRecord(asOptionalRecord(storedPayload?.refreshEvidence), asOptionalRecord(fallbackStoredPayload?.refreshEvidence), asOptionalRecord(livePayload?.refreshEvidence));
  if (tekton === null && pipeline === null && refresh === null) return null;
  const pipelineRefName = stringOrNull(tekton?.pipelineRefName);
  const pipelineName = stringOrNull(asOptionalRecord(pipeline?.metadata)?.name);
  const refreshRender = asOptionalRecord(refresh?.render);
  const refreshApply = asOptionalRecord(refresh?.apply);
  const refreshRenderedPipeline = stringOrNull(refreshRender?.pipelineName);
  const refreshPipeline = stringOrNull(refreshApply?.pipelineName) ?? stringOrNull(refresh?.pipeline);
  const refreshSourceCommit = stringOrNull(refresh?.sourceCommit);
  return {
    pipelineRunRefName: pipelineRefName,
    pipeline: compactPipelineEvidence(pipeline),
    refreshBoundedReason: refresh === null ? "missing-control-plane-refresh-capability-evidence; status refresh job is reported separately at top-level refresh/liveRefresh" : null,
    refresh: refresh === null
      ? null
      : {
          ...refresh,
          pipelineRefMatches: pipelineRefName === null || refreshPipeline === null ? null : pipelineRefName === refreshPipeline,
          pipelineSpecMatches: pipelineName === null || refreshPipeline === null ? null : pipelineName === refreshPipeline,
          renderedPipelineMatchesApplied: refreshRenderedPipeline === null || refreshPipeline === null ? null : refreshRenderedPipeline === refreshPipeline,
          sourceCommitMatches: input.observedSha === null || refreshSourceCommit === null ? null : input.observedSha === refreshSourceCommit,
        },
  };
}

function compactPipelineEvidence(value: Record<string, unknown> | null): Record<string, unknown> | null {
  if (value === null) return null;
  const metadata = asOptionalRecord(value.metadata);
  const spec = asOptionalRecord(value.spec);
  return {
    metadata: { name: stringOrNull(metadata?.name) },
    spec: {
      runtimeReadyTask: compactRefreshRuntimeReady(asOptionalRecord(spec?.runtimeReadyTask)),
    },
  };
}

function compactRefreshRender(value: Record<string, unknown> | null): Record<string, unknown> | null {
  if (value === null) return null;
  return {
    pipelineName: stringOrNull(value.pipelineName),
    taskCount: numberOrNull(value.taskCount),
    runtimeReadyTask: compactRefreshRuntimeReady(asOptionalRecord(value.runtimeReadyTask)),
  };
}

function compactRefreshApply(value: Record<string, unknown> | null): Record<string, unknown> | null {
  if (value === null) return null;
  return {
    pipelineName: stringOrNull(value.pipelineName),
    namespace: stringOrNull(value.namespace),
    resourceVersion: stringOrNull(value.resourceVersion),
    annotations: compactStringMap(asOptionalRecord(value.annotations)),
    labels: compactStringMap(asOptionalRecord(value.labels)),
    degradedReason: stringOrNull(value.degradedReason),
  };
}

function compactRefreshRuntimeReady(value: Record<string, unknown> | null): Record<string, unknown> | null {
  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: unknown, limit: number): Array<Record<string, unknown>> {
  return Array.isArray(value)
    ? value
      .map((item) => asOptionalRecord(item))
      .filter((item): item is Record<string, unknown> => item !== null)
      .slice(0, limit)
      .map((item) => ({
        input: stringOrNull(item.input),
        operator: stringOrNull(item.operator),
        values: compactStringArray(item.values, 4),
      }))
    : [];
}

function compactStringMap(value: Record<string, unknown> | null): Record<string, string> | null {
  if (value === null) return null;
  const output: Record<string, string> = {};
  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 firstRecord(...values: Array<Record<string, unknown> | null>): Record<string, unknown> | null {
  for (const value of values) {
    if (value !== null) return value;
  }
  return null;
}

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

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

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

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

function compactStringArray(value: unknown, limit: number): string[] {
  return Array.isArray(value) ? value.map((item) => stringOrNull(item)).filter((item): item is string => item !== null).slice(0, limit) : [];
}
