import type {
  ExperienceScoreAggregationMode,
  ExperienceSignalStatus,
  ExperienceTrendDirection,
} from "@/lib/experienciometro/types";

export const EXPERIENCE_ITEM_MAX_SCORE = 3;

export type ExperienceResponseScoreInput = {
  categoryId: string;
  categoryKey: string;
  categoryName: string;
  categoryWeight: number;
  itemId: string;
  itemWeight: number;
  score: number;
};

export type ExperienceCategoryScore = {
  categoryId: string;
  categoryKey: string;
  categoryName: string;
  avgScoreRaw: number;
  avgScorePct: number;
  responseCount: number;
  categoryWeight: number;
};

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

export function roundToOne(value: number): number {
  return Math.round(value * 10) / 10;
}

export function scoreToPercent(score: number): number {
  return (clamp(score, 0, EXPERIENCE_ITEM_MAX_SCORE) / EXPERIENCE_ITEM_MAX_SCORE) * 100;
}

function weightedAverage(values: Array<{ value: number; weight: number }>): number {
  const safeValues = values.filter((item) => Number.isFinite(item.value) && Number.isFinite(item.weight) && item.weight > 0);
  const totalWeight = safeValues.reduce((acc, item) => acc + item.weight, 0);
  if (totalWeight <= 0) {
    return 0;
  }

  const weighted = safeValues.reduce((acc, item) => acc + item.value * item.weight, 0);
  return weighted / totalWeight;
}

export function buildCategoryScores(inputs: ExperienceResponseScoreInput[]): ExperienceCategoryScore[] {
  const grouped = new Map<string, ExperienceResponseScoreInput[]>();

  for (const input of inputs) {
    const key = input.categoryId;
    if (!grouped.has(key)) {
      grouped.set(key, []);
    }
    grouped.get(key)!.push(input);
  }

  return Array.from(grouped.values()).map((rows) => {
    const base = rows[0];
    const avgScoreRaw = weightedAverage(
      rows.map((row) => ({
        value: clamp(row.score, 0, EXPERIENCE_ITEM_MAX_SCORE),
        weight: row.itemWeight > 0 ? row.itemWeight : 1,
      }))
    );

    return {
      categoryId: base.categoryId,
      categoryKey: base.categoryKey,
      categoryName: base.categoryName,
      avgScoreRaw: roundToOne(avgScoreRaw),
      avgScorePct: roundToOne(scoreToPercent(avgScoreRaw)),
      responseCount: rows.length,
      categoryWeight: base.categoryWeight > 0 ? base.categoryWeight : 1,
    };
  });
}

export function buildEvaluationScore(inputs: ExperienceResponseScoreInput[]): {
  totalScoreRaw: number;
  totalScorePct: number;
  categories: ExperienceCategoryScore[];
} {
  const categories = buildCategoryScores(inputs);

  const totalScoreRaw = weightedAverage(
    categories.map((category) => ({
      value: category.avgScoreRaw,
      weight: category.categoryWeight > 0 ? category.categoryWeight : 1,
    }))
  );

  return {
    totalScoreRaw: roundToOne(totalScoreRaw),
    totalScorePct: roundToOne(scoreToPercent(totalScoreRaw)),
    categories,
  };
}

export function parseRecentWeightsCsv(raw: string): number[] {
  return raw
    .split(",")
    .map((value) => Number(value.trim()))
    .filter((value) => Number.isFinite(value) && value > 0);
}

export function stringifyRecentWeights(weights: number[]): string {
  const safe = weights.filter((value) => Number.isFinite(value) && value > 0);
  if (safe.length === 0) {
    return "0.5,0.3,0.2";
  }

  return safe.map((value) => String(roundToOne(value))).join(",");
}

export function aggregateLocationScore(input: {
  scoresDesc: number[];
  aggregationMode: ExperienceScoreAggregationMode;
  recentWindow: number;
  recentWeights: number[];
}): number {
  const scores = input.scoresDesc.filter((score) => Number.isFinite(score) && score >= 0);
  if (scores.length === 0) {
    return 0;
  }

  if (input.aggregationMode === "full_average") {
    return roundToOne(scores.reduce((acc, score) => acc + score, 0) / scores.length);
  }

  if (input.aggregationMode === "moving_average") {
    const windowSize = Math.max(1, Math.floor(input.recentWindow));
    const scoped = scores.slice(0, windowSize);
    return roundToOne(scoped.reduce((acc, score) => acc + score, 0) / scoped.length);
  }

  const windowSize = Math.max(1, Math.floor(input.recentWindow));
  const scoped = scores.slice(0, windowSize);
  const defaultWeights = [0.5, 0.3, 0.2];
  const baseWeights = input.recentWeights.length > 0 ? input.recentWeights : defaultWeights;
  const normalizedWeights = scoped.map((_, index) => baseWeights[index] ?? 0);

  const weighted = weightedAverage(
    scoped.map((score, index) => ({
      value: score,
      weight: normalizedWeights[index] > 0 ? normalizedWeights[index] : 1,
    }))
  );

  return roundToOne(weighted);
}

export function signalFromScore(score: number, thresholds: { greenThreshold: number; yellowThreshold: number }): ExperienceSignalStatus {
  if (score >= thresholds.greenThreshold) {
    return "green";
  }

  if (score >= thresholds.yellowThreshold) {
    return "yellow";
  }

  return "red";
}

export function trendDirectionFromDelta(delta: number): ExperienceTrendDirection {
  if (delta > 0.2) {
    return "up";
  }

  if (delta < -0.2) {
    return "down";
  }

  return "flat";
}
