import { z } from "zod";
import {
  defineRecordCodec,
  parseRecord,
  type RecordCodec,
} from "../records/envelope.js";
import {
  decisionScopeSchema,
  normalizeDecisionScope,
  type DecisionScope,
} from "./scope.js";

export const REVIEW_DECISION_VERSION = "review-decision/v1" as const;

const promotionSchema = z
  .object({
    contractId: z.string(),
    contractVersion: z.string(),
  })
  .strict();

export const reviewDecisionSchema = z
  .object({
    schemaVersion: z.literal(REVIEW_DECISION_VERSION),
    id: z.string(),
    findingId: z.string(),
    evidenceFingerprint: z.string(),
    verdict: z.enum(["confirmed_defect", "not_defect"]),
    scope: decisionScopeSchema,
    reviewer: z.string(),
    reviewedAt: z.string(),
    reportId: z.string(),
    supersedes: z.array(z.string()),
    promotion: promotionSchema.optional(),
    reason: z.string().optional(),
  })
  .strict();

export type ReviewDecision = z.infer<typeof reviewDecisionSchema>;

export const reviewDecisionCodec: RecordCodec<ReviewDecision> = defineRecordCodec({
  schema: reviewDecisionSchema,
  currentVersion: REVIEW_DECISION_VERSION,
});

function compareUnicodeScalars(a: string, b: string): number {
  let i = 0;
  let j = 0;

  while (i < a.length && j < b.length) {
    const codeA = a.codePointAt(i);
    const codeB = b.codePointAt(j);
    if (codeA === undefined || codeB === undefined) {
      break;
    }
    if (codeA !== codeB) {
      return codeA < codeB ? -1 : 1;
    }
    i += codeA > 0xffff ? 2 : 1;
    j += codeB > 0xffff ? 2 : 1;
  }

  return a.length - b.length;
}

export function normalizeReviewDecision(decision: ReviewDecision): ReviewDecision {
  const parsed = reviewDecisionSchema.parse(decision);
  return {
    ...parsed,
    scope: normalizeDecisionScope(parsed.scope),
    supersedes: [...parsed.supersedes].sort(compareUnicodeScalars),
  };
}

export function parseReviewDecision(value: unknown): ReviewDecision {
  return normalizeReviewDecision(parseRecord(reviewDecisionCodec, value));
}

export type { DecisionScope };
