import "server-only";

import { z } from "zod";
import { callOpenAiJsonSchema } from "@/lib/ai/openai";
import { storeAiSuggestionFromEnvelope } from "@/lib/ai/suggestions";
import { getLicitationRecommendationsForUser } from "@/lib/licitations/recommendations";
import type { RecommendationResultItem } from "@/lib/licitations/types";

const M3_PROMPT_VERSION = "m3_ai_rerank_v1";
const DEFAULT_TOP_RERANK = 12;
const MAX_TOP_RERANK = 25;

const M3EvaluationSchema = z.object({
  id: z.string().min(1),
  aiScore: z.number().min(0).max(100),
  aiReasons: z.array(z.string().min(5).max(320)).max(5),
  aiRisks: z.array(z.string().min(5).max(320)).max(5),
  nextStep: z.string().min(8).max(300),
  confidence: z.number().min(0).max(1).optional(),
});

const M3AiResponseSchema = z.object({
  evaluations: z.array(M3EvaluationSchema).max(MAX_TOP_RERANK),
  confidence: z.enum(["low", "medium", "high"]).optional(),
});

const M3AiJsonSchema = {
  type: "object",
  additionalProperties: false,
  required: ["evaluations"],
  properties: {
    evaluations: {
      type: "array",
      items: {
        type: "object",
        additionalProperties: false,
        required: ["id", "aiScore", "aiReasons", "aiRisks", "nextStep"],
        properties: {
          id: { type: "string" },
          aiScore: { type: "number", minimum: 0, maximum: 100 },
          aiReasons: {
            type: "array",
            items: { type: "string" },
          },
          aiRisks: {
            type: "array",
            items: { type: "string" },
          },
          nextStep: { type: "string" },
          confidence: { type: "number", minimum: 0, maximum: 1 },
        },
      },
    },
    confidence: { type: "string", enum: ["low", "medium", "high"] },
  },
} as const;

export type AiRecommendationResultItem = RecommendationResultItem & {
  deterministicScore: number;
  aiScore: number;
  blendedScore: number;
  aiReasons: string[];
  aiRisks: string[];
  nextStep: string;
  suggestionId: string | null;
};

export async function getAiEnhancedLicitationRecommendationsForUser(
  userId: string,
  options?: {
    profileId?: string | null;
    top?: number;
  },
) {
  const deterministic = await getLicitationRecommendationsForUser(userId, options?.profileId);
  const requestedTop = Number.isFinite(options?.top) ? Number(options?.top) : DEFAULT_TOP_RERANK;
  const rerankTop = Math.max(1, Math.min(MAX_TOP_RERANK, Math.floor(requestedTop)));

  if (deterministic.results.length === 0) {
    return {
      profileId: deterministic.profileId,
      results: [] as AiRecommendationResultItem[],
      meta: {
        engine: "empty_assist",
        model: null,
        usage: null,
        warnings: [{ code: "no_candidates", message: "No existen licitaciones candidatas para evaluar." }],
        confidence: null,
      },
    };
  }

  const topCandidates = deterministic.results.slice(0, rerankTop);
  const remainingCandidates = deterministic.results.slice(rerankTop);

  const systemPrompt = [
    "Eres un analista senior de licitaciones publicas en Mexico.",
    "Tu tarea es re-evaluar licitaciones ya pre-rankeadas de forma determinista.",
    "No inventes datos externos y responde solo JSON valido en espanol.",
    "aiScore representa ajuste semantico de ajuste estrategico (0 a 100).",
  ].join(" ");

  const userPrompt = [
    "Empresa y ranking determinista previo:",
    JSON.stringify({
      profileId: deterministic.profileId,
      candidates: topCandidates.map((item) => ({
        id: item.id,
        deterministicScore: item.score,
        title: item.title,
        description: item.description,
        amount: item.amount,
        procedureType: item.procedureType,
        category: item.category,
        municipalityName: item.municipalityName,
        stateName: item.stateName,
        source: item.source,
      })),
    }),
    "",
    "Para cada id devuelve:",
    "- aiScore: 0-100.",
    "- aiReasons: 1 a 3 razones concretas.",
    "- aiRisks: 0 a 3 riesgos relevantes para decidir interes.",
    "- nextStep: siguiente accion recomendada para el equipo comercial.",
  ].join("\n");

  const envelope = await callOpenAiJsonSchema({
    promptVersion: M3_PROMPT_VERSION,
    systemPrompt,
    userPrompt,
    outputSchema: M3AiResponseSchema,
    schemaName: "m3_licitation_rerank",
    jsonSchema: M3AiJsonSchema as unknown as Record<string, unknown>,
    model: process.env.OPENAI_M3_MODEL?.trim() || undefined,
  });

  const evaluations = envelope.data?.evaluations ?? [];
  const evaluationById = new Map(evaluations.map((item) => [item.id, item]));

  const enhancedTop = await Promise.all(
    topCandidates.map(async (candidate) => {
      const evaluation = evaluationById.get(candidate.id);
      const aiScore = evaluation ? Math.max(0, Math.min(100, Math.round(evaluation.aiScore))) : candidate.score;
      const blendedScore = Math.round(candidate.score * 0.7 + aiScore * 0.3);
      const aiReasons = evaluation?.aiReasons ?? [];
      const aiRisks = evaluation?.aiRisks ?? [];
      const nextStep = evaluation?.nextStep ?? "Validar terminos y preparar analisis normativo en Modulo 4.";

      const persisted = await storeAiSuggestionFromEnvelope({
        userId,
        moduleKey: "M3",
        featureKey: "licitation_rerank",
        subjectType: "licitation",
        subjectId: candidate.id,
        inputForHash: {
          profileId: deterministic.profileId,
          candidate,
        },
        envelope,
        responsePayload: {
          id: candidate.id,
          deterministicScore: candidate.score,
          aiScore,
          blendedScore,
          aiReasons,
          aiRisks,
          nextStep,
        },
      });

      return {
        ...candidate,
        deterministicScore: candidate.score,
        aiScore,
        blendedScore,
        aiReasons,
        aiRisks,
        nextStep,
        suggestionId: persisted.suggestionId,
      } satisfies AiRecommendationResultItem;
    }),
  );

  const rerankedTop = [...enhancedTop].sort((left, right) => right.blendedScore - left.blendedScore);
  const passthrough = remainingCandidates.map((candidate) => ({
    ...candidate,
    deterministicScore: candidate.score,
    aiScore: candidate.score,
    blendedScore: candidate.score,
    aiReasons: [] as string[],
    aiRisks: [] as string[],
    nextStep: "Completa el perfil para mejorar el analisis de ajuste en futuras recomendaciones.",
    suggestionId: null,
  }));

  return {
    profileId: deterministic.profileId,
    results: [...rerankedTop, ...passthrough],
    meta: {
      engine: envelope.engine,
      model: envelope.model,
      usage: envelope.usage,
      warnings: envelope.warnings,
      confidence: envelope.confidence,
    },
  };
}
