// src/components/keywords/export-enrichment-dialog.tsx
"use client";

import JSZip from "jszip";
import { useEffect, useMemo, useState } from "react";
import { toast } from "sonner";
import { Button } from "@/components/ui/button";
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogHeader,
  DialogTitle,
} from "@/components/ui/dialog";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import { markKeywordsExportedAction } from "@/lib/keywords/actions";
import {
  buildEnrichmentFiles,
  type EnrichmentMode,
  type EnrichmentTool,
  EXPORT_SCOPES,
  type ExportScope,
  hasNoMetric,
  isPendingForTool,
  keywordsForScope,
  TOOL_LIMITS,
} from "@/lib/keywords/export-enrichment";
import type { Keyword } from "@/lib/keywords/types";

const TOOL_LABELS: Record<EnrichmentTool, string> = {
  mangools: "Mangools (700)",
  semrush: "Semrush (2000)",
  ahrefs: "Ahrefs (10000)",
};

const TOOL_NAMES: Record<EnrichmentTool, string> = {
  mangools: "Mangools",
  semrush: "Semrush",
  ahrefs: "Ahrefs",
};

const SCOPE_LABELS: Record<ExportScope, string> = {
  selection: "Uniquement la sélection",
  "stale-week": "Data plus vieille qu'1 semaine",
  "stale-month": "Data plus vieille qu'1 mois",
  "never-synced": "Jamais synchronisé par l'outil",
};

function triggerDownload(blob: Blob, filename: string) {
  const url = URL.createObjectURL(blob);
  const a = document.createElement("a");
  a.href = url;
  a.download = filename;
  document.body.appendChild(a);
  a.click();
  a.remove();
  URL.revokeObjectURL(url);
}

export function ExportEnrichmentDialog({
  open,
  onOpenChange,
  keywords,
  siteId,
  scoped = false,
  loading = false,
  scopeSelectedIds,
  filePrefix = "recherche",
  onMarked,
}: {
  open: boolean;
  onOpenChange: (open: boolean) => void;
  keywords: Keyword[];
  siteId: string;
  /** Mode « portées » (réservoir) : `keywords` devient le réservoir filtré
   *  complet (pool des balayages de fraîcheur) et ce tableau porte les ids de
   *  la sélection courante. Absent = comportement historique (mode
   *  enrichir/rafraîchir sur `keywords`). */
  scopeSelectedIds?: string[];
  /** Préfixe des fichiers téléchargés (« recherche » historique, « reservoir »
   *  côté réservoir). */
  filePrefix?: string;
  /** True when `keywords` is the user's row selection (vs the whole stage). */
  scoped?: boolean;
  /** True while the caller is still resolving `keywords` server-side (P1:
   *  filter-mode selections/whole-stage exports are fetched on demand via
   *  `fetchKeywordsByFilterAction`, not synchronously from loaded rows) —
   *  disables the download and shows a loading state instead of a premature
   *  "aucun mot-clé" empty state. */
  loading?: boolean;
  /** Called after `markKeywordsExportedAction` resolves successfully, so the
   *  parent can refresh its in-session rows immediately. */
  onMarked?: () => void;
}) {
  const scopeMode = scopeSelectedIds !== undefined;
  const [tool, setTool] = useState<EnrichmentTool>("mangools");
  const [mode, setMode] = useState<EnrichmentMode>("missing");
  const [scope, setScope] = useState<ExportScope>("selection");

  // Référence temporelle des portées « périmé » — figée à l'ouverture pour que
  // les compteurs ne bougent pas sous la souris pendant que le dialog est ouvert.
  const [now, setNow] = useState(() => Date.now());
  useEffect(() => {
    if (open) setNow(Date.now());
  }, [open]);

  const selectedIdSet = useMemo(
    () => new Set(scopeSelectedIds ?? []),
    [scopeSelectedIds],
  );

  // Portée par défaut : la sélection si elle existe, sinon les jamais-synchro.
  // Recalée quand le pool finit de charger (les ids arrivent en async).
  useEffect(() => {
    if (open && !loading && scopeMode)
      setScope(selectedIdSet.size > 0 ? "selection" : "never-synced");
  }, [open, loading, scopeMode, selectedIdSet]);

  const scopedKeywords = useMemo(
    () =>
      scopeMode
        ? keywordsForScope(keywords, selectedIdSet, tool, scope, now)
        : keywords,
    [scopeMode, keywords, selectedIdSet, tool, scope, now],
  );
  // Compteurs affichés dans le sélecteur de portée (dépendent de l'outil).
  const scopeCounts = useMemo(
    () =>
      scopeMode
        ? Object.fromEntries(
            EXPORT_SCOPES.map((s) => [
              s,
              keywordsForScope(keywords, selectedIdSet, tool, s, now).length,
            ]),
          )
        : null,
    [scopeMode, keywords, selectedIdSet, tool, now],
  );

  const files = useMemo(
    // En mode portées, le filtrage est déjà fait par la portée : mode "all"
    // pour ne pas re-filtrer sur la source primaire.
    () => buildEnrichmentFiles(scopedKeywords, tool, scopeMode ? "all" : mode),
    [scopedKeywords, tool, scopeMode, mode],
  );
  const totalTerms = files.reduce((n, f) => n + f.terms.length, 0);
  // Mots-clés exclus car déjà exportés vers l'outil et en attente de retour de
  // métriques (ELEVA-49) — en mode portées, comptés sur la population de la
  // portée (sélection ou pool entier).
  const pendingCount = useMemo(() => {
    if (scopeMode) {
      const base =
        scope === "selection"
          ? keywords.filter((k) => selectedIdSet.has(k.id))
          : keywords;
      return base.filter((k) => isPendingForTool(k, tool)).length;
    }
    return mode === "missing"
      ? keywords.filter((k) => hasNoMetric(k) && isPendingForTool(k, tool))
          .length
      : 0;
  }, [scopeMode, scope, keywords, selectedIdSet, tool, mode]);

  async function handleExport() {
    if (!files.length) return;
    // Filename carries country + language so the (country, language) targeting
    // unit is explicit when pasting into the tool (and FR/fr vs FR/ur don't clash).
    const name = (f: { country: string; language: string; index: number }) =>
      `${filePrefix}-${siteId}-${tool}-${f.country}-${f.language}-${f.index}.txt`;
    try {
      if (files.length === 1) {
        triggerDownload(
          new Blob([files[0].terms.join("\n")], { type: "text/plain" }),
          name(files[0]),
        );
      } else {
        const zip = new JSZip();
        for (const f of files) zip.file(name(f), f.terms.join("\n"));
        const blob = await zip.generateAsync({ type: "blob" });
        triggerDownload(blob, `${filePrefix}-${siteId}-${tool}.zip`);
      }
      // Marquage « exporté » fire-and-forget : l'UX du téléchargement reste
      // inchangée, le refresh récupère les enrichmentExports à jour.
      const exportedIds = files.flatMap((f) => f.ids);
      markKeywordsExportedAction(siteId, exportedIds, tool)
        .then(() => onMarked?.())
        .catch(() => toast.error("Échec du marquage « exporté »"));
      toast.success(
        `Export généré — ${totalTerms} mot(s)-clé(s), ${files.length} fichier(s)`,
      );
      onOpenChange(false);
    } catch {
      toast.error("Échec de la génération de l'export");
    }
  }

  return (
    <Dialog open={open} onOpenChange={onOpenChange}>
      <DialogContent>
        <DialogHeader>
          <DialogTitle>Exporter pour enrichissement</DialogTitle>
          <DialogDescription>
            Liste de mots-clés{scoped ? " de la sélection" : ""}, prête à coller
            dans l'outil choisi (1 terme par ligne, découpée à sa limite
            d'import).
          </DialogDescription>
        </DialogHeader>

        <div className="flex flex-col gap-3">
          {scopeMode ? (
            <Select
              value={scope}
              onValueChange={(v) => setScope(v as ExportScope)}
            >
              <SelectTrigger className="w-full" aria-label="Portée de l'export">
                <SelectValue />
              </SelectTrigger>
              <SelectContent position="popper">
                {EXPORT_SCOPES.map((s) => (
                  <SelectItem
                    key={s}
                    value={s}
                    disabled={s === "selection" && selectedIdSet.size === 0}
                  >
                    {SCOPE_LABELS[s]}
                    {loading ? "" : ` (${scopeCounts?.[s] ?? 0})`}
                  </SelectItem>
                ))}
              </SelectContent>
            </Select>
          ) : (
            <Select
              value={mode}
              onValueChange={(v) => setMode(v as EnrichmentMode)}
            >
              <SelectTrigger className="w-full" aria-label="Mode d'export">
                <SelectValue />
              </SelectTrigger>
              <SelectContent position="popper">
                <SelectItem value="missing">
                  Sans métrique uniquement (enrichir)
                </SelectItem>
                <SelectItem value="all">
                  Toute la sélection (rafraîchir les métriques)
                </SelectItem>
              </SelectContent>
            </Select>
          )}

          <Select
            value={tool}
            onValueChange={(v) => setTool(v as EnrichmentTool)}
          >
            <SelectTrigger className="w-full">
              <SelectValue />
            </SelectTrigger>
            <SelectContent position="popper">
              {(Object.keys(TOOL_LABELS) as EnrichmentTool[]).map((t) => (
                <SelectItem key={t} value={t}>
                  {TOOL_LABELS[t]}
                </SelectItem>
              ))}
            </SelectContent>
          </Select>

          <p className="text-muted-foreground text-sm">
            {loading
              ? "Chargement des mots-clés…"
              : totalTerms === 0
                ? scopeMode
                  ? "Aucun mot-clé dans cette portée pour cet outil."
                  : mode === "missing"
                    ? "Aucun mot-clé à enrichir (tous ont déjà des métriques) — passe en « rafraîchir » pour tout exporter."
                    : "Aucun mot-clé dans la sélection."
                : `${totalTerms} mot(s)-clé(s) · ${files.length} fichier(s) (≤ ${TOOL_LIMITS[tool]} / fichier)`}
          </p>
          {!loading && pendingCount > 0 && (
            <p className="text-muted-foreground text-xs">
              {pendingCount} déjà exporté(s) vers {TOOL_NAMES[tool]}, en attente
              de retour
            </p>
          )}
        </div>

        <div className="flex justify-end gap-2">
          <Button variant="outline" onClick={() => onOpenChange(false)}>
            Annuler
          </Button>
          <Button onClick={handleExport} disabled={loading || totalTerms === 0}>
            Télécharger
          </Button>
        </div>
      </DialogContent>
    </Dialog>
  );
}
