import { z } from "zod";
import { executionContextSchema } from "../../schema/src/records/context.js";
import { findingTargetSchema } from "../../schema/src/records/finding.js";

export const CORPUS_LABEL_VERSION = "corpus-label/v1" as const;

export const labelScopeSchema = z
  .object({
    target: findingTargetSchema,
    context: executionContextSchema,
  })
  .strict();

export type LabelScope = z.infer<typeof labelScopeSchema>;

const plantedLabelSchema = z
  .object({
    id: z.string().min(1),
    detectorClass: z.string().min(1),
    expectedLane: z.enum(["blocking", "advisory"]),
    scope: labelScopeSchema,
    note: z.string().min(1),
  })
  .strict();

export type PlantedLabel = z.infer<typeof plantedLabelSchema>;

const exceptionLabelSchema = z
  .object({
    id: z.string().min(1),
    contractRef: z.string().min(1),
    scope: labelScopeSchema,
  })
  .strict();

export type ExceptionLabel = z.infer<typeof exceptionLabelSchema>;

function labelScopeKey(label: PlantedLabel): string {
  return JSON.stringify({
    detectorClass: label.detectorClass,
    expectedLane: label.expectedLane,
    scope: label.scope,
  });
}

export const corpusLabelSchema = z
  .object({
    schemaVersion: z.literal(CORPUS_LABEL_VERSION),
    fixture: z.string().min(1),
    planted: z.array(plantedLabelSchema),
    exceptions: z.array(exceptionLabelSchema),
  })
  .strict()
  .superRefine((manifest, ctx) => {
    const ids = new Set<string>();
    const scopeKeys = new Set<string>();

    for (const label of [...manifest.planted, ...manifest.exceptions]) {
      if (ids.has(label.id)) {
        ctx.addIssue({
          code: "custom",
          message: `duplicate corpus label ID: ${label.id}`,
        });
      }
      ids.add(label.id);
    }

    for (const label of manifest.planted) {
      const key = labelScopeKey(label);
      if (scopeKeys.has(key)) {
        ctx.addIssue({
          code: "custom",
          message: `ambiguous planted label scope: ${label.id}`,
        });
      }
      scopeKeys.add(key);
    }
  });

export type CorpusLabelManifest = z.infer<typeof corpusLabelSchema>;
