"use client";

import { useEffect, useRef } from "react";
import { useRouter } from "next/navigation";
import { toast } from "sonner";
import { importJobsAction } from "@/components/keywords/import-jobs";
import type { ActionFailure } from "@/lib/actions";
import { reconcileToasts, type ToastAction, type TrackedJob } from "@/lib/job-toasts";

// Notification persistante de **tout** travail en arrière-plan (règle du 2026-09-01).
//
// Il vit dans le layout `(app)` : le toast survit donc à la navigation, et le premier tour de
// polling le réhydrate après un rechargement complet — un import lancé puis abandonné dans un
// autre onglet se signale de nouveau tout seul.
//
// Il n'entre pas en conflit avec le `JobWatcher` de la page d'import : le même travail y est
// visible deux fois, en détail sur sa page et en résumé dans le coin de l'écran. Ce sont deux
// surfaces, pas deux notifications.

/** Rythme du sondage : assez lent pour être gratuit, assez vif pour ne pas paraître figé. */
const INTERVAL_MS = 10_000;

/** Une source de travaux de fond : une Server Action qui rend l'état courant des siens. */
type JobSource = () => Promise<{ jobs: TrackedJob[] } | ActionFailure>;

/**
 * Les sources sondées. Une seule aujourd'hui — les imports ; Crawl et Search Console
 * ajouteront la leur ici, et rien d'autre ne bougera.
 */
const SOURCES: readonly JobSource[] = [importJobsAction];

export function JobsToaster() {
  const router = useRouter();
  // Identifiants des travaux ayant un toast ouvert. Dans une ref, jamais dans un state :
  // le sondage ne doit pas provoquer de rendu, et l'effet ne doit pas se relancer à chaque
  // tour de boucle.
  const tracked = useRef<ReadonlySet<string>>(new Set());

  useEffect(() => {
    let cancelled = false;
    let timer: ReturnType<typeof setTimeout> | undefined;

    const tick = async () => {
      const jobs: TrackedJob[] = [];
      let complete = true;
      for (const source of SOURCES) {
        try {
          const result = await source();
          if ("jobs" in result) jobs.push(...result.jobs);
          else complete = false;
        } catch {
          // Api injoignable, session en cours de renouvellement, dev server redémarré.
          complete = false;
        }
      }
      if (cancelled) return;
      // Un tour incomplet est **ignoré**, il n'est pas traité comme « plus rien ne tourne » :
      // sinon la moindre coupure réseau fermerait tous les toasts en cours et l'utilisateur
      // croirait son import terminé alors qu'il avance très bien côté serveur.
      if (complete) {
        const { actions, tracked: next } = reconcileToasts(tracked.current, jobs);
        tracked.current = next;
        for (const action of actions) show(action, router);
      }
      timer = setTimeout(() => void tick(), INTERVAL_MS);
    };

    void tick();
    return () => {
      cancelled = true;
      if (timer !== undefined) clearTimeout(timer);
    };
  }, [router]);

  return null;
}

/** Durée d'un toast de conclusion : le temps de le lire et d'aller au rapport. */
const FINISHED_DURATION_MS = 10_000;

function show(action: ToastAction, router: ReturnType<typeof useRouter>): void {
  if (action.kind === "dismiss") {
    toast.dismiss(action.id);
    return;
  }
  const { job } = action;
  // `id` stable = le toast est **mis à jour en place** à chaque tour, jamais empilé, et le
  // toast de chargement devient celui de conclusion sans clignoter.
  const common = { id: job.id, description: job.detail ?? undefined };
  if (action.kind === "loading") {
    toast.loading(job.title, {
      ...common,
      // Persistant : il ne s'efface qu'à la fin du travail, pas au bout de quatre secondes.
      // L'icône n'est pas posée ici : le `Toaster` donne le Quantum à tout toast `loading`.
      duration: Number.POSITIVE_INFINITY,
    });
    return;
  }
  const href = job.href;
  const options = {
    ...common,
    duration: FINISHED_DURATION_MS,
    action:
      href === null ? undefined : { label: "Voir le rapport", onClick: () => router.push(href) },
  };
  if (action.kind === "error") toast.error(job.title, options);
  else toast.success(job.title, options);
  // Les tables et badges rendus côté serveur affichent encore « En cours » : les relire
  // maintenant, sinon l'écran contredit le toast qui vient de s'afficher.
  router.refresh();
}
