import type { Accommodation, Limitation, Report, ReportStatus, ScaleValue, StoredEntry, TaskCompletion } from "./types";

type Component = {
  score: number;
  weight: number;
};

const STATUS_SCORE_MAP: Partial<Record<ReportStatus, number>> = {
  kan_fortsette: 100,
  trenger_pause: 65,
  trenger_enklere_oppgave: 50,
  bør_stoppe: 20,
  onsker_a_stoppe: 20,
  ønsker_å_stoppe: 20
};

const TASK_COMPLETION_SCORE_MAP: Record<TaskCompletion, number> = {
  nesten_ingenting: 0,
  litt: 25,
  delvis: 50,
  som_forventet: 80,
  mer_enn_forventet: 100
};

export type ScoreBand = {
  min: number;
  max: number;
  label: string;
  short_label: string;
  description: string;
};

export const scoreBands: ScoreBand[] = [
  {
    min: 90,
    max: 100,
    label: "Særdeles høyt funksjonsnivå",
    short_label: "Særdeles høyt",
    description: "Rapporten tyder på svært god funksjon i den aktuelle arbeidssituasjonen, over det som normalt kan forventes."
  },
  {
    min: 70,
    max: 89,
    label: "Forventet funksjonsnivå",
    short_label: "Forventet",
    description: "Rapporten ligger innenfor et forventet/godt funksjonsnivå for den aktuelle arbeidssituasjonen."
  },
  {
    min: 50,
    max: 69,
    label: "Moderat redusert funksjon",
    short_label: "Moderat redusert",
    description: "Rapporten tyder på merkbar reduksjon i arbeidsevne, energi, symptombelastning eller bærekraft."
  },
  {
    min: 30,
    max: 49,
    label: "Betydelig redusert funksjon",
    short_label: "Betydelig redusert",
    description: "Rapporten tyder på klart begrenset funksjon og/eller høy belastning ved gjennomføring."
  },
  {
    min: 0,
    max: 29,
    label: "Svært lav funksjon",
    short_label: "Svært lav",
    description: "Rapporten tyder på svært lav aktuell funksjon, høy belastning eller at aktiviteten ikke fremstår bærekraftig."
  }
];

export const scoreBandDisclaimer =
  "Totalscoren er normalisert slik at 75 % omtrent tilsvarer et forventet/godt rapportert funksjonsnivå, mens 100 % tilsvarer særdeles høyt funksjonsnivå. Scoren er en intern funksjonsindikator for oversikt og trend, ikke en medisinsk fasit.";

export function getScoreBand(score: number | null | undefined) {
  if (score === null || score === undefined || Number.isNaN(score)) return null;
  const clamped = clamp(Math.round(score), 0, 100);
  return scoreBands.find((band) => clamped >= band.min && clamped <= band.max) ?? null;
}

export function normalizePositiveScale(value: ScaleValue | undefined) {
  if (!value) return null;
  return ((value - 1) / 4) * 100;
}

export function normalizeNegativeScale(value: ScaleValue | undefined) {
  if (!value) return null;
  return ((5 - value) / 4) * 100;
}

function addScore(components: Component[], score: number | null | undefined, weight: number) {
  if (score === null || score === undefined || Number.isNaN(score)) return;
  components.push({ score: clamp(score, 0, 100), weight });
}

function energyScore(report: Partial<Report>) {
  const energyLevel = report.energy_level;
  const physical = report.physical_energy_detail ?? report.physical_energy;
  const mental = report.mental_energy_detail ?? report.mental_energy;

  if (physical || mental) {
    const values = [physical ?? energyLevel, mental ?? energyLevel].filter(Boolean) as ScaleValue[];
    if (values.length === 0) return null;
    return values.reduce((sum, value) => sum + (normalizePositiveScale(value) ?? 0), 0) / values.length;
  }

  return normalizePositiveScale(energyLevel);
}

function limitationsScore(values: Limitation[] | undefined) {
  const selected = values?.filter((value) => value !== "other") ?? [];
  if (!values || values.length === 0) return null;
  if (selected.length === 0) return null;
  return Math.max(30, 100 - selected.length * 10);
}

function accommodationsScore(values: Accommodation[] | undefined) {
  if (!values || values.length === 0) return null;
  return values.includes("nothing_helped") ? 40 : 75;
}

export function calculateReportScore(report: Partial<Report>) {
  const components: Component[] = [];

  addScore(components, normalizePositiveScale(report.work_ability), 1.5);
  addScore(components, energyScore(report), 1.25);
  addScore(components, normalizePositiveScale(report.mental_clarity), 1);
  addScore(components, normalizeNegativeScale(report.symptom_burden), 1.25);
  addScore(components, normalizeNegativeScale(report.effort_strain), 1.25);
  addScore(components, report.status ? STATUS_SCORE_MAP[report.status] : null, 1);
  addScore(components, normalizePositiveScale(report.perceived_productivity), 0.75);
  addScore(components, report.task_completion ? TASK_COMPLETION_SCORE_MAP[report.task_completion] : null, 0.75);
  addScore(components, limitationsScore(report.main_limitations), 0.5);
  addScore(components, accommodationsScore(report.helpful_accommodations), 0.25);

  if (components.length === 0) return null;

  const weighted = components.reduce((sum, component) => sum + component.score * component.weight, 0);
  const weights = components.reduce((sum, component) => sum + component.weight, 0);
  return clamp(Math.round(weighted / weights), 0, 100);
}

export function getReportScore(report: Report) {
  return report.total_score_percent ?? calculateReportScore(report);
}

export function getEntryTime(entry: StoredEntry) {
  const created = Date.parse(entry.created_at);
  if (!Number.isNaN(created)) return created;

  const fallback = Date.parse(`${entry.report_date}T${entry.report_time || "00:00:00"}`);
  return Number.isNaN(fallback) ? 0 : fallback;
}

export function normalizeReportMetric(report: Report, metric: keyof Report | "total_score_percent") {
  if (metric === "total_score_percent") return getReportScore(report);
  if (metric === "symptom_burden" || metric === "effort_strain") {
    return normalizeNegativeScale(report[metric] as ScaleValue | undefined);
  }
  return normalizePositiveScale(report[metric] as ScaleValue | undefined);
}

export function calculatePeriodWorkAbilityAssessment(reports: Report[]) {
  const scores = reports.map(getReportScore).filter((score): score is number => typeof score === "number" && Number.isFinite(score));
  if (scores.length === 0) {
    return {
      score_percent: null,
      score_band: null,
      report_count: 0,
      method: "arithmetic_mean_of_report_total_scores" as const
    };
  }
  const score_percent = Math.round(scores.reduce((sum, score) => sum + score, 0) / scores.length);
  return {
    score_percent,
    score_band: getScoreBand(score_percent),
    report_count: scores.length,
    method: "arithmetic_mean_of_report_total_scores" as const
  };
}

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