import type { Ampelfarbe, ScoringDimensionDefinition, ScoringFarbe } from "./scoring-model";

export interface LlmSubcriterionAssessment {
  id: string;
  farbe: ScoringFarbe;
  evidence: string;
  begruendung: string;
  confidence: number;
  missingReason: string;
}

export interface LlmDimensionAssessment {
  id: string;
  subcriteria: LlmSubcriterionAssessment[];
}

export interface LlmScoringAssessment {
  ausschlussgruende: string[];
  dimensionen: LlmDimensionAssessment[];
}

export interface ScoredSubcriterion extends LlmSubcriterionAssessment {
  name: string;
  indikator: string;
  weight: number;
  score: number | null;
  weightedScore: number;
}

export interface ScoringDimension {
  id: string;
  name: string;
  weight: number;
  farbe: ScoringFarbe;
  score: number;
  weightedScore: number;
  scorableWeight: number;
  subcriteria: ScoredSubcriterion[];
}

export interface ScoringResult {
  farbe: Ampelfarbe;
  gesamtScore: number;
  unbewertbareKriterien: number;
  missingDataWarnings: string[];
  dimensionen: ScoringDimension[];
}

const COLOR_SCORE: Record<ScoringFarbe, number | null> = {
  gruen: 100,
  gelb: 50,
  rot: 0,
  unbewertbar: null,
};

export function normalizeScoringFarbe(value: unknown): ScoringFarbe {
  return value === "gruen" || value === "gelb" || value === "rot" || value === "unbewertbar"
    ? value
    : "unbewertbar";
}

export function scoreToFarbe(score: number): Ampelfarbe {
  if (score >= 75) return "gruen";
  return "gelb";
}

export function calculateScoringResult(
  model: ScoringDimensionDefinition[],
  assessment: LlmScoringAssessment,
): ScoringResult {
  const assessmentByDimension = new Map(assessment.dimensionen.map((dimension) => [dimension.id, dimension]));
  let totalWeightedScore = 0;
  let unbewertbareKriterien = 0;
  const missingDataWarnings: string[] = [];

  const dimensionen = model.map((dimensionDefinition): ScoringDimension => {
    const dimensionAssessment = assessmentByDimension.get(dimensionDefinition.id);
    const assessmentBySubcriterion = new Map(
      (dimensionAssessment?.subcriteria ?? []).map((subcriterion) => [subcriterion.id, subcriterion]),
    );

    let achievedScore = 0;
    let scorableWeight = 0;
    const subcriteria = dimensionDefinition.subcriteria.map((subcriterionDefinition): ScoredSubcriterion => {
      const rawAssessment = assessmentBySubcriterion.get(subcriterionDefinition.id);
      const farbe = normalizeScoringFarbe(rawAssessment?.farbe);
      const score = COLOR_SCORE[farbe];
      const confidence = Number.isFinite(rawAssessment?.confidence)
        ? Math.max(0, Math.min(1, Number(rawAssessment?.confidence)))
        : 0;
      const missingReason = String(rawAssessment?.missingReason ?? "").trim();
      const evidence = String(rawAssessment?.evidence ?? "").trim();

      if (score == null) {
        unbewertbareKriterien += 1;
        if (missingReason) {
          missingDataWarnings.push(`${dimensionDefinition.name} / ${subcriterionDefinition.name}: ${missingReason}`);
        }
      } else {
        scorableWeight += subcriterionDefinition.weight;
      }

      achievedScore += ((score ?? 0) * subcriterionDefinition.weight) / 100;

      return {
        id: subcriterionDefinition.id,
        name: subcriterionDefinition.name,
        indikator: subcriterionDefinition.indikator,
        farbe,
        evidence,
        begruendung: String(rawAssessment?.begruendung ?? "").trim(),
        confidence,
        missingReason,
        weight: subcriterionDefinition.weight,
        score,
        weightedScore: 0,
      };
    });

    const roundedScorableWeight = Number(scorableWeight.toFixed(2));
    const roundedDimensionScore = roundedScorableWeight
      ? Number(((achievedScore / roundedScorableWeight) * 100).toFixed(2))
      : 0;
    const weightedScore = roundedScorableWeight ? (roundedDimensionScore * dimensionDefinition.weight) / 100 : 0;
    if (roundedScorableWeight) {
      totalWeightedScore += weightedScore;
    }
    const normalizedSubcriteria = subcriteria.map((subcriterion) => ({
      ...subcriterion,
      weightedScore:
        subcriterion.score == null || !roundedScorableWeight
          ? 0
          : Number(((subcriterion.score * subcriterion.weight) / roundedScorableWeight).toFixed(2)),
    }));

    return {
      id: dimensionDefinition.id,
      name: dimensionDefinition.name,
      weight: dimensionDefinition.weight,
      farbe: scoreToFarbe(roundedDimensionScore),
      score: roundedDimensionScore,
      weightedScore: Number(weightedScore.toFixed(2)),
      scorableWeight: roundedScorableWeight,
      subcriteria: normalizedSubcriteria,
    };
  });

  const scorableDimensionWeight = dimensionen
    .filter((dimension) => dimension.scorableWeight > 0)
    .reduce((sum, dimension) => sum + dimension.weight, 0);
  const gesamtScore = scorableDimensionWeight
    ? Number(((totalWeightedScore / scorableDimensionWeight) * 100).toFixed(2))
    : 0;
  const hasWeakDimension = dimensionen.some((dimension) => dimension.scorableWeight > 0 && dimension.score < 35);
  const rawFarbe = scoreToFarbe(gesamtScore);
  const farbe = rawFarbe === "gruen" && hasWeakDimension ? "gelb" : rawFarbe;

  return {
    farbe,
    gesamtScore,
    unbewertbareKriterien,
    missingDataWarnings,
    dimensionen,
  };
}

export function deriveTrafficLight(
  scoring: ScoringResult,
  ausschlussgruende: string[],
): Ampelfarbe {
  return ausschlussgruende.length ? "rot" : scoring.farbe;
}
