import {
  PAC_AUTOMATIC_DELIVERY_REFERENCE,
  decideCicdDeliveryMutation,
  readCicdDeliveryAuthorityCatalog,
  resolveCicdDeliveryAuthority,
  resolveCicdDeliveryAuthorityFromCatalog,
  type CicdDeliveryAuthority,
  type CicdDeliveryAuthorityCatalog,
  type CicdDeliveryMutationDecision,
} from "../cicd-delivery-authority";
import { authorizeSentinelPacInternalPublish } from "../sentinel-pac-execution-context";
import {
  record,
  rendered,
  sentinelCliSuffix,
  text,
  type SentinelCicdState,
  type WebProbeSentinelOptions,
  type WebProbeSentinelSourceAuthority,
} from "../hwlab-node-web-sentinel-cicd-shared";
import { hwlabRuntimeLaneConfigPath, type HwlabRuntimeLaneSpec } from "../hwlab-node-lanes";
import type { RenderedCliResult } from "../output";

export function webProbeSentinelDeliveryAuthority(spec: HwlabRuntimeLaneSpec): CicdDeliveryAuthority {
  try {
    return webProbeSentinelDeliveryAuthorityFromCatalog(spec, readCicdDeliveryAuthorityCatalog());
  } catch {
    return resolveCicdDeliveryAuthority({
      consumerId: sentinelPacConsumerId(spec),
      node: spec.nodeId,
      lane: spec.lane,
    });
  }
}

export function webProbeSentinelDeliveryAuthorityFromCatalog(
  spec: HwlabRuntimeLaneSpec,
  catalog: CicdDeliveryAuthorityCatalog,
): CicdDeliveryAuthority {
  const consumerId = sentinelPacConsumerId(spec);
  const exactConsumer = resolveCicdDeliveryAuthorityFromCatalog(catalog, {
    consumerId,
    node: spec.nodeId,
    lane: spec.lane,
  });
  if (exactConsumer.kind === "pac-pr-merge") return exactConsumer;

  const stableSentinelIdentity = spec.sourceAuthority?.mode === "giteaSnapshot"
    || catalog.consumers.some((consumer) => consumer.consumerId.toLowerCase() === consumerId.toLowerCase())
    || catalog.consumers.some((consumer) => consumer.consumerId.toLowerCase().startsWith("sentinel-")
      && consumer.node.toLowerCase() === spec.nodeId.toLowerCase()
      && consumer.lane.toLowerCase() === spec.lane.toLowerCase());
  if (stableSentinelIdentity) return exactConsumer;

  return resolveCicdDeliveryAuthorityFromCatalog(catalog, {
    declaredSourceAuthorityMode: spec.sourceAuthority?.mode ?? null,
    declaredConfigRef: hwlabRuntimeLaneConfigPath(),
  });
}

export function sentinelPacConsumerId(spec: HwlabRuntimeLaneSpec): string {
  return `sentinel-${spec.nodeId.toLowerCase()}-${spec.lane}`;
}

export function webProbeSentinelDeliveryMutationDecision(
  spec: HwlabRuntimeLaneSpec,
  command: string,
  action: string,
): CicdDeliveryMutationDecision {
  return decideCicdDeliveryMutation(webProbeSentinelDeliveryAuthority(spec), {
    command,
    statusCommand: `bun scripts/cli.ts web-probe sentinel control-plane status --node ${spec.nodeId} --lane ${spec.lane}`,
    action,
    node: spec.nodeId,
    lane: spec.lane,
  });
}

export function guardedSentinelDeliveryAction(options: WebProbeSentinelOptions): { command: string; action: string } | null {
  if (options.kind === "image" && options.action === "build") {
    return { command: "web-probe sentinel image build", action: "sentinel-image-build" };
  }
  if (options.kind === "control-plane" && options.action === "trigger-current") {
    return { command: "web-probe sentinel control-plane trigger-current", action: "sentinel-control-plane-trigger-current" };
  }
  if (options.kind === "publish" && (options.internalExecution === "pac-controller" || options.manualRecovery)) {
    return { command: "web-probe sentinel publish-current", action: "sentinel-publish-current" };
  }
  return null;
}

export function renderBlockedSentinelDeliveryMutation(
  spec: HwlabRuntimeLaneSpec,
  options: WebProbeSentinelOptions,
  command: string,
  action: string,
): RenderedCliResult | null {
  const decision = webProbeSentinelDeliveryMutationDecision(spec, command, action);
  if (decision.allowed) return null;
  const executionAuthorization = options.kind === "publish" && options.internalExecution === "pac-controller"
    ? authorizeSentinelPacInternalPublish(options, decision.authority)
    : null;
  const result = {
    ...decision.result,
    ...(executionAuthorization === null ? {} : {
      mode: executionAuthorization.mode,
      reason: executionAuthorization.reason,
      capability: executionAuthorization.capability,
    }),
    executionAuthorization,
    next: sentinelReadOnlyNextForSpec(spec, options.sentinelId, decision.authority),
    sentinelId: options.sentinelId,
    valuesRedacted: true,
  };
  const next = record(result.next);
  const fixReference = typeof next.fixManualRelease === "string"
    ? next.fixManualRelease
    : record(next.fixManualRelease).reference ?? PAC_AUTOMATIC_DELIVERY_REFERENCE;
  return Object.assign(
    rendered(false, command, [
      "WEB SENTINEL DELIVERY BLOCKED",
      `mode=${text(result.mode)} mutation=false authority=${text(record(result.deliveryAuthority).kind)}`,
      `status=${text(next.status)}`,
      `history=${text(next.history ?? next.pacHistory)}`,
      `fix=${text(fixReference)}`,
      "GitHub PR merge 只更新 source authority；修复后重新执行 release plan，范围准确时再手动 trigger。",
    ].join("\n")),
    result,
  );
}

export function sentinelReadOnlyNextForSpec(
  spec: HwlabRuntimeLaneSpec,
  sentinelId: string | null,
  authority: CicdDeliveryAuthority,
): Record<string, string> {
  const suffix = sentinelId === null ? "" : ` --sentinel ${sentinelId}`;
  const controlPlaneStatus = `bun scripts/cli.ts web-probe sentinel control-plane status --node ${spec.nodeId} --lane ${spec.lane}${suffix}`;
  const base = {
    status: controlPlaneStatus,
    controlPlaneStatus,
    fixManualRelease: PAC_AUTOMATIC_DELIVERY_REFERENCE,
  };
  if (authority.kind !== "pac-pr-merge") return base;
  const pacHistory = `bun scripts/cli.ts platform-infra pipelines-as-code history --target ${authority.consumer.node} --consumer ${authority.consumer.consumerId} --limit 10`;
  return {
    ...base,
    pacStatus: `bun scripts/cli.ts platform-infra pipelines-as-code status --target ${authority.consumer.node} --consumer ${authority.consumer.consumerId}`,
    pacHistory,
    history: pacHistory,
  };
}

export function sentinelReadOnlyNext(
  state: SentinelCicdState,
  authority: CicdDeliveryAuthority = webProbeSentinelDeliveryAuthority(state.spec),
): Record<string, string> {
  return sentinelReadOnlyNextForSpec(state.spec, state.sentinelId, authority);
}

export function publishCurrentNext(state: SentinelCicdState): Record<string, string> {
  const deliveryAuthority = webProbeSentinelDeliveryAuthority(state.spec);
  if (deliveryAuthority.kind !== "legacy-manual") return sentinelReadOnlyNext(state, deliveryAuthority);
  const node = state.spec.nodeId;
  const lane = state.spec.lane;
  const suffix = sentinelCliSuffix(state);
  const consumer = sentinelPacConsumerId(state.spec);
  const sourceCommit = state.sourceHead.commit === null ? "" : ` --source-commit ${state.sourceHead.commit}`;
  return {
    pacCloseout: `bun scripts/cli.ts platform-infra pipelines-as-code closeout --target ${node} --consumer ${consumer}${sourceCommit} --wait`,
    pacStatus: `bun scripts/cli.ts platform-infra pipelines-as-code status --target ${node} --consumer ${consumer}`,
    pacHistory: `bun scripts/cli.ts platform-infra pipelines-as-code history --target ${node} --consumer ${consumer} --limit 10`,
    manualRecovery: `bun scripts/cli.ts web-probe sentinel publish-current --node ${node} --lane ${lane}${suffix} --confirm --wait --manual-recovery --reason <reason>`,
    controlPlaneStatus: `bun scripts/cli.ts web-probe sentinel control-plane status --node ${node} --lane ${lane}${suffix}`,
    dashboardVerify: `bun scripts/cli.ts web-probe sentinel dashboard verify --node ${node} --lane ${lane}${suffix}`,
    gitMirrorStatus: `bun scripts/cli.ts hwlab nodes git-mirror status --node ${node} --lane ${lane}`,
    gitMirrorSync: `bun scripts/cli.ts hwlab nodes git-mirror sync --node ${node} --lane ${lane} --confirm`,
    gitMirrorFlush: `bun scripts/cli.ts hwlab nodes git-mirror flush --node ${node} --lane ${lane} --confirm --wait`,
  };
}

export function sentinelSourceResolveMode(options: WebProbeSentinelOptions): "cached" | "sync" {
  if (options.kind === "image" && options.action === "build" && options.confirm && options.wait) return "sync";
  if (options.kind === "control-plane" && options.action === "trigger-current" && options.confirm && options.wait) return "sync";
  if (options.kind === "publish" && options.confirm && options.wait) return "sync";
  return "cached";
}

export interface SentinelSourceOverride {
  readonly commit: string;
  readonly stageRef?: string | null;
  readonly mirrorCommit?: string | null;
  readonly sourceAuthority: WebProbeSentinelSourceAuthority;
}

export function sentinelSourceOverrideFromOptions(options: WebProbeSentinelOptions): SentinelSourceOverride | null {
  if (options.kind !== "image" && options.kind !== "control-plane" && options.kind !== "publish") return null;
  const override = options.sourceOverride;
  if (override.sourceCommit === null && override.sourceStageRef === null && override.sourceMirrorCommit === null && override.sourceAuthority === null) return null;
  if (override.sourceCommit === null) throw new Error("--source-commit is required when overriding web-probe sentinel source");
  return {
    commit: override.sourceCommit,
    stageRef: override.sourceStageRef,
    mirrorCommit: override.sourceMirrorCommit,
    sourceAuthority: override.sourceAuthority ?? "git-mirror-snapshot",
  };
}
