import { z } from "zod";

import { publishPlatformSchema } from "../domain/validation";
import type {
  CalibrationDirection,
  CalibrationEvent,
  PerformanceMetrics,
  PerformanceSnapshot,
  PerformanceSource,
  PublicationRecord,
  RubricVersion,
  ScoringRun,
} from "./types";

const optionalTextSchema = z
  .preprocess((value) => (value == null ? "" : value), z.string())
  .transform((value) => value.trim());

function optionalMetric(value: unknown) {
  if (value == null || value === "") return undefined;
  const numberValue = typeof value === "number" ? value : Number(value);
  return Number.isFinite(numberValue) && numberValue >= 0 ? numberValue : value;
}

const metricsShape = {
  views: z.preprocess(optionalMetric, z.number().nonnegative().optional()),
  impressions: z.preprocess(optionalMetric, z.number().nonnegative().optional()),
  clicks: z.preprocess(optionalMetric, z.number().nonnegative().optional()),
  inquiries: z.preprocess(optionalMetric, z.number().nonnegative().optional()),
  likes: z.preprocess(optionalMetric, z.number().nonnegative().optional()),
  comments: z.preprocess(optionalMetric, z.number().nonnegative().optional()),
  shares: z.preprocess(optionalMetric, z.number().nonnegative().optional()),
  saves: z.preprocess(optionalMetric, z.number().nonnegative().optional()),
  average_position: z.preprocess(optionalMetric, z.number().nonnegative().optional()),
};

export const performanceMetricsSchema = z
  .object(metricsShape)
  .transform(
    (metrics) =>
      Object.fromEntries(
        Object.entries(metrics).filter(([, value]) => value !== undefined),
      ) as PerformanceMetrics,
  );

export const publicationInputSchema = z.object({
  platform: publishPlatformSchema,
  url: z.string().trim().url(),
  published_at: z.string().datetime(),
  notes: optionalTextSchema.default(""),
});

export const manualPerformanceInputSchema = z
  .object({
    window_label: z.string().trim().min(1),
    feedback_summary: optionalTextSchema.default(""),
    raw_reference: optionalTextSchema.optional(),
    ...metricsShape,
  })
  .transform(({ window_label, feedback_summary, raw_reference, ...metrics }) => ({
    window_label,
    feedback_summary,
    raw_reference: raw_reference || undefined,
    metrics: performanceMetricsSchema.parse(metrics),
  }));

function isPerformanceSource(value: unknown): value is PerformanceSource {
  return (
    value === "manual" ||
    (typeof value === "string" &&
      value.startsWith("adapter:") &&
      value.length > "adapter:".length)
  );
}

function rejectUnsafeRawReference(value: string | undefined) {
  if (!value) return value;
  if (/cookie|sessionid|token|secret|api[_-]?key|authorization/i.test(value)) {
    throw new Error("raw_reference must not contain credentials");
  }
  return value;
}

export const rubricVersionSchema = z.object({
  id: z.string().trim().min(1),
  version: z.string().trim().min(1),
  name: z.string().trim().min(1),
  dimensions: z.array(
    z.object({
      id: z.string().trim().min(1),
      label: z.string().trim().min(1),
      weight: z.number().positive(),
      description: z.string().trim().min(1),
    }),
  ),
  formula: z.literal("weighted_average_0_to_10"),
  is_active: z.boolean(),
  created_at: z.string().datetime(),
}) satisfies z.ZodType<RubricVersion>;

export const scoringRunSchema = z.object({
  id: z.string().trim().min(1),
  job_id: z.string().trim().min(1),
  revision: z.number().int().positive(),
  rubric_version_id: z.string().trim().min(1),
  dimension_scores: z.record(z.string(), z.number().min(0).max(5)),
  composite_score: z.number().min(0).max(10),
  rationale: z.string().trim(),
  created_at: z.string().datetime(),
}) satisfies z.ZodType<ScoringRun>;

export const publicationRecordSchema = z.object({
  id: z.string().trim().min(1),
  job_id: z.string().trim().min(1),
  revision: z.number().int().positive(),
  platform: publishPlatformSchema,
  url: z.string().trim().url(),
  published_at: z.string().datetime(),
  status: z.enum(["draft", "published", "archived"]),
  notes: z.string().trim(),
  created_at: z.string().datetime(),
  updated_at: z.string().datetime(),
}) satisfies z.ZodType<PublicationRecord>;

export const performanceSnapshotSchema = z
  .object({
    id: z.string().trim().min(1),
    publication_id: z.string().trim().min(1),
    source: z.custom<PerformanceSource>(isPerformanceSource),
    window_label: z.string().trim().min(1),
    metrics: performanceMetricsSchema,
    feedback_summary: z.string().trim(),
    raw_reference: z.string().trim().optional(),
    snapshot_at: z.string().datetime(),
  })
  .transform((snapshot) => ({
    ...snapshot,
    raw_reference: rejectUnsafeRawReference(snapshot.raw_reference),
  })) satisfies z.ZodType<PerformanceSnapshot>;

export const calibrationDirectionSchema = z.enum([
  "better_than_expected",
  "as_expected",
  "worse_than_expected",
  "needs_more_data",
]) satisfies z.ZodType<CalibrationDirection>;

export const calibrationEventSchema = z.object({
  id: z.string().trim().min(1),
  publication_id: z.string().trim().min(1),
  scoring_run_id: z.string().trim().min(1),
  performance_snapshot_id: z.string().trim().min(1),
  direction: calibrationDirectionSchema,
  observations: z.array(z.string().trim().min(1)),
  recommended_action: z.string().trim().min(1),
  created_at: z.string().datetime(),
}) satisfies z.ZodType<CalibrationEvent>;
