import { calculateTotal } from './patCalculations';

const toNumber = (value, fallback = 0) => {
  const numeric = Number(value);
  return Number.isFinite(numeric) ? numeric : fallback;
};

const round = (value, precision = 4) => {
  const factor = 10 ** precision;
  return Math.round(value * factor) / factor;
};

const mean = (values) => {
  if (!values.length) return 0;
  return values.reduce((sum, value) => sum + value, 0) / values.length;
};

const stddev = (values) => {
  if (values.length <= 1) return 0;
  const avg = mean(values);
  const variance = mean(values.map((value) => (value - avg) ** 2));
  return Math.sqrt(variance);
};

const sortByDateDesc = (items = []) =>
  [...items].sort((left, right) => {
    const leftDate = new Date(left?.datum || 0).getTime();
    const rightDate = new Date(right?.datum || 0).getTime();

    if (leftDate === rightDate) {
      const leftCreated = new Date(left?.created_at || 0).getTime();
      const rightCreated = new Date(right?.created_at || 0).getTime();
      return rightCreated - leftCreated;
    }

    return rightDate - leftDate;
  });

const collectSampleValues = (exercise) => {
  if (!exercise || typeof exercise !== 'object') return [];

  if (Array.isArray(exercise.subA) || Array.isArray(exercise.subB)) {
    return [...(exercise.subA || []), ...(exercise.subB || [])]
      .map((value) => Number(value))
      .filter((value) => Number.isFinite(value));
  }

  return (exercise.values || [])
    .map((value) => Number(value))
    .filter((value) => Number.isFinite(value));
};

const getExerciseActual = (exercise, missingStrategy = 'zero') => {
  if (!exercise) return missingStrategy === 'zero' ? 0 : null;

  const avg = Number(exercise.durchschnitt);
  if (Number.isFinite(avg)) return avg;

  const samples = collectSampleValues(exercise);
  if (!samples.length) return missingStrategy === 'zero' ? 0 : null;

  return mean(samples);
};

const formatDateLabel = (value) => {
  if (!value) return '—';
  const parsed = new Date(value);
  if (Number.isNaN(parsed.getTime())) return value;

  return new Intl.DateTimeFormat('de-DE', {
    day: '2-digit',
    month: '2-digit',
    year: 'numeric'
  }).format(parsed);
};

export const buildAnalysis = (assessments = [], patType, options = {}) => {
  const windowSize = toNumber(options.windowSize, 5);
  const missingStrategy = options.missingStrategy || 'zero';

  const matchingAssessments = sortByDateDesc(
    assessments.filter((assessment) => assessment?.patType === patType)
  );

  const windowedAssessments = matchingAssessments.slice(0, windowSize);

  if (!windowedAssessments.length) {
    return {
      windowedAssessments: [],
      exerciseMetrics: [],
      topWeaknesses: [],
      topStrengths: [],
      trendSeries: [],
      radarSeries: [],
      isLimitedData: false,
      patType,
      windowSize
    };
  }

  const latestAssessment = windowedAssessments[0];
  const baseExercises = latestAssessment?.exercises || [];
  const maxFactor = Math.max(...baseExercises.map((exercise) => toNumber(exercise?.faktor, 0)), 1);

  const exerciseMetrics = baseExercises.map((baseExercise, index) => {
    const actualSeries = windowedAssessments.map((assessment) => {
      const exercise = assessment?.exercises?.[index];
      const actual = getExerciseActual(exercise, missingStrategy);
      return actual === null ? 0 : actual;
    });

    const actualMean = mean(actualSeries);
    const soll = toNumber(baseExercise?.soll, 0);
    const factor = toNumber(baseExercise?.faktor, 0);

    const normalizedSoll = soll > 0 ? soll : 1;
    const gapScore = Math.max(0, (soll - actualMean) / normalizedSoll);
    const factorScore = maxFactor > 0 ? factor / maxFactor : 0;
    const consistencyScore = Math.min(1, stddev(actualSeries) / normalizedSoll);
    const priorityScore = gapScore * factorScore * (1 + consistencyScore);

    const overNorm = Math.max(0, (actualMean - soll) / normalizedSoll);
    const stability = 1 - consistencyScore;
    const strengthScore = overNorm * factorScore * Math.max(stability, 0.1);

    const latestActual = actualSeries[0] || 0;
    const oldestActual = actualSeries[actualSeries.length - 1] || 0;
    const trendDelta = latestActual - oldestActual;

    return {
      index,
      name: baseExercise?.name || `Übung ${index + 1}`,
      soll,
      factor,
      actualMean: round(actualMean, 4),
      latestActual: round(latestActual, 4),
      trendDelta: round(trendDelta, 4),
      gapScore: round(gapScore, 4),
      consistencyScore: round(consistencyScore, 4),
      priorityScore: round(priorityScore, 4),
      strengthScore: round(strengthScore, 4),
      actualSeries: actualSeries.map((value) => round(value, 4))
    };
  });

  const topWeaknesses = [...exerciseMetrics]
    .sort((left, right) => right.priorityScore - left.priorityScore)
    .slice(0, 3);

  const topStrengths = [...exerciseMetrics]
    .sort((left, right) => right.strengthScore - left.strengthScore)
    .slice(0, 3);

  const trendSeries = [...windowedAssessments]
    .reverse()
    .map((assessment) => ({
      assessmentId: assessment.id,
      datum: assessment.datum,
      label: formatDateLabel(assessment.datum),
      totalPoints: Math.round(calculateTotal(assessment.exercises || []))
    }));

  const radarSeries = [...exerciseMetrics]
    .sort((left, right) => right.factor - left.factor)
    .slice(0, 6)
    .map((metric) => ({
      name: metric.name,
      soll: metric.soll,
      actual: metric.actualMean,
      ratio: metric.soll > 0 ? round(Math.min(1.2, metric.actualMean / metric.soll), 4) : 0,
      priorityScore: metric.priorityScore
    }));

  return {
    windowedAssessments,
    exerciseMetrics,
    topWeaknesses,
    topStrengths,
    trendSeries,
    radarSeries,
    isLimitedData: windowedAssessments.length < windowSize,
    patType,
    windowSize
  };
};

export default buildAnalysis;
