export type NormativeDocumentType = "BASES_LICITACION" | "CONVOCATORIA" | "ANEXOS" | "REGLAMENTO" | "LEY" | "OTRO";

export type NormativeAnalysisInput = {
  text: string;
  fileName: string;
  documentType: NormativeDocumentType;
  issuingEntity: string | null;
  methodUsed: "direct" | "ocr";
  numPages: number;
  warnings: string[];
};

export type NormativeSeverity = "alto" | "medio" | "bajo";
export type NormativePriority = "alta" | "media" | "baja";

export type NormativeChecklistItem = {
  title: string;
  description: string;
};

export type NormativeCriticalItem = NormativeChecklistItem & {
  severity: NormativeSeverity;
};

export type NormativeLockInItem = {
  title: string;
  impact: string;
  recommendation: string;
};

export type NormativePreparationRecommendation = {
  title: string;
  description: string;
  priority: NormativePriority;
};

export type NormativeSourceTraceabilityItem = {
  documentType: NormativeDocumentType;
  sourceLabel: string;
  fields: string[];
};

export type NormativeAnalysisResult = {
  executiveSummary: string;
  participationViability: {
    score: number;
    level: "alta" | "media" | "baja";
    rationale: string;
  };
  risk: {
    level: NormativeSeverity;
    description: string;
  };
  requiredChecklist: NormativeChecklistItem[];
  criticalRequirements: NormativeCriticalItem[];
  lockIns: NormativeLockInItem[];
  preparationRecommendations: NormativePreparationRecommendation[];
  officialFormats: string[];
  valueForMoney: {
    score: number;
    signals: string[];
    actions: string[];
  };
  circularEconomy: {
    applicable: boolean;
    signals: string[];
    actions: string[];
  };
  sourceTraceability: NormativeSourceTraceabilityItem[];
  confidence: "low" | "medium" | "high";
};

const MAX_LINE_LENGTH = 260;
const MAX_CHECKLIST_ITEMS = 15;
const MAX_CRITICAL_ITEMS = 5;
const MAX_LOCKINS = 3;

const requirementPattern =
  /\b(deber[áa]n?|obligatori[oa]s?|requisit[oa]s?|se requiere|se solicita|presentar|entregar|acreditar|cumplir|debe|deber[áa]\s+contar|anexar|adjuntar|constancia)\b/i;
const riskPattern =
  /\b(descalific|sanci[oó]n|penaliz|incumpl|inhabilit|rescisi[oó]n|causal|desechad|inviable|impugnable|no\s+se\s+acepta|motivo\s+de\s+desechamiento)\w*/i;
const criticalPattern =
  /\b(cualquier documento|no se acepta|imposible|obligatorio|causa de descalificaci[oó]n|deber[áa]|ser[áa]\s+descalificad[oa]|desechamiento)\b/i;
const lockInPattern =
  /\b(board member|leadership|dmtf|oem|distribuidor autorizado|fabricante|propietari[oa]|certificaci[oó]n exclusiva|marca(?:\s+determinada)?|patente|licencia propietaria|exclusiv[oa]|unico proveedor|[uú]nica marca|solamente|equivalente no aceptado)\b/i;
const deadlinePattern = /\b(cierre|fecha l[ií]mite|l[ií]mite de|apertura|junta|fallo|calendario|cronograma|vigencia|plazo|entrega)\b/i;
const formatPattern = /\b(anexo|formato)\b/i;

const datePattern =
  /\b\d{1,2}[\/\-]\d{1,2}[\/\-]\d{2,4}\b|\b\d{1,2}\s+de\s+[a-zA-Záéíóúñ]+\s+de\s+\d{4}\b|\b\d{4}[\/\-]\d{2}[\/\-]\d{2}\b/;

const defaultFormatTemplates = [
  "Acredita la existencia legal del licitante y las facultades de su representante para presentar la propuesta y firmar el contrato.",
  "Carta de compromiso donde el licitante acepta someterse a reglas, requisitos y especificaciones de la licitacion.",
  "Manifiesto de conocimiento y aceptacion de bases y aclaraciones.",
  "Declaracion bajo protesta de decir verdad de no estar en supuestos de inhabilitacion.",
  "Compromiso de conducirse honestamente y abstenerse de conductas para obtener ventajas indebidas.",
  "Manifestacion de contar con politica de integridad (personas morales).",
  "Declaracion de propuesta independiente y sin colusion.",
  "Declaracion sobre procedencia extranjera de bienes ofertados (si aplica).",
  "Declaracion sobre procedencia nacional de bienes o servicios ofertados.",
  "Formato de clasificacion de empresa (micro, pequena o mediana).",
  "Checklist de documentos entregados por el licitante.",
  "Declaracion de inexistencia de conflicto de interes del representante, socios o accionistas.",
  "Declaracion de no emitir comprobantes fiscales sin activos para prestar servicios (no EFOS).",
];

function cleanText(value: string) {
  return value.replace(/\u00a0/g, " ").replace(/\r/g, "\n").replace(/[ \t]+/g, " ").trim();
}

function compactLine(value: string) {
  const compact = value.replace(/\s+/g, " ").trim();
  if (!compact) {
    return null;
  }

  if (compact.length > MAX_LINE_LENGTH) {
    return `${compact.slice(0, MAX_LINE_LENGTH - 1)}...`;
  }

  return compact;
}

function splitLines(text: string) {
  return text
    .split("\n")
    .map((line) => compactLine(line))
    .filter((line): line is string => Boolean(line));
}

function splitSentences(text: string) {
  return text
    .split(/(?<=[.!?])\s+/)
    .map((sentence) => compactLine(sentence))
    .filter((sentence): sentence is string => Boolean(sentence));
}

function toUnique<T>(values: T[], keySelector: (value: T) => string, maxItems = Number.POSITIVE_INFINITY) {
  const seen = new Set<string>();
  const result: T[] = [];

  for (const value of values) {
    const key = keySelector(value);
    if (seen.has(key)) {
      continue;
    }
    seen.add(key);
    result.push(value);

    if (result.length >= maxItems) {
      break;
    }
  }

  return result;
}

function clamp(value: number, min: number, max: number) {
  return Math.max(min, Math.min(max, value));
}

function compactWords(value: string, maxWords: number) {
  const words = value
    .replace(/[•·]/g, " ")
    .replace(/^[-–—\s]+/, "")
    .trim()
    .split(/\s+/)
    .filter(Boolean);

  return words.slice(0, maxWords).join(" ");
}

function titleFromLine(line: string) {
  const normalized = line
    .replace(/^[0-9]+[.)]\s*/, "")
    .replace(/^[-–—]+\s*/, "")
    .replace(/\.$/, "")
    .trim();

  if (normalized.length <= 80) {
    return normalized;
  }

  return `${compactWords(normalized, 8)}...`;
}

function buildExecutiveSummary(
  sentences: string[],
  documentType: NormativeDocumentType,
  requiredChecklist: NormativeChecklistItem[],
  criticalRequirements: NormativeCriticalItem[],
  lockIns: NormativeLockInItem[],
) {
  const intro =
    sentences
      .filter((sentence) => requirementPattern.test(sentence) || deadlinePattern.test(sentence) || datePattern.test(sentence))
      .slice(0, 3)
      .join(" ") ||
    sentences.slice(0, 2).join(" ") ||
    "No se detecto suficiente contenido para construir un resumen ejecutivo robusto.";

  const typeLabel = documentType.toLowerCase().replace("_", " ");
  const metrics = `Se identificaron ${requiredChecklist.length} requisitos obligatorios, ${criticalRequirements.length} puntos criticos y ${lockIns.length} posibles candados.`;

  return `Analisis de ${typeLabel}: ${intro} ${metrics}`;
}

function buildRequiredChecklist(lines: string[]) {
  const candidates = lines.filter((line) => requirementPattern.test(line) || deadlinePattern.test(line));

  return toUnique(
    candidates.map((line) => ({
      title: titleFromLine(line),
      description: line,
    })),
    (item) => item.title.toLowerCase(),
    MAX_CHECKLIST_ITEMS,
  );
}

function criticalSeverity(line: string): NormativeSeverity {
  if (riskPattern.test(line) || /\b(cualquier|imposible|descalificacion|inviable|obligatorio)\b/i.test(line)) {
    return "alto";
  }

  if (/\b(restring|limit|vigencia|garantia|fianza|solvencia|capacidad financiera)\b/i.test(line)) {
    return "medio";
  }

  return "bajo";
}

function buildCriticalRequirements(lines: string[], requiredChecklist: NormativeChecklistItem[]) {
  const explicit = lines.filter((line) => riskPattern.test(line) || criticalPattern.test(line));

  const source = explicit.length > 0 ? explicit : requiredChecklist.slice(0, 6).map((item) => item.description);

  return toUnique(
    source.map((line) => ({
      title: titleFromLine(line),
      description: line,
      severity: criticalSeverity(line),
    })),
    (item) => item.title.toLowerCase(),
    MAX_CRITICAL_ITEMS,
  );
}

function recommendationForLockIn(line: string) {
  const normalized = line.toLowerCase();

  if (normalized.includes("dmtf") || normalized.includes("board member") || normalized.includes("leadership")) {
    return "Solicitar en Junta de Aclaraciones una alternativa equivalente que no restrinja indebidamente la competencia.";
  }

  if (normalized.includes("oem") || normalized.includes("fabricante") || normalized.includes("distribuidor autorizado")) {
    return "Iniciar de inmediato negociacion de respaldo con OEM y preparar evidencia documental del acuerdo.";
  }

  if (normalized.includes("propietari") || normalized.includes("marca")) {
    return "Evaluar impugnacion tecnica o solicitud de aclaracion para permitir equivalencias funcionales certificadas.";
  }

  return "Preparar pregunta tecnica en Junta de Aclaraciones para justificar proporcionalidad del requisito.";
}

function buildLockIns(lines: string[], criticalRequirements: NormativeCriticalItem[]) {
  const lockinLines = lines.filter((line) => lockInPattern.test(line));
  const source = lockinLines.length > 0 ? lockinLines : criticalRequirements.filter((item) => item.severity === "alto").map((item) => item.description);

  return toUnique(
    source.map((line) => ({
      title: titleFromLine(line),
      impact: line,
      recommendation: recommendationForLockIn(line),
    })),
    (item) => item.title.toLowerCase(),
    MAX_LOCKINS,
  );
}

function buildPreparationRecommendations(riskLevel: NormativeSeverity, lockIns: NormativeLockInItem[]) {
  const recommendations: NormativePreparationRecommendation[] = [
    {
      title: "Crear un cuarto de guerra con checklists exhaustivos",
      description:
        "Asignar responsables por paquete documental y ejecutar doble revision de cada requisito antes de integrar la propuesta final.",
      priority: "alta",
    },
    {
      title: "Asegurar la carta de respaldo del fabricante (OEM)",
      description:
        "Gestionar respaldo oficial del fabricante para cubrir requisitos de distribuidor autorizado, garantias y despliegue tecnico.",
      priority: lockIns.length > 0 ? "alta" : "media",
    },
    {
      title: "Preparar una Junta de Aclaraciones estrategica",
      description:
        "Presentar preguntas puntuales sobre requisitos restrictivos y proponer alternativas tecnicas equivalentes para ampliar competencia.",
      priority: riskLevel === "alto" ? "alta" : "media",
    },
    {
      title: "Validacion financiera y de garantias por adelantado",
      description:
        "Tramitar con anticipacion garantias de seriedad y evidencia de capacidad financiera para evitar retrasos de ultimo momento.",
      priority: "media",
    },
    {
      title: "Enfoque en propuesta de valor y sostenibilidad",
      description:
        "Destacar certificaciones, cumplimiento tecnico y costo total de propiedad para fortalecer la evaluacion integral de la oferta.",
      priority: "media",
    },
    {
      title: "Documentar la solucion de software de gestion",
      description:
        "Alinear fichas tecnicas y evidencia funcional con cada punto del anexo tecnico para demostrar cumplimiento completo.",
      priority: riskLevel === "alto" ? "alta" : "media",
    },
  ];

  return recommendations;
}

function buildOfficialFormats(lines: string[]) {
  const detected = lines
    .filter((line) => formatPattern.test(line))
    .map((line) => line.replace(/\.$/, "").trim())
    .filter((line) => line.length >= 30);

  const uniqueDetected = toUnique(detected, (item) => item.toLowerCase(), 20);

  const countHintMatch = lines.join(" ").match(/\b(\d{1,2})\s+formatos?\b/i);
  const hintedCount = countHintMatch ? Number.parseInt(countHintMatch[1], 10) : null;
  const targetCount = hintedCount && Number.isFinite(hintedCount) ? clamp(hintedCount, 6, 20) : 13;

  const merged = [...uniqueDetected];

  for (const template of defaultFormatTemplates) {
    if (merged.length >= targetCount) {
      break;
    }

    if (!merged.some((item) => item.toLowerCase() === template.toLowerCase())) {
      merged.push(template);
    }
  }

  return merged.slice(0, targetCount);
}

function buildValueForMoney(lines: string[], viabilityScore: number, riskLevel: NormativeSeverity) {
  const signals = toUnique(
    lines.filter((line) => /\bcosto\b|\bprecio\b|\bcalidad\b|\beficiencia\b|\bgarantia\b|\bciclo de vida\b|\btotal\b/i.test(line)),
    (line) => line.toLowerCase(),
    6,
  );

  const actions = [
    "Incluir costo total de propiedad (CAPEX + OPEX) con supuestos verificables.",
    "Demostrar relacion calidad-precio con matriz comparativa y riesgos mitigados.",
    "Presentar indicadores de eficiencia operativa y garantia de desempeno.",
  ];

  const signalBonus = Math.min(18, signals.length * 3);
  const riskPenalty = riskLevel === "alto" ? 18 : riskLevel === "medio" ? 10 : 4;
  const score = Math.round(clamp(viabilityScore + signalBonus - riskPenalty, 25, 96));

  return {
    score,
    signals: signals.length > 0 ? signals : ["No se detectaron señales explicitas; se recomienda justificar costo total y eficiencia con evidencia."],
    actions,
  };
}

function buildCircularEconomy(lines: string[]) {
  const signals = toUnique(
    lines.filter((line) => /\bcircular\b|\brecicl|reutiliz|remanufact|residu|huella|emision|sustent|sostenib/i.test(line)),
    (line) => line.toLowerCase(),
    6,
  );

  if (signals.length === 0) {
    return {
      applicable: false,
      signals: ["No se encontraron criterios ambientales explicitos en el texto analizado."],
      actions: ["Agregar propuesta voluntaria de economia circular (reuso, reciclaje, disposicion responsable) para diferenciar oferta."],
    };
  }

  return {
    applicable: true,
    signals,
    actions: [
      "Vincular criterios de reutilizacion/reciclaje con entregables y metrica de cumplimiento.",
      "Incluir plan de manejo de residuos y evidencia documental de trazabilidad.",
      "Cuantificar reduccion de impacto ambiental (energia, emisiones o materiales).",
    ],
  };
}

function buildSourceTraceability(args: {
  documentType: NormativeDocumentType;
  requiredChecklist: NormativeChecklistItem[];
  criticalRequirements: NormativeCriticalItem[];
  lockIns: NormativeLockInItem[];
  officialFormats: string[];
}): NormativeSourceTraceabilityItem[] {
  const fields: string[] = [];
  if (args.requiredChecklist.length > 0) {
    fields.push("requiredChecklist");
  }
  if (args.criticalRequirements.length > 0) {
    fields.push("criticalRequirements");
  }
  if (args.lockIns.length > 0) {
    fields.push("lockIns");
  }
  if (args.officialFormats.length > 0) {
    fields.push("officialFormats");
  }

  const sourceLabel =
    args.documentType === "BASES_LICITACION"
      ? "Bases"
      : args.documentType === "CONVOCATORIA"
        ? "Convocatoria"
        : args.documentType === "ANEXOS"
          ? "Anexos"
        : args.documentType === "REGLAMENTO"
          ? "Reglamento"
          : args.documentType === "LEY"
            ? "Ley"
            : "Documento";

  return [
    {
      documentType: args.documentType,
      sourceLabel,
      fields,
    },
  ];
}

function getConfidence(textLength: number, findingsCount: number) {
  if (textLength >= 3500 && findingsCount >= 14) {
    return "high" as const;
  }

  if (textLength >= 1200 && findingsCount >= 7) {
    return "medium" as const;
  }

  return "low" as const;
}

function getRiskLevel(criticalRequirements: NormativeCriticalItem[], lockIns: NormativeLockInItem[]) {
  const highCount = criticalRequirements.filter((item) => item.severity === "alto").length;
  const mediumCount = criticalRequirements.filter((item) => item.severity === "medio").length;

  if (highCount >= 3 || lockIns.length >= 3) {
    return {
      level: "alto" as const,
      description: "Multiples requisitos complejos o restrictivos.",
    };
  }

  if (highCount >= 1 || mediumCount >= 3 || lockIns.length >= 1) {
    return {
      level: "medio" as const,
      description: "Existen requisitos sensibles que requieren preparacion anticipada.",
    };
  }

  return {
    level: "bajo" as const,
    description: "No se detectan barreras criticas fuera de la preparacion documental normal.",
  };
}

function getViability(requiredCount: number, criticalRequirements: NormativeCriticalItem[], lockIns: NormativeLockInItem[], confidence: "low" | "medium" | "high") {
  const criticalHighCount = criticalRequirements.filter((item) => item.severity === "alto").length;
  const criticalMediumCount = criticalRequirements.filter((item) => item.severity === "medio").length;

  const confidencePenalty = confidence === "low" ? 8 : confidence === "medium" ? 3 : 0;
  const requirementPenalty = Math.max(0, requiredCount - 8) * 1.8;
  const criticalPenalty = criticalHighCount * 8 + criticalMediumCount * 3;
  const lockInPenalty = lockIns.length * 6;

  const score = Math.round(clamp(88 - confidencePenalty - requirementPenalty - criticalPenalty - lockInPenalty, 20, 95));

  if (score >= 75) {
    return {
      score,
      level: "alta" as const,
      rationale: "Viabilidad favorable con una ruta de cumplimiento clara.",
    };
  }

  if (score >= 55) {
    return {
      score,
      level: "media" as const,
      rationale: "Requiere preparacion adicional para reducir riesgo de descalificacion.",
    };
  }

  return {
    score,
    level: "baja" as const,
    rationale: "Riesgo elevado: se recomienda estrategia documental y legal reforzada antes de participar.",
  };
}

export function analyzeNormativeText(input: NormativeAnalysisInput): NormativeAnalysisResult {
  const cleaned = cleanText(input.text);
  const lines = splitLines(cleaned);
  const sentences = splitSentences(cleaned);

  const requiredChecklist = buildRequiredChecklist(lines);
  const criticalRequirements = buildCriticalRequirements(lines, requiredChecklist);
  const lockIns = buildLockIns(lines, criticalRequirements);
  const findingsCount = requiredChecklist.length + criticalRequirements.length + lockIns.length;
  const confidence = getConfidence(cleaned.length, findingsCount);
  const risk = getRiskLevel(criticalRequirements, lockIns);
  const participationViability = getViability(requiredChecklist.length, criticalRequirements, lockIns, confidence);
  const preparationRecommendations = buildPreparationRecommendations(risk.level, lockIns);
  const officialFormats = buildOfficialFormats(lines);
  const valueForMoney = buildValueForMoney(lines, participationViability.score, risk.level);
  const circularEconomy = buildCircularEconomy(lines);
  const sourceTraceability = buildSourceTraceability({
    documentType: input.documentType,
    requiredChecklist,
    criticalRequirements,
    lockIns,
    officialFormats,
  });
  const executiveSummary = buildExecutiveSummary(sentences, input.documentType, requiredChecklist, criticalRequirements, lockIns);

  return {
    executiveSummary,
    participationViability,
    risk,
    requiredChecklist,
    criticalRequirements,
    lockIns,
    preparationRecommendations,
    officialFormats,
    valueForMoney,
    circularEconomy,
    sourceTraceability,
    confidence,
  };
}
