import { useEffect, useState } from "preact/hooks";

type Note = {
  numEtud: number;
  idModule: string;
  note: number;
  noteSession2: number | null;
};
type UE = { id: number; nom: string };
type UEModule = {
  idModule: string;
  idUE: number;
  idPromo: string;
  coeff: number;
};
type Module = { id: string; nom: string };
type Ajustement = {
  numEtud: number;
  idUE: number;
  valeur: number;
  malus: number;
};

type Props = {
  numEtud: number | null;
  prenom: string;
};

function scoreClass(score: number | null): string {
  if (score === null) return "score-none";
  return score >= 10 ? "score-good" : "score-warn";
}

function avgClass(avg: number | null): string {
  if (avg === null) return "";
  return avg >= 10 ? "avg-good" : "avg-warn";
}

/** Returns the effective note (session 2 if exists, otherwise session 1). */
function effectiveNote(n: Note): number {
  return n.noteSession2 ?? n.note;
}

export default function NotesView({ numEtud, prenom }: Props) {
  const [notes, setNotes] = useState<Note[]>([]);
  const [ues, setUes] = useState<UE[]>([]);
  const [ueModules, setUeModules] = useState<UEModule[]>([]);
  const [modules, setModules] = useState<Module[]>([]);
  const [ajustements, setAjustements] = useState<Ajustement[]>([]);
  const [promos, setPromos] = useState<string[]>([]);
  const [activePromo, setActivePromo] = useState<string | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    if (numEtud === null) {
      setLoading(false);
      return;
    }

    async function load() {
      try {
        const [notesRes, uesRes, ueModRes, modRes, ajRes] = await Promise.all([
          fetch(`/notes/api/notes?numEtud=${numEtud}`),
          fetch("/notes/api/ues"),
          fetch("/notes/api/ue-modules"),
          fetch("/notes/api/modules"),
          fetch(`/notes/api/ajustements?numEtud=${numEtud}`),
        ]);

        if (!notesRes.ok || !uesRes.ok || !ueModRes.ok) {
          throw new Error("Erreur lors du chargement");
        }

        const [notesData, uesData, ueModData, modData, ajData] = await Promise
          .all([
            notesRes.json(),
            uesRes.json(),
            ueModRes.json(),
            modRes.ok ? modRes.json() : [],
            ajRes.ok ? ajRes.json() : [],
          ]);

        setNotes(notesData);
        setUes(uesData);
        setUeModules(ueModData);
        setModules(modData);
        setAjustements(ajData);

        const noteModuleIds = new Set(notesData.map((n: Note) => n.idModule));
        const relevantPromos = [
          ...new Set(
            ueModData
              .filter((um: UEModule) => noteModuleIds.has(um.idModule))
              .map((um: UEModule) => um.idPromo),
          ),
        ] as string[];

        setPromos(relevantPromos);
        if (relevantPromos.length > 0) setActivePromo(relevantPromos[0]);
      } catch (e) {
        setError(e instanceof Error ? e.message : "Erreur inconnue");
      } finally {
        setLoading(false);
      }
    }

    load();
  }, [numEtud]);

  if (numEtud === null) {
    return (
      <div class="page-content">
        <p class="state-empty">
          Bonjour {prenom}{" "}
          — aucun dossier etudiant n'est associe a votre compte.
        </p>
      </div>
    );
  }

  if (loading) {
    return (
      <div class="page-content">
        <p class="state-loading">Chargement...</p>
      </div>
    );
  }

  if (error) {
    return (
      <div class="page-content">
        <p class="state-error">{error}</p>
      </div>
    );
  }

  const filteredUeModules = activePromo
    ? ueModules.filter((um) => um.idPromo === activePromo)
    : ueModules;

  const ueIds = [...new Set(filteredUeModules.map((um) => um.idUE))];

  const moduleMap = Object.fromEntries(modules.map((m) => [m.id, m]));
  const noteMap = Object.fromEntries(
    notes.map((n) => [n.idModule, n]),
  );
  const ajMap = Object.fromEntries(
    ajustements.map((a) => [a.idUE, a]),
  );

  return (
    <div class="page-content">
      {promos.length > 1 && (
        <div class="tabs">
          {promos.map((p) => (
            <button
              type="button"
              key={p}
              class={`tab-btn${activePromo === p ? " active" : ""}`}
              onClick={() => setActivePromo(p)}
            >
              {p}
            </button>
          ))}
        </div>
      )}

      {ueIds.length === 0 && (
        <p class="state-empty">Aucune note disponible pour cette periode.</p>
      )}

      {ueIds.map((ueId) => {
        const ue = ues.find((u) => u.id === ueId);
        if (!ue) return null;

        const ueModsForUE = filteredUeModules.filter((um) => um.idUE === ueId);
        let weightedSum = 0;
        let coveredCoeff = 0;
        ueModsForUE.forEach((um) => {
          const noteObj = noteMap[um.idModule];
          if (noteObj) {
            const val = effectiveNote(noteObj);
            weightedSum += val * um.coeff;
            coveredCoeff += um.coeff;
          }
        });

        const avg = coveredCoeff > 0 ? weightedSum / coveredCoeff : null;
        const ajust = ajMap[ueId] ?? null;

        // If ajust.valeur exists, it replaces the calculated average
        // Then malus is subtracted
        let finalAvg: number | null = avg;
        if (ajust) {
          finalAvg = ajust.valeur;
          if (ajust.malus > 0) {
            finalAvg = (finalAvg ?? 0) - ajust.malus;
          }
        }

        return (
          <div key={ueId} class="ue-card">
            <div class="ue-card-header">
              <p class="ue-card-title">UE : {ue.nom}</p>
              {finalAvg !== null
                ? (
                  <p class={`ue-card-avg ${avgClass(finalAvg)}`}>
                    Moyenne : {finalAvg.toFixed(2)}/20
                    {ajust && ajust.malus > 0 && (
                      <span>(malus : -{ajust.malus})</span>
                    )}
                  </p>
                )
                : <p class="ue-card-avg avg-warn">Notes non disponibles</p>}
            </div>

            {ueModsForUE.map((um) => {
              const mod = moduleMap[um.idModule];
              const noteObj = noteMap[um.idModule] ?? null;
              const effective = noteObj ? effectiveNote(noteObj) : null;
              const hasS2 = noteObj?.noteSession2 != null;

              return (
                <div key={um.idModule} class="ue-module-row">
                  <span class="ue-module-name">
                    {mod ? mod.id : um.idModule} —{" "}
                    {mod ? mod.nom : "ECUE inconnu"} (coef {um.coeff})
                  </span>
                  <span class={`score-chip ${scoreClass(effective)}`}>
                    {effective !== null ? `${effective}/20` : "—"}
                    {hasS2 && (
                      <span
                        style="font-size: 0.7rem; opacity: 0.7; margin-left: 0.35rem"
                        title={`Session 1 : ${noteObj!.note}/20`}
                      >
                        (S1: {noteObj!.note})
                      </span>
                    )}
                  </span>
                </div>
              );
            })}
          </div>
        );
      })}
    </div>
  );
}
