// src/components/keywords/import-keywords-dialog.tsx — single keyword import.
// Quick-import UX (drag-drop, auto-detected source, language + destination
// pickers) on top of the DuckDB analytics engine: the CSV is uploaded to R2 and
// crunched server-side (dedup, 50 Mo, SE-Ranking/Semrush/Mangools/Ahrefs), so
// there is one import path and no more « ON CONFLICT » crash on duplicate rows.
"use client";

import { ImportIcon } from "lucide-react";
import { useEffect, useRef, useState } from "react";
import { toast } from "sonner";
import { SingleCombobox } from "@/components/site/single-combobox";
import { Button } from "@/components/ui/button";
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogHeader,
  DialogTitle,
} from "@/components/ui/dialog";
import { Label } from "@/components/ui/label";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import { getAnalyticsJob } from "@/lib/analytics/actions";
import { detectKeywordSource } from "@/lib/keywords/import/detect-file";
import { useActiveSite } from "@/lib/keywords/site-context";
import type { KeywordSource } from "@/lib/keywords/types";
import { LANGUAGE_OPTIONS, siteLanguageOptions } from "@/lib/sites/options";

type Source = KeywordSource | "unknown";
type Destination = "reservoir" | "recherche";

const MAX_BYTES = 50 * 1024 * 1024; // 50 Mo
const POLL_MS = 2000;

export function ImportKeywordsDialog({
  open,
  onOpenChange,
  siteId,
  onImported,
  defaultLang = "en",
  initialFile = null,
}: {
  open: boolean;
  onOpenChange: (o: boolean) => void;
  siteId: string;
  /** Job terminé. `inserted` = lignes réellement entrées en base (0 est un
   *  retour légitime : CSV vide ou intégralement en doublons). Les appelants
   *  qui se contentent de rafraîchir peuvent l'ignorer ; ceux qui NAVIGUENT
   *  doivent le lire. */
  onImported: (inserted: number) => void;
  /** Site primary language — default for the « Langue » picker (locale not in
   *  the CSV for Semrush/SE-Ranking). The picker offers the site's configured
   *  languages (primary + i18n), falling back to the full Google Ads set only
   *  when no active site is resolved. */
  defaultLang?: string;
  /** File already picked upstream (eg. SourcesModal auto-detecting a keywords
   *  CSV) — when the dialog opens with a non-null value it is run through
   *  the exact same `pickFile()` path as a manual drop/selection (.csv check,
   *  50 Mo cap, source detection), so there's no separate validation path.
   *  Only consumed on the transition into `open`; picking another file
   *  afterwards behaves like the manual flow (no re-forcing). */
  initialFile?: File | null;
}) {
  const { activeSite } = useActiveSite();
  // Liste langue restreinte aux langues du site actif (principale + i18n) ;
  // repli sur l'univers Google Ads tant que le site n'est pas résolu.
  const langOptions = activeSite
    ? siteLanguageOptions(activeSite)
    : LANGUAGE_OPTIONS;

  const [file, setFile] = useState<File | null>(null);
  const [source, setSource] = useState<Source>("unknown");
  const [lang, setLang] = useState<string>(defaultLang);
  // Réservoir (master pool) by default; « Recherche » also tags the keywords
  // with the `research` stage so they appear in the Recherche subset view.
  const [destination, setDestination] = useState<Destination>("reservoir");
  const [pending, setPending] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const inputRef = useRef<HTMLInputElement>(null);
  const pollRef = useRef<ReturnType<typeof setInterval> | null>(null);

  function clearPoll() {
    if (pollRef.current) {
      clearInterval(pollRef.current);
      pollRef.current = null;
    }
  }

  // Clear any pending poll on unmount (read the ref inside so there are no deps).
  useEffect(
    () => () => {
      if (pollRef.current) clearInterval(pollRef.current);
    },
    [],
  );

  const wasOpenRef = useRef(false);

  // Run initialFile through the same pickFile() path as a manual drop, but
  // only on the closed→open transition — not on every initialFile change
  // while already open, so re-picking a different file later isn't overridden.
  // biome-ignore lint/correctness/useExhaustiveDependencies: pickFile only touches state setters (stable identity), safe to omit from deps.
  useEffect(() => {
    const justOpened = open && !wasOpenRef.current;
    wasOpenRef.current = open;
    if (justOpened && initialFile) {
      pickFile(initialFile);
    }
  }, [open, initialFile]);

  function reset() {
    setFile(null);
    setSource("unknown");
    setLang(defaultLang);
    setDestination("reservoir");
    setPending(false);
    setError(null);
  }

  async function pickFile(f: File) {
    if (!f.name.toLowerCase().endsWith(".csv")) {
      setError("Le fichier doit être un .csv");
      return;
    }
    if (f.size > MAX_BYTES) {
      setError("Fichier trop lourd (max 50 Mo).");
      return;
    }
    setError(null);
    // Read just the header slice to auto-detect the source (don't load 50 Mo).
    const head = await f.slice(0, 64 * 1024).text();
    const header = head.split(/\r?\n/, 1)[0] ?? "";
    setSource(detectKeywordSource(header) ?? "unknown");
    setFile(f);
  }

  function startPolling(jobId: string) {
    clearPoll();
    pollRef.current = setInterval(async () => {
      const job = await getAnalyticsJob(jobId);
      if (!job) return;
      if (job.status === "done") {
        clearPoll();
        setPending(false);
        // Un job « done » n'a pas forcément inséré quoi que ce soit (CSV vide,
        // ou entièrement en doublons). Le nombre est donc transmis à
        // `onImported` : un appelant qui NAVIGUE au retour (le wizard
        // d'onboarding) doit pouvoir ne pas le faire quand rien n'est entré —
        // sans quoi la navigation détruit le toast ci-dessous avant qu'il soit
        // lu, et l'écran se recharge à l'identique sans explication.
        const inserted = job.totals.upserted ?? job.totals.created ?? 0;
        toast.success(`Import terminé — ${inserted} mots-clés`);
        onImported(inserted);
        onOpenChange(false);
        reset();
      } else if (job.status === "failed") {
        clearPoll();
        setPending(false);
        toast.error(job.error ?? "Import échoué");
      }
    }, POLL_MS);
  }

  async function submit() {
    if (!file || source === "unknown") return;
    setPending(true);
    setError(null);
    try {
      const body = new FormData();
      body.set("siteId", siteId);
      body.set("source", source);
      body.set("lang", lang);
      body.set("destination", destination);
      body.set("file", file);
      const res = await fetch("/keywords/import-heavy", {
        method: "POST",
        body,
      });
      const data = (await res.json()) as
        | { ok: true; jobId: string }
        | { ok: false; error: string };
      if (!data.ok) {
        setPending(false);
        setError(data.error);
        toast.error(data.error);
        return;
      }
      toast.success("Import lancé");
      startPolling(data.jobId);
    } catch {
      setPending(false);
      setError("Échec de l'envoi du fichier.");
      toast.error("Échec de l'envoi du fichier.");
    }
  }

  return (
    <Dialog
      open={open}
      onOpenChange={(o) => {
        if (!o) {
          clearPoll();
          reset();
        }
        onOpenChange(o);
      }}
    >
      <DialogContent className="max-w-lg">
        <DialogHeader>
          <DialogTitle>Importer des mots-clés</DialogTitle>
          <DialogDescription>
            Export Mangools, Semrush, SE Ranking ou Ahrefs — la source est
            détectée automatiquement, le fichier est traité en arrière-plan.
          </DialogDescription>
        </DialogHeader>

        {error ? (
          <p className="text-destructive text-sm" role="alert">
            {error}
          </p>
        ) : null}

        <div className="grid gap-3">
          <button
            type="button"
            onClick={() => inputRef.current?.click()}
            onDragOver={(e) => e.preventDefault()}
            onDrop={(e) => {
              e.preventDefault();
              const f = e.dataTransfer.files[0];
              if (f) pickFile(f);
            }}
            className="flex h-36 w-full flex-col items-center justify-center gap-2 rounded-lg border border-input border-dashed text-muted-foreground text-sm"
          >
            <ImportIcon className="size-6" />
            {file
              ? `${file.name} (${(file.size / 1024 / 1024).toFixed(1)} Mo)`
              : "Dépose ton CSV ici, ou clique pour parcourir"}
            <input
              ref={inputRef}
              type="file"
              accept=".csv,text/csv"
              className="hidden"
              onChange={(e) => {
                const f = e.target.files?.[0];
                if (f) pickFile(f);
              }}
            />
          </button>

          {file ? (
            <>
              {source === "unknown" ? (
                <p className="text-destructive text-sm" role="alert">
                  Format non reconnu — choisis la source manuellement.
                </p>
              ) : null}
              <div className="flex flex-wrap items-end gap-3">
                <div className="flex items-center gap-2 text-sm">
                  <span className="text-muted-foreground">Source :</span>
                  <Select
                    value={source}
                    onValueChange={(v) => setSource(v as Source)}
                    disabled={pending}
                  >
                    <SelectTrigger className="w-40" aria-label="Source">
                      <SelectValue placeholder="Choisir…" />
                    </SelectTrigger>
                    <SelectContent position="popper">
                      <SelectItem value="mangools">Mangools</SelectItem>
                      <SelectItem value="semrush">Semrush</SelectItem>
                      <SelectItem value="seranking">SE Ranking</SelectItem>
                      <SelectItem value="ahrefs">Ahrefs</SelectItem>
                    </SelectContent>
                  </Select>
                </div>
                <div className="flex items-center gap-2 text-sm">
                  <span className="text-muted-foreground">Langue :</span>
                  <div className="w-44">
                    <SingleCombobox
                      options={langOptions}
                      value={lang}
                      onValueChange={setLang}
                      placeholder="Langue"
                      ariaLabel="Langue des mots-clés"
                    />
                  </div>
                </div>
                <div className="grid gap-1">
                  <Label>Envoyer vers</Label>
                  <div className="flex gap-1">
                    <Button
                      type="button"
                      size="sm"
                      variant={
                        destination === "reservoir" ? "default" : "outline"
                      }
                      onClick={() => setDestination("reservoir")}
                      disabled={pending}
                    >
                      Réservoir
                    </Button>
                    <Button
                      type="button"
                      size="sm"
                      variant={
                        destination === "recherche" ? "default" : "outline"
                      }
                      onClick={() => setDestination("recherche")}
                      disabled={pending}
                    >
                      Recherche
                    </Button>
                  </div>
                </div>
              </div>
              {source === "ahrefs" ? (
                <p className="text-muted-foreground text-xs">
                  Ahrefs : exporte en <strong>CSV UTF-8</strong> (l'export
                  UTF-16 par défaut n'est pas lisible). Les lignes SERP par
                  mot-clé sont dédupliquées automatiquement.
                </p>
              ) : null}
            </>
          ) : null}

          <div className="flex justify-end gap-2">
            <Button
              variant="outline"
              onClick={reset}
              disabled={pending || !file}
            >
              Autre fichier
            </Button>
            <Button
              onClick={submit}
              disabled={pending || !file || source === "unknown"}
            >
              {pending ? "Import en cours…" : "Lancer l'import"}
            </Button>
          </div>
        </div>
      </DialogContent>
    </Dialog>
  );
}
