import type {
  NormativeAnalysisResult,
  NormativeCriticalItem,
  NormativeDocumentType,
  NormativeLockInItem,
  NormativePreparationRecommendation,
  NormativeChecklistItem,
  NormativeSourceTraceabilityItem,
} from "@/lib/normative-analysis/analyze";

export type NormativeBundleSourceAnalysis = {
  sourceId: string;
  fileName: string;
  documentType: NormativeDocumentType;
  result: NormativeAnalysisResult;
};

export type NormativeBundleConflict = {
  title: string;
  field: "requiredChecklist" | "criticalRequirements" | "lockIns";
  keptDocumentType: NormativeDocumentType;
  replacedDocumentType: NormativeDocumentType;
};

export type NormativeBundleMergeResult = {
  result: NormativeAnalysisResult;
  sources: Array<{
    sourceId: string;
    fileName: string;
    documentType: NormativeDocumentType;
  }>;
  conflicts: NormativeBundleConflict[];
};

function normalize(value: string) {
  return value
    .normalize("NFD")
    .replace(/[\u0300-\u036f]/g, "")
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, " ")
    .trim();
}

function docPriority(type: NormativeDocumentType) {
  if (type === "BASES_LICITACION") {
    return 5;
  }

  if (type === "CONVOCATORIA") {
    return 4;
  }

  if (type === "ANEXOS") {
    return 3;
  }

  if (type === "REGLAMENTO" || type === "LEY") {
    return 2;
  }

  return 1;
}

function riskWeight(level: NormativeAnalysisResult["risk"]["level"]) {
  if (level === "alto") {
    return 3;
  }

  if (level === "medio") {
    return 2;
  }

  return 1;
}

function dedupeList(items: string[], limit: number) {
  const seen = new Set<string>();
  const output: string[] = [];

  for (const item of items) {
    const normalized = normalize(item);
    if (!normalized || seen.has(normalized)) {
      continue;
    }

    seen.add(normalized);
    output.push(item.trim());

    if (output.length >= limit) {
      break;
    }
  }

  return output;
}

function mergeByTitle<TItem extends { title: string }>(args: {
  field: "requiredChecklist" | "criticalRequirements" | "lockIns";
  sources: NormativeBundleSourceAnalysis[];
  readItems: (result: NormativeAnalysisResult) => TItem[];
}): { items: TItem[]; conflicts: NormativeBundleConflict[] } {
  const chosen = new Map<string, { item: TItem; documentType: NormativeDocumentType; priority: number }>();
  const conflicts: NormativeBundleConflict[] = [];

  for (const source of args.sources) {
    const items = args.readItems(source.result);
    const priority = docPriority(source.documentType);

    for (const item of items) {
      const signature = normalize(item.title);
      if (!signature) {
        continue;
      }

      const previous = chosen.get(signature);
      if (!previous) {
        chosen.set(signature, { item, documentType: source.documentType, priority });
        continue;
      }

      if (priority > previous.priority) {
        conflicts.push({
          title: item.title,
          field: args.field,
          keptDocumentType: source.documentType,
          replacedDocumentType: previous.documentType,
        });
        chosen.set(signature, { item, documentType: source.documentType, priority });
      }
    }
  }

  const items = Array.from(chosen.values())
    .sort((left, right) => right.priority - left.priority)
    .map((entry) => entry.item);

  return {
    items,
    conflicts,
  };
}

function toConfidence(rank: number): NormativeAnalysisResult["confidence"] {
  if (rank >= 2.4) {
    return "high";
  }

  if (rank >= 1.4) {
    return "medium";
  }

  return "low";
}

function confidenceRank(value: NormativeAnalysisResult["confidence"]) {
  if (value === "high") {
    return 3;
  }

  if (value === "medium") {
    return 2;
  }

  return 1;
}

function mergeExecutiveSummary(sources: NormativeBundleSourceAnalysis[]) {
  const sorted = [...sources].sort((left, right) => docPriority(right.documentType) - docPriority(left.documentType));
  const first = sorted[0];
  if (!first) {
    return "Sin analisis disponible.";
  }

  const suffix = sorted.length > 1 ? ` Se fusionaron ${sorted.length} documentos priorizando Bases.` : "";
  return `${first.result.executiveSummary}${suffix}`.trim();
}

function mergeOfficialFormats(sources: NormativeBundleSourceAnalysis[]) {
  const ordered = [...sources].sort((left, right) => docPriority(right.documentType) - docPriority(left.documentType));
  const formats = ordered.flatMap((source) => source.result.officialFormats);
  return dedupeList(formats, 30);
}

function mergePreparations(sources: NormativeBundleSourceAnalysis[]) {
  const all: NormativePreparationRecommendation[] = [];
  const seen = new Set<string>();

  for (const source of sources) {
    for (const item of source.result.preparationRecommendations) {
      const signature = normalize(item.title);
      if (!signature || seen.has(signature)) {
        continue;
      }
      seen.add(signature);
      all.push(item);
    }
  }

  return all.slice(0, 12);
}

function mergeSourceTraceability(sources: NormativeBundleSourceAnalysis[]) {
  const traceability: NormativeSourceTraceabilityItem[] = [];

  for (const source of sources) {
    const fromResult = source.result.sourceTraceability.map((item) => ({
      ...item,
      documentType: item.documentType,
    }));

    if (fromResult.length > 0) {
      traceability.push(...fromResult);
      continue;
    }

    traceability.push({
      documentType: source.documentType,
      sourceLabel: source.fileName,
      fields: ["requiredChecklist", "criticalRequirements", "officialFormats"],
    });
  }

  return traceability;
}

function mergeChecklist(sources: NormativeBundleSourceAnalysis[]) {
  return mergeByTitle<NormativeChecklistItem>({
    field: "requiredChecklist",
    sources,
    readItems: (result) => result.requiredChecklist,
  });
}

function mergeCritical(sources: NormativeBundleSourceAnalysis[]) {
  return mergeByTitle<NormativeCriticalItem>({
    field: "criticalRequirements",
    sources,
    readItems: (result) => result.criticalRequirements,
  });
}

function mergeLockIns(sources: NormativeBundleSourceAnalysis[]) {
  return mergeByTitle<NormativeLockInItem>({
    field: "lockIns",
    sources,
    readItems: (result) => result.lockIns,
  });
}

export function mergeNormativeBundleSources(sources: NormativeBundleSourceAnalysis[]): NormativeBundleMergeResult {
  if (sources.length === 0) {
    const empty: NormativeAnalysisResult = {
      executiveSummary: "Sin analisis disponible.",
      participationViability: {
        score: 0,
        level: "baja",
        rationale: "Sin datos para estimar viabilidad.",
      },
      risk: {
        level: "alto",
        description: "Sin datos para estimar riesgo.",
      },
      requiredChecklist: [],
      criticalRequirements: [],
      lockIns: [],
      preparationRecommendations: [],
      officialFormats: [],
      valueForMoney: {
        score: 0,
        signals: [],
        actions: [],
      },
      circularEconomy: {
        applicable: false,
        signals: [],
        actions: [],
      },
      sourceTraceability: [],
      confidence: "low",
    };

    return {
      result: empty,
      sources: [],
      conflicts: [],
    };
  }

  const viabilityWeighted = sources.reduce((sum, source) => sum + source.result.participationViability.score * docPriority(source.documentType), 0);
  const viabilityWeightTotal = sources.reduce((sum, source) => sum + docPriority(source.documentType), 0);
  const viabilityScore = Math.round(viabilityWeighted / Math.max(1, viabilityWeightTotal));

  const highestRisk = sources
    .map((source) => source.result.risk)
    .sort((left, right) => riskWeight(right.level) - riskWeight(left.level))[0];

  const confidenceAvg = sources.reduce((sum, source) => sum + confidenceRank(source.result.confidence), 0) / Math.max(1, sources.length);

  const checklist = mergeChecklist(sources);
  const critical = mergeCritical(sources);
  const lockIns = mergeLockIns(sources);

  const valueForMoneyScore = Math.round(
    sources.reduce((sum, source) => sum + source.result.valueForMoney.score * docPriority(source.documentType), 0) / Math.max(1, viabilityWeightTotal),
  );
  const valueForMoneySignals = dedupeList(sources.flatMap((source) => source.result.valueForMoney.signals), 12);
  const valueForMoneyActions = dedupeList(sources.flatMap((source) => source.result.valueForMoney.actions), 12);

  const circularSignals = dedupeList(sources.flatMap((source) => source.result.circularEconomy.signals), 12);
  const circularActions = dedupeList(sources.flatMap((source) => source.result.circularEconomy.actions), 12);
  const circularApplicable = sources.some((source) => source.result.circularEconomy.applicable);

  const mergedResult: NormativeAnalysisResult = {
    executiveSummary: mergeExecutiveSummary(sources),
    participationViability: {
      score: viabilityScore,
      level: viabilityScore >= 75 ? "alta" : viabilityScore >= 55 ? "media" : "baja",
      rationale: "Resultado consolidado de bundle documental (Bases/Convocatoria/Anexos) con prioridad por fuente.",
    },
    risk: highestRisk ?? {
      level: "alto",
      description: "Sin datos",
    },
    requiredChecklist: checklist.items.slice(0, 30),
    criticalRequirements: critical.items.slice(0, 15),
    lockIns: lockIns.items.slice(0, 10),
    preparationRecommendations: mergePreparations(sources),
    officialFormats: mergeOfficialFormats(sources),
    valueForMoney: {
      score: valueForMoneyScore,
      signals: valueForMoneySignals,
      actions: valueForMoneyActions,
    },
    circularEconomy: {
      applicable: circularApplicable,
      signals: circularSignals,
      actions: circularActions,
    },
    sourceTraceability: mergeSourceTraceability(sources),
    confidence: toConfidence(confidenceAvg),
  };

  return {
    result: mergedResult,
    sources: sources.map((source) => ({
      sourceId: source.sourceId,
      fileName: source.fileName,
      documentType: source.documentType,
    })),
    conflicts: [...checklist.conflicts, ...critical.conflicts, ...lockIns.conflicts],
  };
}
