import { ScoreDirection, ScoreMetric, ScoreSummary, ScoreThreshold } from '@/lib/assessment-types';
import { getScoringMetadata, MetricDefinition, QuestionnaireScoringMetadata } from '@/lib/scoring-metadata';
import {
  calculateBigFiveResults,
  calculateIpipNeoResults,
  ipipNeoItemsByVersion,
} from '@/components/questionnaire/test/private/BigFiveCalculator';
import { calculateHEXACOResults } from '@/components/questionnaire/test/private/HEXACOCalculator';
import { calculateRIASECResults } from '@/components/questionnaire/test/private/RIASECCalculator';
import {
  calculateSchwartzResults,
  higherOrderNames,
} from '@/components/questionnaire/test/private/SchwartzCalculator';

const fallbackMetadata: QuestionnaireScoringMetadata = {
  questionnaireVersion: 'unknown-questionnaire-v1',
  scoreVersion: 'mindscope-raw-v1',
  min: 0,
  reverseItems: [],
  direction: 'trait',
  highScoreMeaning: '高分代表原始作答总和更高，具体含义需要结合量表说明查看。',
  thresholds: [],
  note: '该量表暂以结构化原始分保存，用于保留完整记录与后续复核，不替代量表结果页中的正式解释。',
};

const labelMap: Record<string, string> = {
  extraversion: '外向性',
  agreeableness: '宜人性',
  conscientiousness: '尽责性',
  emotionalStability: '情绪稳定性',
  openness: '开放性',
  neuroticism: '神经质',
  honestyHumility: '诚实谦逊',
  emotionality: '情绪性',
  depression: '抑郁',
  anxiety: '焦虑',
  stress: '压力',
  opennessToChange: '开放变化',
  conservation: '保守稳定',
  selfEnhancement: '自我提升',
  selfTranscendence: '自我超越',
  R: '现实型',
  I: '研究型',
  A: '艺术型',
  S: '社会型',
  E: '企业型',
  C: '常规型',
  total: '总分',
  raw: '原始作答总和',
};

function sum(answers: string[]) {
  return answers.reduce((total, answer) => total + (Number(answer) || 0), 0);
}

function keyed(answers: string[]) {
  return Object.fromEntries(answers.map((answer, index) => [index + 1, answer]));
}

function metadataFor(questionnaireId: string) {
  return getScoringMetadata(questionnaireId) || {
    ...fallbackMetadata,
    questionnaireVersion: `${questionnaireId || 'unknown'}-v1`,
  };
}

function thresholdLabel(value: number, thresholds: ScoreThreshold[]) {
  if (!thresholds.length) return undefined;
  return thresholds.reduce((current, threshold) => (
    value >= threshold.min ? threshold.label : current
  ), thresholds[0].label);
}

function metricDefinition(meta: QuestionnaireScoringMetadata, key: string): MetricDefinition | undefined {
  return meta.metrics?.find((item) => item.key === key);
}

function makeMetric(
  meta: QuestionnaireScoringMetadata,
  key: string,
  label: string,
  value: number,
  overrides: Partial<ScoreMetric> = {},
): ScoreMetric {
  const definition = metricDefinition(meta, key);
  const min = overrides.min ?? definition?.min ?? meta.min;
  const max = overrides.max ?? definition?.max ?? meta.max;
  const thresholds = overrides.thresholds ?? definition?.thresholds ?? meta.thresholds;
  return {
    key,
    label,
    value,
    min,
    max,
    direction: overrides.direction ?? definition?.direction ?? meta.direction,
    highScoreMeaning: overrides.highScoreMeaning ?? definition?.highScoreMeaning ?? meta.highScoreMeaning,
    thresholds,
    level: overrides.level ?? thresholdLabel(value, thresholds),
  };
}

function finalize(
  questionnaireId: string,
  metrics: ScoreMetric[],
  primary?: ScoreMetric,
  options: { note?: string; scoringStatus?: ScoreSummary['scoringStatus'] } = {},
): ScoreSummary {
  const meta = metadataFor(questionnaireId);
  return {
    primary,
    metrics,
    note: options.note ?? meta.note,
    questionnaireVersion: meta.questionnaireVersion,
    scoreVersion: meta.scoreVersion,
    min: meta.min,
    max: meta.max,
    reverseItems: meta.reverseItems,
    direction: meta.direction,
    highScoreMeaning: meta.highScoreMeaning,
    thresholds: meta.thresholds,
    scoringStatus: options.scoringStatus ?? 'structured',
  };
}

export function buildScoreSummary(questionnaireId: string, answers: string[]): ScoreSummary {
  const meta = metadataFor(questionnaireId);

  if (questionnaireId === 'bigfive') {
    const result = calculateBigFiveResults(answers);
    const metrics = Object.entries(result).map(([key, value]) => (
      makeMetric(meta, key, labelMap[key] || key, value.score, { min: 10, max: 50 })
    ));
    return finalize(questionnaireId, metrics);
  }

  if (questionnaireId === 'bigfive-120' || questionnaireId === 'bigfive-300') {
    const version = questionnaireId === 'bigfive-120' ? 120 : 300;
    const result = calculateIpipNeoResults(answers, [...ipipNeoItemsByVersion[version]]);
    const perDomainMax = version === 120 ? 120 : 300;
    const metrics = Object.entries(result.domains).map(([key, value]) => (
      makeMetric(meta, key, labelMap[key] || key, value.score, {
        min: value.itemCount,
        max: perDomainMax,
      })
    ));
    return finalize(questionnaireId, metrics);
  }

  if (questionnaireId === 'hexaco') {
    const metrics = Object.entries(calculateHEXACOResults(answers)).map(([key, value]) => (
      makeMetric(meta, key, labelMap[key] || key, Number(value.toFixed(2)), { min: 1, max: 5 })
    ));
    return finalize(questionnaireId, metrics);
  }

  if (questionnaireId === 'riasec') {
    const result = calculateRIASECResults(answers);
    const metrics = Object.entries(result.scores).map(([key, value]) => (
      makeMetric(meta, key, labelMap[key] || key, value.score, { min: 0, max: 40 })
    ));
    const primary = makeMetric(meta, 'holland', `霍兰德代码：${result.hollandCode}`, result.ranking[0][1].score, {
      min: 0,
      max: 40,
    });
    return finalize(questionnaireId, metrics, primary);
  }

  if (questionnaireId === 'schwartz') {
    const result = calculateSchwartzResults(answers);
    const metrics = Object.entries(result.higherOrderScores).map(([key, value]) => (
      makeMetric(meta, key, higherOrderNames[key as keyof typeof higherOrderNames] || labelMap[key] || key, Number(value.toFixed(2)), {
        min: 1,
        max: 5,
      })
    ));
    return finalize(questionnaireId, metrics);
  }

  if (questionnaireId === 'dass21') {
    const answerMap = keyed(answers);
    const groups = {
      depression: [3, 5, 10, 13, 16, 17, 21],
      anxiety: [2, 4, 7, 9, 15, 19, 20],
      stress: [1, 6, 8, 11, 12, 14, 18],
    };
    const metrics = Object.entries(groups).map(([key, ids]) => {
      const value = ids.reduce((total, id) => total + Number(answerMap[id] || 0), 0) * 2;
      return makeMetric(meta, key, labelMap[key] || key, value, { min: 0, max: 42 });
    });
    return finalize(questionnaireId, metrics);
  }

  if (questionnaireId === 'pss10') {
    const reverse = new Set([4, 5, 7, 8]);
    const score = answers.reduce((total, answer, index) => {
      const value = Number(answer) || 0;
      return total + (reverse.has(index + 1) ? 4 - value : value);
    }, 0);
    const primary = makeMetric(meta, 'total', 'PSS-10 总分', score, { min: 0, max: 40 });
    return finalize(questionnaireId, [primary], primary);
  }

  if (questionnaireId === 'sds') {
    const reverse = new Set([2, 5, 6, 11, 12, 14, 16, 17, 18, 20]);
    const original = answers.reduce((total, answer, index) => {
      const value = Number(answer) || 0;
      return total + (reverse.has(index + 1) ? 5 - value : value);
    }, 0);
    const score = Math.round(original * 1.25);
    const primary = makeMetric(meta, 'total', 'SDS 标准分', score, { min: 25, max: 100 });
    return finalize(questionnaireId, [primary], primary);
  }

  const raw = sum(answers);
  const single: Record<string, { label: string; value: number; max?: number }> = {
    phq9: { label: 'PHQ-9 总分', value: raw, max: 27 },
    gad7: { label: 'GAD-7 总分', value: raw, max: 21 },
    isi: { label: 'ISI 总分', value: raw, max: 28 },
    bdi2: { label: 'BDI-II 总分', value: raw, max: 63 },
    who5: { label: 'WHO-5 百分制得分', value: raw * 4, max: 100 },
    crt: { label: 'CRT 答对题数', value: raw, max: 7 },
  };

  if (single[questionnaireId]) {
    const item = single[questionnaireId];
    const primary = makeMetric(meta, 'total', item.label, item.value, { min: meta.min, max: item.max ?? meta.max });
    return finalize(questionnaireId, [primary], primary);
  }

  const primary = makeMetric(meta, 'raw', '原始作答总和', raw, {
    min: meta.min,
    max: meta.max,
    direction: meta.direction as ScoreDirection,
  });
  return finalize(questionnaireId, [primary], primary, { scoringStatus: 'raw' });
}
