import { afterEach, describe, expect, it, vi } from "vitest";
import { analyzeNormativeTextWithAi } from "@/lib/normative-analysis/ai-analyze";

function mockOpenAiResponse(payload: unknown, options?: { ok?: boolean; status?: number }) {
  return {
    ok: options?.ok ?? true,
    status: options?.status ?? 200,
    json: async () => payload,
  };
}

const input = {
  text: "El licitante debera presentar propuesta tecnica y economica. La falta de firma sera causa de descalificacion. Marca exclusiva no aceptada.",
  fileName: "bases.pdf",
  documentType: "BASES_LICITACION" as const,
  issuingEntity: "Municipio",
  methodUsed: "ocr" as const,
  numPages: 5,
  warnings: [],
};

describe("analyzeNormativeTextWithAi", () => {
  afterEach(() => {
    vi.unstubAllGlobals();
    vi.restoreAllMocks();
    delete process.env.OPENAI_API_KEY;
  });

  it("keeps AI engine by normalizing partial JSON-schema responses", async () => {
    process.env.OPENAI_API_KEY = "test-key";

    const fetchMock = vi.fn(async () =>
      mockOpenAiResponse({
        model: "gpt-4.1-mini-test",
        choices: [
          {
            message: {
              content: JSON.stringify({
                analysis: {
                  executiveSummary: "Resumen ejecutivo del documento con observaciones relevantes para tomar decision.",
                  participationViability: { score: 72, level: "media", rationale: "Requiere atencion documental." },
                  risk: { level: "alto", description: "Riesgo por requisitos de descalificacion." },
                  requiredChecklist: [{ title: "Carta", description: "Presentar carta bajo protesta." }],
                  criticalRequirements: [{ title: "Firma", description: "Sin firma se descalifica.", severity: "alto" }],
                  lockIns: [{ title: "Marca", impact: "Se menciona marca exclusiva.", recommendation: "Solicitar equivalencia." }],
                  preparationRecommendations: [{ title: "Plan", description: "Preparar expediente.", priority: "alta" }],
                  officialFormats: ["A", "B", "C"],
                  valueForMoney: { score: 65, signals: ["Precio competitivo"], actions: ["Presentar TCO"] },
                  circularEconomy: { applicable: false, signals: ["No aplica"], actions: ["Mantener cumplimiento"] },
                  sourceTraceability: [{ documentType: "BASES_LICITACION", sourceLabel: "Bases", fields: ["requiredChecklist"] }],
                  confidence: "medium",
                },
              }),
            },
          },
        ],
      }),
    );

    vi.stubGlobal("fetch", fetchMock);

    const output = await analyzeNormativeTextWithAi(input);

    expect(output.engine).toBe("openai_json_schema");
    expect(output.result.requiredChecklist.length).toBeGreaterThan(0);
    expect(output.result.officialFormats.length).toBeGreaterThan(0);
    expect(output.warnings.some((warning) => warning.includes("normalizo respuesta AI parcial"))).toBe(true);
    expect(fetchMock).toHaveBeenCalledTimes(1);
  });

  it("accepts flat json_object responses without analysis envelope", async () => {
    process.env.OPENAI_API_KEY = "test-key";

    const fetchMock = vi
      .fn()
      .mockResolvedValueOnce(
        mockOpenAiResponse(
          {
            error: { message: "schema mode temporary unavailable" },
          },
          { ok: false, status: 500 },
        ),
      )
      .mockResolvedValueOnce(
        mockOpenAiResponse({
          model: "gpt-4.1-mini-test",
          choices: [
            {
              message: {
                content: JSON.stringify({
                  executiveSummary: "Analisis sintetico con hallazgos principales para el area de contratacion.",
                  participationViability: { score: 70, level: "media", rationale: "Hay riesgo controlable." },
                  risk: { level: "medio", description: "Existen condiciones de atencion prioritaria." },
                  requiredChecklist: [{ title: "Documentacion", description: "Presentar requisitos obligatorios." }],
                  criticalRequirements: [{ title: "Plazo", description: "Plazo corto de entrega.", severity: "medio" }],
                  lockIns: [],
                  preparationRecommendations: [{ title: "Checklist", description: "Revisar entregables.", priority: "alta" }],
                  officialFormats: ["Anexo tecnico", "Formato economico"],
                  valueForMoney: { score: 68, signals: ["Calidad-precio"], actions: ["Justificar costo total"] },
                  circularEconomy: { applicable: false, signals: ["Sin criterio explicito"], actions: ["Agregar opcion voluntaria"] },
                  sourceTraceability: [{ documentType: "BASES_LICITACION", sourceLabel: "Bases", fields: ["criticalRequirements"] }],
                  confidence: "medium",
                }),
              },
            },
          ],
        }),
      );

    vi.stubGlobal("fetch", fetchMock);

    const output = await analyzeNormativeTextWithAi(input);

    expect(output.engine).toBe("openai_json_object");
    expect(output.result.requiredChecklist.length).toBeGreaterThan(0);
    expect(output.warnings.some((warning) => warning.includes("JSON object como fallback"))).toBe(true);
    expect(fetchMock).toHaveBeenCalledTimes(2);
  });
});
