'use client';

import { useCallback, useEffect, useMemo, useState } from 'react';
import { isAxiosError } from 'axios';
import {
  ChevronDown,
  ChevronLeft,
  ChevronRight,
  Loader2,
  RotateCcw,
  ScrollText,
  Search,
} from 'lucide-react';

import { Button } from '@/components/ui/button';
import {
  Card,
  CardContent,
  CardDescription,
  CardHeader,
  CardTitle,
} from '@/components/ui/card';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from '@/components/ui/table';
import {
  extractApiErrorMessage,
  fetchJournal,
  type JournalEntree,
  type JournalFiltres,
  type JournalLecture,
} from '@/lib/api';
import {
  FONCTIONS_JOURNAL,
  formatDateJournal,
  libelleFonction,
} from '@/lib/journal';
import { cn } from '@/lib/utils';

const PAGE_SIZE_OPTIONS = [25, 50, 100] as const;

const selectClassName =
  'flex h-10 rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50';

const FILTRES_VIDES: Required<JournalFiltres> = {
  idu: '',
  id_cont: '',
  depuis: '',
  jusqua: '',
  idu_on_uti_debut: '',
  idu_on_uti_fin: '',
  evenement_depuis: '',
  evenement_jusqua: '',
  fonction: '',
};

function formatBytes(bytes: number | null): string {
  if (bytes === null) {
    return '—';
  }

  if (bytes < 1024) {
    return `${bytes} o`;
  }

  if (bytes < 1024 * 1024) {
    return `${Math.round(bytes / 1024)} Ko`;
  }

  return `${(bytes / (1024 * 1024)).toFixed(1)} Mo`;
}

function toIsoUtc(valeurLocale: string): string {
  if (!valeurLocale) {
    return '';
  }

  const date = new Date(valeurLocale);

  return Number.isNaN(date.getTime()) ? '' : date.toISOString();
}

type JournalPanelProps = {
  filtresInitiaux?: JournalFiltres;
};

export function JournalPanel({ filtresInitiaux }: JournalPanelProps = {}) {
  const [filtres, setFiltres] = useState<Required<JournalFiltres>>({
    ...FILTRES_VIDES,
    ...filtresInitiaux,
  });
  const [lecture, setLecture] = useState<JournalLecture | null>(null);
  const [isLoading, setIsLoading] = useState(true);
  const [loadError, setLoadError] = useState<string | null>(null);
  const [page, setPage] = useState(1);
  const [pageSize, setPageSize] = useState<(typeof PAGE_SIZE_OPTIONS)[number]>(
    PAGE_SIZE_OPTIONS[0],
  );
  const [ligneOuverte, setLigneOuverte] = useState<string | null>(null);

  // §5.2.6 : si l'IDU est renseigné, les filtres de date et d'ID_ON_UTI sont interdits.
  const iduExclusif = filtres.idu.trim() !== '';

  const chargerJournal = useCallback(async (criteres: JournalFiltres) => {
    setIsLoading(true);
    setLoadError(null);

    try {
      setLecture(await fetchJournal(criteres));
      setPage(1);
      setLigneOuverte(null);
    } catch (error) {
      if (isAxiosError(error) && error.response?.status === 403) {
        setLoadError(
          'Votre profil ne permet pas de lire le journal (fonction « Lire journal » requise).',
        );
      } else {
        setLoadError(
          extractApiErrorMessage(error, 'Impossible de lire le journal.'),
        );
      }
    } finally {
      setIsLoading(false);
    }
  }, []);

  useEffect(() => {
    void chargerJournal(filtresInitiaux ?? {});
    // Les filtres initiaux proviennent de l'URL : ils ne changent pas sans remontage.
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [chargerJournal]);

  const entrees = useMemo(() => lecture?.entrees ?? [], [lecture]);
  const totalPages = Math.max(1, Math.ceil(entrees.length / pageSize));
  const currentPage = Math.min(page, totalPages);

  const entreesPage = useMemo(() => {
    const start = (currentPage - 1) * pageSize;
    return entrees.slice(start, start + pageSize);
  }, [entrees, currentPage, pageSize]);

  const rangeStart = entrees.length === 0 ? 0 : (currentPage - 1) * pageSize + 1;
  const rangeEnd = Math.min(currentPage * pageSize, entrees.length);

  function onChangeFiltre(champ: keyof JournalFiltres, valeur: string) {
    setFiltres((current) => ({ ...current, [champ]: valeur }));
  }

  function onSubmitFiltres(event: React.FormEvent<HTMLFormElement>) {
    event.preventDefault();

    const criteres: JournalFiltres = iduExclusif
      ? {
          idu: filtres.idu.trim(),
          id_cont: filtres.id_cont.trim(),
          fonction: filtres.fonction,
          evenement_depuis: toIsoUtc(filtres.evenement_depuis),
          evenement_jusqua: toIsoUtc(filtres.evenement_jusqua),
        }
      : {
          id_cont: filtres.id_cont.trim(),
          depuis: toIsoUtc(filtres.depuis),
          jusqua: toIsoUtc(filtres.jusqua),
          idu_on_uti_debut: filtres.idu_on_uti_debut.trim(),
          idu_on_uti_fin: filtres.idu_on_uti_fin.trim(),
          fonction: filtres.fonction,
          evenement_depuis: toIsoUtc(filtres.evenement_depuis),
          evenement_jusqua: toIsoUtc(filtres.evenement_jusqua),
        };

    void chargerJournal(criteres);
  }

  function onReinitialiser() {
    setFiltres(FILTRES_VIDES);
    void chargerJournal({});
  }

  return (
    <>
      <div className="mb-8 flex items-center gap-3">
        <div className="flex h-12 w-12 items-center justify-center rounded-xl bg-primary/10">
          <ScrollText className="h-6 w-6 text-primary" />
        </div>
        <div>
          <h1 className="text-2xl font-bold tracking-tight">Journal</h1>
          <p className="text-sm text-muted-foreground">
            Journal du fonctionnement du CCFN
          </p>
        </div>
      </div>

      <Card className="mb-6">
        <CardHeader className="border-b pb-6">
          <CardTitle className="text-base">Fonction « Lire journal »</CardTitle>
          <CardDescription>
            Si l&apos;IDU est renseigné, les plages de dates de dépôt et
            d&apos;ID_ON_UTI sont désactivées.
          </CardDescription>
        </CardHeader>

        <CardContent className="pt-6">
          <form className="space-y-4" onSubmit={onSubmitFiltres}>
            <div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
              <div className="space-y-2">
                <Label htmlFor="filtre-oid">IDU (identifiant unique)</Label>
                <Input
                  id="filtre-idu"
                  placeholder="01J8…"
                  value={filtres.idu}
                  onChange={(event) => onChangeFiltre('idu', event.target.value)}
                />
              </div>

              <div className="space-y-2">
                <Label htmlFor="filtre-id-cont">ID_CONT (conteneur)</Label>
                <Input
                  id="filtre-id-cont"
                  placeholder="Référence du dossier"
                  value={filtres.id_cont}
                  onChange={(event) =>
                    onChangeFiltre('id_cont', event.target.value)
                  }
                />
              </div>

              <div className="space-y-2">
                <Label htmlFor="filtre-fonction">Fonction réalisée</Label>
                <select
                  id="filtre-fonction"
                  className={cn(selectClassName, 'w-full')}
                  value={filtres.fonction}
                  onChange={(event) =>
                    onChangeFiltre('fonction', event.target.value)
                  }
                >
                  <option value="">Toutes les fonctions</option>
                  {FONCTIONS_JOURNAL.map((fonction) => (
                    <option key={fonction.value} value={fonction.value}>
                      {fonction.label}
                    </option>
                  ))}
                </select>
              </div>

              <div className="space-y-2">
                <Label htmlFor="filtre-depuis">ON déposés depuis</Label>
                <Input
                  id="filtre-depuis"
                  type="datetime-local"
                  disabled={iduExclusif}
                  value={filtres.depuis}
                  onChange={(event) =>
                    onChangeFiltre('depuis', event.target.value)
                  }
                />
              </div>

              <div className="space-y-2">
                <Label htmlFor="filtre-jusqua">ON déposés jusqu&apos;au</Label>
                <Input
                  id="filtre-jusqua"
                  type="datetime-local"
                  disabled={iduExclusif}
                  value={filtres.jusqua}
                  onChange={(event) =>
                    onChangeFiltre('jusqua', event.target.value)
                  }
                />
              </div>

              <div className="grid gap-2 sm:grid-cols-2">
                <div className="space-y-2">
                  <Label htmlFor="filtre-idu-uti-debut">ID_ON_UTI de</Label>
                  <Input
                    id="filtre-idu-uti-debut"
                    disabled={iduExclusif}
                    value={filtres.idu_on_uti_debut}
                    onChange={(event) =>
                      onChangeFiltre('idu_on_uti_debut', event.target.value)
                    }
                  />
                </div>
                <div className="space-y-2">
                  <Label htmlFor="filtre-idu-uti-fin">à</Label>
                  <Input
                    id="filtre-idu-uti-fin"
                    disabled={iduExclusif}
                    value={filtres.idu_on_uti_fin}
                    onChange={(event) =>
                      onChangeFiltre('idu_on_uti_fin', event.target.value)
                    }
                  />
                </div>
              </div>

              <div className="space-y-2">
                <Label htmlFor="filtre-evenement-depuis">
                  Évènements depuis
                </Label>
                <Input
                  id="filtre-evenement-depuis"
                  type="datetime-local"
                  value={filtres.evenement_depuis}
                  onChange={(event) =>
                    onChangeFiltre('evenement_depuis', event.target.value)
                  }
                />
              </div>

              <div className="space-y-2">
                <Label htmlFor="filtre-evenement-jusqua">
                  Évènements jusqu&apos;au
                </Label>
                <Input
                  id="filtre-evenement-jusqua"
                  type="datetime-local"
                  value={filtres.evenement_jusqua}
                  onChange={(event) =>
                    onChangeFiltre('evenement_jusqua', event.target.value)
                  }
                />
              </div>
            </div>

            <div className="flex flex-wrap items-center gap-2">
              <Button type="submit" disabled={isLoading}>
                {isLoading ? (
                  <Loader2 className="h-4 w-4 animate-spin" />
                ) : (
                  <Search className="h-4 w-4" />
                )}
                Lire le journal
              </Button>
              <Button
                type="button"
                variant="outline"
                onClick={onReinitialiser}
                disabled={isLoading}
              >
                <RotateCcw className="h-4 w-4" />
                Réinitialiser
              </Button>
            </div>
          </form>
        </CardContent>
      </Card>

      {lecture ? (
        <Card className="mb-6">
          <CardContent className="grid gap-4 py-6 text-sm sm:grid-cols-2 lg:grid-cols-4">
            {/* §5.3.6 : informations retournées par la fonction Lire Journal. */}
            <div>
              <p className="text-xs uppercase text-muted-foreground">ID_CCFN</p>
              <p className="font-mono text-xs">{lecture.id_ccfn}</p>
            </div>
            <div>
              <p className="text-xs uppercase text-muted-foreground">ID_UTI</p>
              <p className="font-mono text-xs">{lecture.id_uti}</p>
            </div>
            <div>
              <p className="text-xs uppercase text-muted-foreground">
                Date et heure de fin
              </p>
              <p className="font-mono text-xs">{lecture.date_heure}</p>
            </div>
            <div>
              <p className="text-xs uppercase text-muted-foreground">Statut</p>
              <p className="text-xs">
                <span className="font-mono">{lecture.statut_execution.code}</span>
                {' — '}
                {lecture.statut_execution.libelle}
              </p>
            </div>
          </CardContent>
        </Card>
      ) : null}

      <Card>
        <CardHeader className="gap-4 border-b pb-6">
          <div className="flex flex-col gap-1 sm:flex-row sm:items-center sm:justify-between">
            <div>
              <CardTitle className="text-base">
                Enregistrements du journal
              </CardTitle>
              <CardDescription>
                {entrees.length} enregistrement{entrees.length > 1 ? 's' : ''}{' '}
                sélectionné{entrees.length > 1 ? 's' : ''}
              </CardDescription>
            </div>

            <div>
              <Label htmlFor="journal-page-size" className="sr-only">
                Lignes par page
              </Label>
              <select
                id="journal-page-size"
                className={cn(selectClassName, 'min-w-[140px]')}
                value={pageSize}
                onChange={(event) =>
                  setPageSize(
                    Number(event.target.value) as (typeof PAGE_SIZE_OPTIONS)[number],
                  )
                }
              >
                {PAGE_SIZE_OPTIONS.map((size) => (
                  <option key={size} value={size}>
                    {size} par page
                  </option>
                ))}
              </select>
            </div>
          </div>
        </CardHeader>

        <CardContent className="p-0">
          {isLoading ? (
            <div className="flex items-center justify-center gap-2 py-16 text-sm text-muted-foreground">
              <Loader2 className="h-5 w-5 animate-spin" />
              Lecture du journal…
            </div>
          ) : loadError ? (
            <p className="py-10 text-center text-sm text-destructive">
              {loadError}
            </p>
          ) : entrees.length === 0 ? (
            <div className="flex flex-col items-center gap-2 px-6 py-12 text-center">
              <ScrollText className="h-8 w-8 text-muted-foreground/60" />
              <p className="font-medium">Aucun enregistrement</p>
              <p className="text-sm text-muted-foreground">
                Ajustez les paramètres d&apos;appel pour élargir la sélection.
              </p>
            </div>
          ) : (
            <>
              <Table>
                <TableHeader>
                  <TableRow>
                    <TableHead>Date et heure</TableHead>
                    <TableHead>Fonction réalisée</TableHead>
                    <TableHead>ID_UTI</TableHead>
                    <TableHead>IDU</TableHead>
                    <TableHead>ID_CONT</TableHead>
                    <TableHead>Statut</TableHead>
                    <TableHead className="text-right">Détail</TableHead>
                  </TableRow>
                </TableHeader>
                <TableBody>
                  {entreesPage.map((entree) => {
                    const cle = String(entree.sequence);
                    const ouverte = ligneOuverte === cle;

                    return (
                      <JournalLigne
                        key={cle}
                        entree={entree}
                        ouverte={ouverte}
                        onToggle={() => setLigneOuverte(ouverte ? null : cle)}
                      />
                    );
                  })}
                </TableBody>
              </Table>

              <div className="flex flex-col gap-3 border-t px-4 py-4 sm:flex-row sm:items-center sm:justify-between">
                <p className="text-sm text-muted-foreground">
                  Affichage de {rangeStart} à {rangeEnd} sur {entrees.length}{' '}
                  enregistrement{entrees.length > 1 ? 's' : ''}
                </p>

                <div className="flex items-center gap-2">
                  <Button
                    type="button"
                    variant="outline"
                    size="sm"
                    onClick={() => setPage((current) => current - 1)}
                    disabled={currentPage <= 1}
                  >
                    <ChevronLeft className="h-4 w-4" />
                    Précédent
                  </Button>
                  <span className="min-w-[4.5rem] text-center text-sm tabular-nums text-muted-foreground">
                    {currentPage} / {totalPages}
                  </span>
                  <Button
                    type="button"
                    variant="outline"
                    size="sm"
                    onClick={() => setPage((current) => current + 1)}
                    disabled={currentPage >= totalPages}
                  >
                    Suivant
                    <ChevronRight className="h-4 w-4" />
                  </Button>
                </div>
              </div>
            </>
          )}
        </CardContent>
      </Card>
    </>
  );
}

type JournalLigneProps = {
  entree: JournalEntree;
  ouverte: boolean;
  onToggle: () => void;
};

function JournalLigne({ entree, ouverte, onToggle }: JournalLigneProps) {
  const succes = entree.statut_execution.code === 'ok';

  return (
    <>
      <TableRow>
        <TableCell className="whitespace-nowrap">
          <span className="font-mono text-xs">{entree.date_heure}</span>
          <span className="block text-xs text-muted-foreground">
            {formatDateJournal(entree.date_heure)} UTC
          </span>
        </TableCell>
        <TableCell className="whitespace-nowrap">
          {libelleFonction(entree.fonction)}
        </TableCell>
        <TableCell className="font-mono text-xs">{entree.id_uti}</TableCell>
        <TableCell className="font-mono text-xs">
          {entree.idu ?? <span className="text-muted-foreground">—</span>}
        </TableCell>
        <TableCell className="font-mono text-xs">
          {entree.id_cont ?? <span className="text-muted-foreground">—</span>}
        </TableCell>
        <TableCell>
          <span
            className={cn(
              'inline-flex rounded-full px-2 py-0.5 text-xs font-medium',
              succes
                ? 'bg-emerald-100 text-emerald-700'
                : 'bg-destructive/10 text-destructive',
            )}
            title={entree.statut_execution.libelle}
          >
            {succes ? 'Correct' : 'Anomalie'}
          </span>
        </TableCell>
        <TableCell className="text-right">
          <Button
            type="button"
            variant="ghost"
            size="sm"
            className="h-8 px-2"
            onClick={onToggle}
            aria-expanded={ouverte}
          >
            <ChevronDown
              className={cn('h-4 w-4 transition-transform', ouverte && 'rotate-180')}
            />
            {ouverte ? 'Masquer' : 'Voir'}
          </Button>
        </TableCell>
      </TableRow>

      {ouverte ? (
        <TableRow>
          <TableCell colSpan={7} className="bg-muted/40">
            <div className="grid gap-4 py-2 text-sm sm:grid-cols-2 lg:grid-cols-4">
              {/* §5.3.6 : éléments minimaux de chaque enregistrement retourné. */}
              <div>
                <p className="text-xs uppercase text-muted-foreground">
                  ID_CCFN
                </p>
                <p className="font-mono text-xs">{entree.id_ccfn}</p>
              </div>
              <div>
                <p className="text-xs uppercase text-muted-foreground">
                  ID_ON_UTI
                </p>
                <p className="font-mono text-xs">{entree.idu_on_uti ?? '—'}</p>
              </div>
              <div>
                <p className="text-xs uppercase text-muted-foreground">
                  Taille de l&apos;ON
                </p>
                <p className="text-xs">{formatBytes(entree.taille_octets)}</p>
              </div>
              <div>
                <p className="text-xs uppercase text-muted-foreground">
                  Algorithme d&apos;empreinte
                </p>
                <p className="font-mono text-xs">
                  {entree.algo_empreinte ?? '—'}
                </p>
              </div>
              <div className="sm:col-span-2 lg:col-span-4">
                <p className="text-xs uppercase text-muted-foreground">
                  Empreinte calculée par le CCFN
                </p>
                <p className="break-all font-mono text-xs">
                  {entree.empreinte ?? '—'}
                </p>
              </div>
              <div className="sm:col-span-2 lg:col-span-4">
                <p className="text-xs uppercase text-muted-foreground">
                  Statut d&apos;exécution
                </p>
                <p className="text-xs">
                  <span className="font-mono">{entree.statut_execution.code}</span>
                  {' — '}
                  {entree.statut_execution.libelle}
                </p>
              </div>
              {entree.detail ? (
                <div className="sm:col-span-2 lg:col-span-4">
                  <p className="text-xs uppercase text-muted-foreground">
                    Partie spécifique à la fonction
                  </p>
                  <pre className="mt-1 overflow-x-auto rounded-md bg-background p-3 text-xs">
                    {entree.detail}
                  </pre>
                </div>
              ) : null}
            </div>
          </TableCell>
        </TableRow>
      ) : null}
    </>
  );
}
