"use client";
import { useState, useEffect, useCallback } from "react";
import { getModeldocumentenDb } from "@/lib/db/modeldocumenten-db";
import { useGesynchroniseerdeOpslag } from "@/lib/hooks/useGesynchroniseerdeOpslag";
import { useOnderdeelHistoriekStorage } from "@/lib/hooks/useOnderdeelHistoriekStorage";
import { bumpVersie } from "@/data/modeldocumenten";
import type {
  Modeldocument,
  Modelonderdeel,
  Wijzigingsvoorstel,
  ModeldocumentVoorstel,
} from "@/data/modeldocumenten";

async function lokaleOnderdelenLezen(): Promise<Modelonderdeel[]> {
  const db = await getModeldocumentenDb();
  return db.getAll("onderdelen");
}
async function lokaalOnderdeelSchrijven(item: Modelonderdeel): Promise<void> {
  const db = await getModeldocumentenDb();
  await db.put("onderdelen", item);
}
async function lokaalOnderdeelVerwijderen(id: string): Promise<void> {
  const db = await getModeldocumentenDb();
  await db.delete("onderdelen", id);
}

async function lokaleModellenLezen(): Promise<Modeldocument[]> {
  const db = await getModeldocumentenDb();
  return db.getAll("modellen");
}
async function lokaalModelSchrijven(item: Modeldocument): Promise<void> {
  const db = await getModeldocumentenDb();
  await db.put("modellen", item);
}
async function lokaalModelVerwijderen(id: string): Promise<void> {
  const db = await getModeldocumentenDb();
  await db.delete("modellen", id);
}

/**
 * Zet een goedgekeurd voorstel-onderdeel om naar het eindstadium van een
 * rechtstreeks opgeladen onderdeel: versie "1.0" en nazicht "actueel" (de
 * notaris heeft de tekst net gevalideerd via "Te valideren"), in plaats van
 * de "0.1"/"na_te_kijken"-markering van vóór de goedkeuring.
 */
function gevalideerdOnderdeel(onderdeel: Modelonderdeel): Modelonderdeel {
  return {
    ...onderdeel,
    versie: "1.0",
    nazicht: {
      status: "actueel",
      datum: new Date().toISOString().slice(0, 10),
    },
  };
}

/** Resultaat van een synchronisatie met de serverwachtrij. */
export interface SyncResultaat {
  onderdeelVoorstellen: number;
  modelVoorstellen: number;
  fout?: string;
}

/**
 * Opslag van door de gebruiker toegevoegde modelonderdelen en
 * modeldocumenten in IndexedDB. Seed-data blijft statisch in
 * data/modeldocumenten; deze hook beheert enkel de gebruikersitems
 * (isGebruiker: true) en de voorstellen ter validatie. Voorstellen die
 * externe AI-agenten via de MCP-server indienen, worden met
 * synchroniseerVanServer() opgehaald uit de serverwachtrij.
 */
export function useModeldocumentenStorage() {
  const {
    items: gebruikersOnderdelen,
    geladen: onderdelenGeladen,
    slaOp: slaOnderdeelOpRaw,
    verwijder: verwijderOnderdeel,
  } = useGesynchroniseerdeOpslag<Modelonderdeel>(
    "/api/sync/modeldocumenten-onderdelen",
    lokaleOnderdelenLezen,
    lokaalOnderdeelSchrijven,
    lokaalOnderdeelVerwijderen
  );
  const {
    items: gebruikersModellen,
    geladen: modellenGeladen,
    slaOp: slaModelOpRaw,
    verwijder: verwijderModel,
  } = useGesynchroniseerdeOpslag<Modeldocument>(
    "/api/sync/modeldocumenten-modellen",
    lokaleModellenLezen,
    lokaalModelSchrijven,
    lokaalModelVerwijderen
  );
  const { historiek, slaHistoriekOp } = useOnderdeelHistoriekStorage();
  const [voorstellen, setVoorstellen] = useState<Wijzigingsvoorstel[]>([]);
  const [modelVoorstellen, setModelVoorstellen] = useState<ModeldocumentVoorstel[]>([]);
  const [voorstellenGeladen, setVoorstellenGeladen] = useState(false);
  const geladen = onderdelenGeladen && modellenGeladen && voorstellenGeladen;

  useEffect(() => {
    async function laadVoorstellen() {
      const db = await getModeldocumentenDb();
      const [voorstellen, modelvoorstellen] = await Promise.all([
        db.getAll("voorstellen"),
        db.getAll("modelvoorstellen"),
      ]);
      setVoorstellen(voorstellen);
      setModelVoorstellen(modelvoorstellen);
      setVoorstellenGeladen(true);
    }
    laadVoorstellen();
  }, []);

  const slaOnderdeelOp = useCallback(
    async (onderdeel: Modelonderdeel) => {
      // Vóór elke bewerking van een bestaand eigen onderdeel wordt de huidige
      // staat naar de historiek geschreven, zodat de notaris ze later kan
      // terugzetten. Nieuwe onderdelen hebben uiteraard nog geen historiek.
      const vorigeStaat = gebruikersOnderdelen.find((o) => o.id === onderdeel.id);
      if (vorigeStaat) {
        const bestaandeHistoriek = historiek.find((h) => h.id === onderdeel.id);
        await slaHistoriekOp({
          id: onderdeel.id,
          versies: [...(bestaandeHistoriek?.versies ?? []), vorigeStaat],
        });
      }
      await slaOnderdeelOpRaw({ ...onderdeel, isGebruiker: true });
    },
    [slaOnderdeelOpRaw, gebruikersOnderdelen, historiek, slaHistoriekOp]
  );

  /**
   * Zet een oudere versie van een eigen onderdeel terug als de huidige staat.
   * Dit is zelf ook een bewerking: de huidige (nu te vervangen) staat wordt
   * via slaOnderdeelOp eerst naar de historiek geschreven, met een verhoogd
   * versienummer en de datum van vandaag, zodat het duidelijk een nieuwe
   * bewaring is — geen tijdreis in de eigenlijke recordhistoriek.
   */
  const herstelVersie = useCallback(
    async (oudeVersie: Modelonderdeel) => {
      const huidige = gebruikersOnderdelen.find((o) => o.id === oudeVersie.id);
      await slaOnderdeelOp({
        ...oudeVersie,
        versie: huidige ? bumpVersie(huidige.versie) : oudeVersie.versie,
        datum: new Date().toISOString().slice(0, 10),
      });
    },
    [gebruikersOnderdelen, slaOnderdeelOp]
  );

  const slaModelOp = useCallback(
    async (model: Modeldocument) => {
      await slaModelOpRaw({ ...model, isGebruiker: true });
    },
    [slaModelOpRaw]
  );

  const slaVoorstelOp = useCallback(async (voorstel: Wijzigingsvoorstel) => {
    const db = await getModeldocumentenDb();
    await db.put("voorstellen", voorstel);
    setVoorstellen((prev) => {
      const bestaat = prev.some((v) => v.id === voorstel.id);
      return bestaat ? prev.map((v) => (v.id === voorstel.id ? voorstel : v)) : [...prev, voorstel];
    });
  }, []);

  const verwijderVoorstel = useCallback(async (id: string) => {
    const db = await getModeldocumentenDb();
    await db.delete("voorstellen", id);
    setVoorstellen((prev) => prev.filter((v) => v.id !== id));
  }, []);

  /**
   * Neemt het voorgestelde onderdeel over in de bibliotheek en verwijdert het
   * voorstel uit de wachtrij. Goedkeuren is het juridisch nazicht: de notaris
   * heeft de tekst net gecontroleerd, dus het resultaat krijgt hetzelfde
   * eindstadium als een rechtstreeks hier opgeladen onderdeel (versie "1.0",
   * nazicht "actueel") — geen blijvend "na_te_kijken/0.1"-stempel van vóór de
   * validatie.
   */
  const keurVoorstelGoed = useCallback(
    async (voorstel: Wijzigingsvoorstel) => {
      await slaOnderdeelOp(gevalideerdOnderdeel(voorstel.voorgesteldOnderdeel));
      await verwijderVoorstel(voorstel.id);
    },
    [slaOnderdeelOp, verwijderVoorstel]
  );

  const slaModelVoorstelOp = useCallback(async (voorstel: ModeldocumentVoorstel) => {
    const db = await getModeldocumentenDb();
    await db.put("modelvoorstellen", voorstel);
    setModelVoorstellen((prev) => {
      const bestaat = prev.some((v) => v.id === voorstel.id);
      return bestaat ? prev.map((v) => (v.id === voorstel.id ? voorstel : v)) : [...prev, voorstel];
    });
  }, []);

  const verwijderModelVoorstel = useCallback(async (id: string) => {
    const db = await getModeldocumentenDb();
    await db.delete("modelvoorstellen", id);
    setModelVoorstellen((prev) => prev.filter((v) => v.id !== id));
  }, []);

  /**
   * Neemt een voorgesteld modeldocument over in de bibliotheek: eerst de
   * meegestuurde nieuwe onderdelen, dan het model zelf, en verwijdert tot slot
   * het voorstel uit de wachtrij.
   */
  const keurModelVoorstelGoed = useCallback(
    async (voorstel: ModeldocumentVoorstel) => {
      for (const onderdeel of voorstel.nieuweOnderdelen) {
        await slaOnderdeelOp(gevalideerdOnderdeel(onderdeel));
      }
      await slaModelOp({ ...voorstel.voorgesteldModel, versie: "1.0" });
      await verwijderModelVoorstel(voorstel.id);
    },
    [slaOnderdeelOp, slaModelOp, verwijderModelVoorstel]
  );

  /**
   * Haalt de voorstellen op die externe AI-agenten via de MCP-server indienden
   * (zowel onderdeel- als modelvoorstellen), neemt nieuwe over in IndexedDB en
   * verwijdert ze daarna uit de serverwachtrij (consume-on-sync). Bestaande
   * voorstellen worden op id ontdubbeld, zodat herhaald synchroniseren veilig is.
   */
  const synchroniseerVanServer = useCallback(async (): Promise<SyncResultaat> => {
    try {
      const [onderdeelRes, modelRes] = await Promise.all([
        fetch("/api/modeldocumenten/voorstellen", { headers: { Accept: "application/json" } }),
        fetch("/api/modeldocumenten/modelvoorstellen", { headers: { Accept: "application/json" } }),
      ]);
      const onderdeelData = (await onderdeelRes.json()) as { voorstellen?: Wijzigingsvoorstel[] };
      const modelData = (await modelRes.json()) as { voorstellen?: ModeldocumentVoorstel[] };

      const db = await getModeldocumentenDb();
      const bestaandeOnderdeelIds = new Set((await db.getAllKeys("voorstellen")) as string[]);
      const bestaandeModelIds = new Set((await db.getAllKeys("modelvoorstellen")) as string[]);

      let nieuweOnderdeelVoorstellen = 0;
      for (const v of onderdeelData.voorstellen ?? []) {
        if (!bestaandeOnderdeelIds.has(v.id)) {
          await slaVoorstelOp(v);
          nieuweOnderdeelVoorstellen += 1;
        }
        // Consume-on-sync: uit de serverwachtrij verwijderen (best effort).
        await fetch(`/api/modeldocumenten/voorstellen?id=${encodeURIComponent(v.id)}`, {
          method: "DELETE",
        }).catch(() => {});
      }

      let nieuweModelVoorstellen = 0;
      for (const v of modelData.voorstellen ?? []) {
        if (!bestaandeModelIds.has(v.id)) {
          await slaModelVoorstelOp(v);
          nieuweModelVoorstellen += 1;
        }
        await fetch(`/api/modeldocumenten/modelvoorstellen?id=${encodeURIComponent(v.id)}`, {
          method: "DELETE",
        }).catch(() => {});
      }

      return {
        onderdeelVoorstellen: nieuweOnderdeelVoorstellen,
        modelVoorstellen: nieuweModelVoorstellen,
      };
    } catch (err) {
      return {
        onderdeelVoorstellen: 0,
        modelVoorstellen: 0,
        fout: err instanceof Error ? err.message : String(err),
      };
    }
  }, [slaVoorstelOp, slaModelVoorstelOp]);

  return {
    gebruikersOnderdelen,
    gebruikersModellen,
    voorstellen,
    modelVoorstellen,
    historiek,
    herstelVersie,
    geladen,
    slaOnderdeelOp,
    verwijderOnderdeel,
    slaModelOp,
    verwijderModel,
    slaVoorstelOp,
    verwijderVoorstel,
    keurVoorstelGoed,
    slaModelVoorstelOp,
    verwijderModelVoorstel,
    keurModelVoorstelGoed,
    synchroniseerVanServer,
  };
}
