import type { ProviderReference } from "@awp/contracts";
import type { Attempt, AttemptSelectionProvenance } from "./model.js";

export function createAttemptSelectionProvenance(
  input: AttemptSelectionProvenance,
): AttemptSelectionProvenance {
  if (input.reason.trim().length === 0) {
    throw new ImmutableAttemptProvenanceError("Attempt selection reason must not be empty");
  }
  if (input.kind === "initial" && input.previousAttemptId !== undefined) {
    throw new ImmutableAttemptProvenanceError(
      "Initial Attempt cannot reference a previous Attempt",
    );
  }
  if (input.kind !== "initial" && input.previousAttemptId === undefined) {
    throw new ImmutableAttemptProvenanceError(
      "Retry/fallback Attempt must reference the previous Attempt",
    );
  }
  return Object.freeze({ ...input });
}

export class ImmutableAttemptProvenanceError extends Error {
  constructor(message = "Attempt selection/provider provenance is immutable") {
    super(message);
    this.name = "ImmutableAttemptProvenanceError";
  }
}

function referenceEquals(
  left: ProviderReference | undefined,
  right: ProviderReference | undefined,
): boolean {
  if (left === undefined || right === undefined) return left === right;
  return (
    left.providerId === right.providerId &&
    left.accountId === right.accountId &&
    left.resourceType === right.resourceType &&
    left.nativeId === right.nativeId &&
    left.nativeRevision === right.nativeRevision
  );
}

export function assertAttemptProvenanceImmutable(previous: Attempt, next: Attempt): void {
  const a = previous.selection;
  const b = next.selection;
  if (
    previous.providerId !== next.providerId ||
    previous.accountId !== next.accountId ||
    previous.model !== next.model ||
    a.kind !== b.kind ||
    a.reason !== b.reason ||
    a.previousAttemptId !== b.previousAttemptId ||
    !referenceEquals(previous.providerReference, next.providerReference)
  ) {
    throw new ImmutableAttemptProvenanceError();
  }
}

export function attemptProviderReference(attempt: Attempt): ProviderReference {
  const reference = attempt.providerReference;
  if (reference === undefined) {
    throw new ImmutableAttemptProvenanceError("Attempt has no bound provider/session reference");
  }
  if (attempt.providerId !== undefined && reference.providerId !== attempt.providerId) {
    throw new ImmutableAttemptProvenanceError(
      "Attempt provider/session reference provider mismatch",
    );
  }
  return reference;
}
