import { useEffect, useState } from "preact/hooks";
import {
  applyAjustement,
  calculateWeightedAverage,
  getEffectiveNote,
} from "$root/logic/grades.ts";

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

type Props = { numEtud: number };

function fmt(n: number): string {
  return `${Math.round(n * 10) / 10}/20`;
}

function noteClass(n: number): string {
  return n >= 10 ? "note-chip note-chip--ok" : "note-chip note-chip--fail";
}

export default function NoteRecap({ numEtud }: Props) {
  const [student, setStudent] = useState<Student | null>(null);
  const [ueList, setUeList] = useState<UE[]>([]);
  const [ueModules, setUeModules] = useState<UEModule[]>([]);
  const [moduleMap, setModuleMap] = useState<Map<string, string>>(new Map());
  const [noteMap, setNoteMap] = useState<Map<string, Note>>(new Map());
  const [ajustements, setAjustements] = useState<Ajustement[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [editingNote, setEditingNote] = useState<
    { idModule: string; field: "note" | "noteSession2"; value: string } | null
  >(null);
  const [ajustInputs, setAjustInputs] = useState<
    Record<number, { valeur: string; malus: string }>
  >({});

  async function load() {
    try {
      const sRes = await fetch(`/students/api/students/${numEtud}`);
      if (!sRes.ok) throw new Error("Eleve introuvable");
      const s: Student = await sRes.json();
      setStudent(s);

      const [uesRes, umRes, mRes, notesRes, ajustRes] = await Promise.all([
        fetch("/notes/api/ues"),
        fetch(
          `/notes/api/ue-modules?idPromo=${encodeURIComponent(s.idPromo)}`,
        ),
        fetch("/notes/api/modules"),
        fetch(`/notes/api/notes?numEtud=${numEtud}`),
        fetch(`/notes/api/ajustements?numEtud=${numEtud}`),
      ]);

      if (uesRes.ok) setUeList(await uesRes.json());
      if (umRes.ok) setUeModules(await umRes.json());
      if (mRes.ok) {
        const mods: Module[] = await mRes.json();
        setModuleMap(new Map(mods.map((m) => [m.id, m.nom])));
      }
      if (notesRes.ok) {
        const ns: Note[] = await notesRes.json();
        setNoteMap(new Map(ns.map((n) => [n.idModule, n])));
      }
      if (ajustRes.ok) {
        const aj: Ajustement[] = await ajustRes.json();
        setAjustements(aj);
        const inputs: Record<number, { valeur: string; malus: string }> = {};
        for (const a of aj) {
          inputs[a.idUE] = {
            valeur: String(a.valeur),
            malus: String(a.malus),
          };
        }
        setAjustInputs(inputs);
      }
    } catch (e) {
      setError(e instanceof Error ? e.message : "Erreur");
    } finally {
      setLoading(false);
    }
  }

  useEffect(() => {
    load();
  }, [numEtud]);

  async function saveNote(
    idModule: string,
    field: "note" | "noteSession2",
    value: string,
  ) {
    if (value.trim() === "" && field === "noteSession2") {
      // Clear session 2 note
      const res = await fetch(
        `/notes/api/notes/${numEtud}/${encodeURIComponent(idModule)}`,
        {
          method: "PUT",
          headers: { "content-type": "application/json" },
          body: JSON.stringify({ noteSession2: null }),
        },
      );
      if (res.ok) {
        const updated: Note = await res.json();
        setNoteMap((prev) => new Map(prev).set(idModule, updated));
      }
      setEditingNote(null);
      return;
    }

    const note = parseFloat(value.replace(",", "."));
    if (isNaN(note) || note < 0 || note > 20) {
      setEditingNote(null);
      return;
    }

    const existing = noteMap.get(idModule);

    if (existing) {
      // Update
      const res = await fetch(
        `/notes/api/notes/${numEtud}/${encodeURIComponent(idModule)}`,
        {
          method: "PUT",
          headers: { "content-type": "application/json" },
          body: JSON.stringify({ [field]: note }),
        },
      );
      if (res.ok) {
        const updated: Note = await res.json();
        setNoteMap((prev) => new Map(prev).set(idModule, updated));
      }
    } else {
      // Create
      const body: Record<string, unknown> = {
        numEtud,
        idModule,
        note: field === "note" ? note : 0,
      };
      if (field === "noteSession2") body.noteSession2 = note;
      const res = await fetch("/notes/api/notes", {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify(body),
      });
      if (res.ok) {
        const created: Note = await res.json();
        setNoteMap((prev) => new Map(prev).set(idModule, created));
      }
    }
    setEditingNote(null);
  }

  async function applyAjust(idUE: number) {
    const inputs = ajustInputs[idUE];
    const val = parseFloat((inputs?.valeur ?? "").replace(",", "."));
    const malus = parseInt(inputs?.malus ?? "0");
    if (isNaN(val) || val < 0 || val > 20) return;
    if (isNaN(malus) || malus < 0) return;

    const existing = ajustements.find((a) => a.idUE === idUE);
    const res = existing
      ? await fetch(`/notes/api/ajustements/${numEtud}/${idUE}`, {
        method: "PUT",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({ valeur: val, malus }),
      })
      : await fetch("/notes/api/ajustements", {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({ numEtud, idUE, valeur: val, malus }),
      });
    if (res.ok) {
      const updated: Ajustement = await res.json();
      setAjustements((prev) =>
        existing
          ? prev.map((a) => (a.idUE === idUE ? updated : a))
          : [...prev, updated]
      );
    }
  }

  async function resetAjust(idUE: number) {
    const res = await fetch(`/notes/api/ajustements/${numEtud}/${idUE}`, {
      method: "DELETE",
    });
    if (res.ok) {
      setAjustements((prev) => prev.filter((a) => a.idUE !== idUE));
      setAjustInputs((prev) => {
        const c = { ...prev };
        delete c[idUE];
        return c;
      });
    }
  }

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

  return (
    <div class="page-content">
      <a
        class="back-link"
        href="/notes/courses"
        f-partial="/notes/partials/courses"
      >
        ← Retour a la liste
      </a>

      <h2
        class="page-title"
        style="border-bottom: none; margin-bottom: 0.5rem"
      >
        Recap notes – {student.prenom} {student.nom}
      </h2>

      <div class="info-bar" style="margin-bottom: 1.25rem">
        <span class="numEtud-chip">{student.numEtud}</span>
        <span style="font-weight: 600">
          {student.prenom} {student.nom}
        </span>
        <span class="note-chip note-chip--promo">{student.idPromo}</span>
      </div>

      {error && <p class="state-error">{error}</p>}

      {ueList.length === 0
        ? (
          <p class="state-empty">
            Aucune UE configuree pour cette promotion.
          </p>
        )
        : ueList.map((ue) => {
          const ueMods = ueList.length > 0 ? ueModules.filter((um) => um.idUE === ue.id) : [];
          const notesRecord = Object.fromEntries(noteMap);
          const avg = calculateWeightedAverage(ueMods, notesRecord);
          const ajust = ajustements.find((a) => a.idUE === ue.id) ?? null;
          const finalAvg = applyAjustement(avg, ajust);

          return (
            <div key={ue.id} class="edit-section">
              {/* UE header */}
              <div style="display: flex; align-items: center; gap: 0.75rem; margin-bottom: 0.75rem; flex-wrap: wrap">
                <p class="edit-section-title" style="margin: 0">{ue.nom}</p>
                {avg !== null && (
                  <span
                    class={noteClass(avg)}
                    style="font-size: 0.78rem"
                  >
                    Moy. calculee : {fmt(avg)}
                  </span>
                )}
                {ajust && (
                  <span
                    class="note-chip note-chip--ajust"
                    style="font-size: 0.78rem"
                  >
                    Ajust. actif : {fmt(ajust.valeur)}
                  </span>
                )}
                {ajust && ajust.malus > 0 && (
                  <span
                    class="note-chip note-chip--fail"
                    style="font-size: 0.78rem"
                  >
                    Malus : -{ajust.malus}
                  </span>
                )}
              </div>

              {/* ECUE rows */}
              {ueMods.length === 0
                ? (
                  <p
                    class="col-dim"
                    style="font-size: 0.8rem; padding: 0.25rem 0; margin-bottom: 0.75rem"
                  >
                    Aucun ECUE associe a cette UE pour cette promotion.
                  </p>
                )
                : (
                  <div style="margin-bottom: 0.75rem">
                    {ueMods.map((um) => {
                      const noteObj = noteMap.get(um.idModule);
                      const noteVal = noteObj?.note;
                      const noteS2 = noteObj?.noteSession2;
                      const effective = noteObj
                        ? getEffectiveNote(noteObj)
                        : undefined;
                      const nomMod = moduleMap.get(um.idModule) ?? um.idModule;

                      return (
                        <div key={um.idModule} class="note-row">
                          <span class="note-row-label">
                            <span class="numEtud-chip note-row-chip">
                              {um.idModule}
                            </span>
                            {nomMod}
                          </span>
                          <span class="col-dim note-row-coef">
                            coef {um.coeff}
                          </span>

                          {/* Session 1 note */}
                          {editingNote?.idModule === um.idModule &&
                              editingNote.field === "note"
                            ? (
                              <div style="display: flex; align-items: center; gap: 0.25rem">
                                <input
                                  class="form-input"
                                  style="width: 5rem; text-align: center; font-size: 0.85rem"
                                  value={editingNote.value}
                                  autoFocus
                                  onInput={(e) =>
                                    setEditingNote({
                                      ...editingNote,
                                      value:
                                        (e.target as HTMLInputElement).value,
                                    })}
                                  onKeyDown={(e) => {
                                    if (e.key === "Enter") {
                                      saveNote(
                                        um.idModule,
                                        "note",
                                        editingNote.value,
                                      );
                                    }
                                    if (e.key === "Escape") {
                                      setEditingNote(null);
                                    }
                                  }}
                                  onBlur={() =>
                                    saveNote(
                                      um.idModule,
                                      "note",
                                      editingNote.value,
                                    )}
                                />
                                <span
                                  class="col-dim"
                                  style="font-size: 0.75rem"
                                >
                                  /20
                                </span>
                              </div>
                            )
                            : (
                              <span
                                class={noteVal !== undefined
                                  ? noteClass(noteVal)
                                  : "note-chip note-chip--none"}
                                style="font-size: 0.78rem; cursor: pointer"
                                title="S1 — Cliquer pour modifier"
                                onClick={() =>
                                  setEditingNote({
                                    idModule: um.idModule,
                                    field: "note",
                                    value: noteVal !== undefined
                                      ? String(noteVal)
                                      : "",
                                  })}
                              >
                                S1:{" "}
                                {noteVal !== undefined ? fmt(noteVal) : "—/20"}
                              </span>
                            )}

                          {/* Session 2 note */}
                          {editingNote?.idModule === um.idModule &&
                              editingNote.field === "noteSession2"
                            ? (
                              <div style="display: flex; align-items: center; gap: 0.25rem">
                                <input
                                  class="form-input"
                                  style="width: 5rem; text-align: center; font-size: 0.85rem"
                                  value={editingNote.value}
                                  autoFocus
                                  placeholder="vide = suppr"
                                  onInput={(e) =>
                                    setEditingNote({
                                      ...editingNote,
                                      value:
                                        (e.target as HTMLInputElement).value,
                                    })}
                                  onKeyDown={(e) => {
                                    if (e.key === "Enter") {
                                      saveNote(
                                        um.idModule,
                                        "noteSession2",
                                        editingNote.value,
                                      );
                                    }
                                    if (e.key === "Escape") {
                                      setEditingNote(null);
                                    }
                                  }}
                                  onBlur={() =>
                                    saveNote(
                                      um.idModule,
                                      "noteSession2",
                                      editingNote.value,
                                    )}
                                />
                                <span
                                  class="col-dim"
                                  style="font-size: 0.75rem"
                                >
                                  /20
                                </span>
                              </div>
                            )
                            : (
                              <span
                                class={noteS2 != null
                                  ? noteClass(noteS2)
                                  : "note-chip note-chip--none"}
                                style="font-size: 0.78rem; cursor: pointer"
                                title="S2 — Cliquer pour modifier (vide = pas de session 2)"
                                onClick={() =>
                                  setEditingNote({
                                    idModule: um.idModule,
                                    field: "noteSession2",
                                    value: noteS2 != null ? String(noteS2) : "",
                                  })}
                              >
                                S2: {noteS2 != null ? fmt(noteS2) : "—"}
                              </span>
                            )}

                          {/* Effective note indicator */}
                          {noteS2 != null && (
                            <span
                              class="col-dim"
                              style="font-size: 0.72rem; font-style: italic"
                            >
                              → {fmt(effective!)}
                            </span>
                          )}
                        </div>
                      );
                    })}
                  </div>
                )}

              {/* Ajustement + Malus */}
              <div class="ajust-section">
                <p class="ajust-title">Ajustement de la moyenne UE</p>
                <p class="ajust-hint">
                  La valeur remplace la moyenne calculee. Le malus est
                  soustrait.
                </p>
                <div style="display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap">
                  <div style="display: flex; align-items: center; gap: 0.25rem">
                    <span class="col-dim" style="font-size: 0.8rem">
                      Val:
                    </span>
                    <input
                      class="form-input"
                      style="width: 4.5rem; text-align: center"
                      placeholder="—"
                      value={ajustInputs[ue.id]?.valeur ?? ""}
                      onInput={(e) =>
                        setAjustInputs((prev) => ({
                          ...prev,
                          [ue.id]: {
                            valeur: (e.target as HTMLInputElement).value,
                            malus: prev[ue.id]?.malus ?? "0",
                          },
                        }))}
                    />
                    <span class="col-dim" style="font-size: 0.8rem">/20</span>
                  </div>
                  <div style="display: flex; align-items: center; gap: 0.25rem">
                    <span class="col-dim" style="font-size: 0.8rem">
                      Malus:
                    </span>
                    <input
                      type="number"
                      class="form-input"
                      style="width: 4rem; text-align: center"
                      placeholder="0"
                      min="0"
                      value={ajustInputs[ue.id]?.malus ?? ""}
                      onInput={(e) =>
                        setAjustInputs((prev) => ({
                          ...prev,
                          [ue.id]: {
                            valeur: prev[ue.id]?.valeur ?? "",
                            malus: (e.target as HTMLInputElement).value,
                          },
                        }))}
                    />
                  </div>
                  <button
                    type="button"
                    class="btn btn-sm btn-primary"
                    onClick={() => applyAjust(ue.id)}
                  >
                    Appliquer
                  </button>
                  {ajust && (
                    <>
                      <button
                        type="button"
                        class="btn btn-sm btn-secondary"
                        onClick={() => resetAjust(ue.id)}
                      >
                        Reinitialiser
                      </button>
                      <span
                        class="col-dim"
                        style="font-size: 0.75rem; font-family: monospace"
                      >
                        Affiche : {fmt(ajust.valeur)}
                        {ajust.malus > 0
                          ? ` - ${ajust.malus} = ${
                            fmt(ajust.valeur - ajust.malus)
                          }`
                          : ""}
                        {avg !== null ? ` (calculee : ${fmt(avg)})` : ""}
                      </span>
                    </>
                  )}
                </div>
              </div>
            </div>
          );
        })}
    </div>
  );
}
