'use client';

import { DashboardEmptyState, DashboardSection } from '@/components/dashboard-page';
import { DataRefreshControl } from '@/components/data-refresh-control';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Card, CardContent } from '@/components/ui/card';
import { ErrorState } from '@/components/ui/error-state';
import { Input } from '@/components/ui/input';
import { Skeleton, SkeletonDetailHeader } from '@/components/ui/skeleton';
import { useFlagsConfiguracion } from '@/hooks/use-flags-configuracion';
import { useToast } from '@/hooks/use-toast';
import { otsApi, uploadFileWithProgress } from '@/lib/api';
import { compressImageFile } from '@/lib/image-compression';
import { canMutateOtRemotely, isOtLocked, OT_DETAIL_REFRESH_INTERVAL_MS, refreshOtDetail } from '@/lib/ot-detail';
import { formatDate, getEstadoLabel } from '@/lib/utils';
import { normalizePrintAssetUrl } from '@/lib/print';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { ArrowLeft, Camera, FileText, ImageOff, ImagePlus, Loader2, RefreshCw, Trash2, Upload } from 'lucide-react';
import { useParams, useRouter } from 'next/navigation';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';

// Sin esto, un <img> que no carga (red muy lenta/inestable) se queda
// "cargando" para siempre sin ningun indicio -- el usuario no puede saber si
// sigue intentando o si ya fallo. onError + un boton de reintento (que
// vuelve a pedir la imagen agregando un parametro que cambia, para no
// depender del cache del navegador reintentando lo mismo que ya fallo).
function FotoImage({ src, alt, className }: { src: string; alt: string; className: string }) {
  const [status, setStatus] = useState<'loading' | 'loaded' | 'error'>('loading');
  const [retryToken, setRetryToken] = useState(0);

  const cleanSrc = normalizePrintAssetUrl(src);
  const resolvedSrc = retryToken > 0 ? `${cleanSrc}${cleanSrc.includes('?') ? '&' : '?'}retry=${retryToken}` : cleanSrc;

  return (
    <div className={`relative overflow-hidden bg-muted ${className}`}>
      {status !== 'error' ? (
        <img
          key={retryToken}
          src={resolvedSrc}
          alt={alt}
          className="h-full w-full object-cover"
          onLoad={() => setStatus('loaded')}
          onError={() => setStatus('error')}
        />
      ) : null}
      {status === 'loading' ? (
        <div className="absolute inset-0 flex items-center justify-center bg-muted">
          <Loader2 className="h-6 w-6 animate-spin text-subtle-foreground" />
        </div>
      ) : null}
      {status === 'error' ? (
        <div className="absolute inset-0 flex flex-col items-center justify-center gap-2 bg-muted p-3 text-center">
          <ImageOff className="h-6 w-6 text-subtle-foreground" />
          <p className="text-xs text-muted-foreground">No se pudo cargar la foto</p>
          <Button
            type="button"
            variant="outline"
            size="sm"
            className="rounded-xl"
            onClick={() => {
              setStatus('loading');
              setRetryToken((value) => value + 1);
            }}
          >
            <RefreshCw className="mr-1 h-3 w-3" />
            Reintentar
          </Button>
        </div>
      ) : null}
    </div>
  );
}

const MAX_IMAGE_SIZE = 20 * 1024 * 1024;
const MAX_PDF_SIZE = 15 * 1024 * 1024;
const ALLOWED_IMAGE_TYPES = ['image/jpeg', 'image/png', 'image/webp', 'image/gif'];

function getImageValidationMessage(file: File) {
  if (!ALLOWED_IMAGE_TYPES.includes(file.type)) {
    return 'La foto debe ser una imagen JPG, PNG, WEBP o GIF.';
  }

  if (file.size > MAX_IMAGE_SIZE) {
    return 'La foto es demasiado grande. Intenta con una imagen menor a 20 MB.';
  }

  return null;
}

function getPdfValidationMessage(file: File) {
  const isPdf = file.type === 'application/pdf' && file.name.toLowerCase().endsWith('.pdf');

  if (!isPdf) {
    return 'El archivo debe ser PDF.';
  }

  if (file.size > MAX_PDF_SIZE) {
    return 'El PDF es demasiado grande. El tamaño máximo permitido es 15 MB.';
  }

  return null;
}

export default function FotosPage() {
  const params = useParams();
  const router = useRouter();
  const { toast } = useToast();
  const queryClient = useQueryClient();
  const { usaPdfEscaner } = useFlagsConfiguracion();
  const routeId = String(params.id);
  const [descripcion, setDescripcion] = useState('');
  const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
  const [progressMap, setProgressMap] = useState<Record<string, number>>({});
  const [uploadStatus, setUploadStatus] = useState('');
  const [pdfProgress, setPdfProgress] = useState(0);
  const [pdfStatus, setPdfStatus] = useState('');
  const pdfInputRef = useRef<HTMLInputElement | null>(null);

  const selectedPreviews = useMemo(
    () => selectedFiles.map((file) => ({ key: `${file.name}-${file.size}`, file, previewUrl: URL.createObjectURL(file) })),
    [selectedFiles],
  );

  useEffect(() => {
    return () => {
      selectedPreviews.forEach((item) => URL.revokeObjectURL(item.previewUrl));
    };
  }, [selectedPreviews]);

  const { data, dataUpdatedAt, isFetching, isLoading, error, refetch } = useQuery({
    queryKey: ['ots', 'detail', routeId, 'fotos'],
    queryFn: () => refreshOtDetail(routeId),
    enabled: !!routeId,
    refetchInterval: OT_DETAIL_REFRESH_INTERVAL_MS,
    refetchOnReconnect: true,
    refetchOnWindowFocus: true,
  });

  const ot = data?.ot;
  const canMutate = canMutateOtRemotely(ot);
  const locked = isOtLocked(ot);
  const fotos = ot?.fotos || [];
  const fotosGenerales = fotos.filter((item: any) => item.tipo !== 'documento');
  const fotosDocumento = fotos.filter((item: any) => item.tipo === 'documento');

  const addSelectedImages = useCallback(
    (files: File[]) => {
      if (!files.length) return;

      const validFiles: File[] = [];
      const invalidMessages: string[] = [];

      files.forEach((file) => {
        const validationMessage = getImageValidationMessage(file);
        if (validationMessage) {
          invalidMessages.push(`${file.name}: ${validationMessage}`);
          return;
        }
        validFiles.push(file);
      });

      if (invalidMessages.length) {
        toast({
          variant: 'destructive',
          title: 'No se pudieron agregar algunas fotos',
          description: invalidMessages.slice(0, 2).join(' '),
        });
      }

      if (validFiles.length) {
        setSelectedFiles((prev) => [...prev, ...validFiles]);
      }
    },
    [toast],
  );

  const refreshData = async () => {
    await queryClient.invalidateQueries({ queryKey: ['ots', 'detail', routeId] });
    await queryClient.invalidateQueries({ queryKey: ['ots', 'detail', routeId, 'fotos'] });
    await queryClient.invalidateQueries({ queryKey: ['historial'] });
  };

  const uploadFotosMutation = useMutation({
    mutationFn: async () => {
      if (!ot?.serverId) {
        throw new Error('Primero guarda la OT para usar esta acción.');
      }
      if (!selectedFiles.length) {
        throw new Error('Selecciona al menos una foto.');
      }

      setUploadStatus('Preparando archivos...');
      for (const file of selectedFiles) {
        const key = `${file.name}-${file.size}`;
        setUploadStatus(`${file.name}: comprimiendo imagen...`);
        const uploadFile = await compressImageFile(file);
        const uploaded = await uploadFileWithProgress(
          uploadFile,
          'foto',
          (progress) => {
            setProgressMap((current) => ({ ...current, [key]: progress }));
          },
          {
            onStatusChange: (status) => setUploadStatus(`${file.name}: ${status.message}`),
          },
        );

        await otsApi.addFoto(Number(ot.serverId), {
          tipo: 'general',
          descripcion: descripcion.trim() || undefined,
          url: uploaded.url,
        });
      }
    },
    onSuccess: async () => {
      setSelectedFiles([]);
      setDescripcion('');
      setProgressMap({});
      setUploadStatus('');
      await refreshData();
      toast({ variant: 'success', title: 'Fotos cargadas', description: 'La galeria de la OT ya fue actualizada.' });
    },
    onError: (error: any) => {
      setUploadStatus('');
      toast({ variant: 'destructive', title: 'Error', description: error.message || 'No se pudieron cargar las fotos' });
    },
  });

  const deleteMutation = useMutation({
    mutationFn: async (fotoId: number) => {
      if (!ot?.serverId) {
        throw new Error('Primero guarda la OT para usar esta acción.');
      }
      return otsApi.removeFoto(Number(ot.serverId), fotoId);
    },
    onSuccess: async () => {
      await refreshData();
      toast({ variant: 'success', title: 'Foto eliminada', description: 'La evidencia fue removida de la orden.' });
    },
    onError: (error: any) => toast({ variant: 'destructive', title: 'Error', description: error.message || 'No se pudo eliminar la foto' }),
  });

  const uploadPdfMutation = useMutation({
    mutationFn: async (file: File) => {
      if (!ot?.serverId) {
        throw new Error('Primero guarda la OT para usar esta acción.');
      }
      setPdfProgress(0);
      setPdfStatus('Preparando documento...');
      const uploaded = await uploadFileWithProgress(file, 'pdf', setPdfProgress, {
        onStatusChange: (status) => setPdfStatus(status.message),
      });
      return otsApi.savePdfEscaner(Number(ot.serverId), uploaded.url);
    },
    onSuccess: async () => {
      setPdfProgress(100);
      setPdfStatus('Documento integrado en la OT.');
      await refreshData();
      toast({ variant: 'success', title: 'PDF adjunto', description: 'El documento ya forma parte de la OT.' });
    },
    onError: (error: any) => {
      setPdfProgress(0);
      setPdfStatus('');
      toast({ variant: 'destructive', title: 'Error', description: error.message || 'No se pudo adjuntar el PDF' });
    },
  });

  const handlePdfFile = useCallback(
    (file?: File) => {
      if (!file) return;

      const validationMessage = getPdfValidationMessage(file);
      if (validationMessage) {
        toast({
          variant: 'destructive',
          title: 'PDF no válido',
          description: validationMessage,
        });
        return;
      }

      uploadPdfMutation.mutate(file);
    },
    [toast, uploadPdfMutation],
  );

  if (isLoading) {
    return (
      <div className="min-h-full pb-16 md:pb-10">
        <main className="mx-auto flex max-w-7xl flex-col gap-6">
          <Card className="overflow-hidden">
            <CardContent className="space-y-5 p-5 md:p-6">
              <SkeletonDetailHeader />
            </CardContent>
          </Card>
          <div className="grid gap-6 xl:grid-cols-[0.95fr_1.05fr]">
            <Skeleton className="h-80 w-full" />
            <Skeleton className="h-80 w-full" />
          </div>
        </main>
      </div>
    );
  }

  if (error || !ot) {
    return (
      <div className="flex min-h-[60dvh] items-center justify-center p-4">
        <ErrorState
          title={error ? 'No se pudo cargar la OT' : 'OT no encontrada'}
          description={error ? 'No logramos comunicarnos con el servidor. Revisa la conexión e intenta de nuevo.' : 'No fue posible abrir la galeria de esta OT.'}
          onRetry={() => (error ? refetch() : router.push('/dashboard/ots'))}
          retryLabel={error ? 'Reintentar' : 'Volver al listado'}
        />
      </div>
    );
  }

  return (
    <div className="min-h-full pb-16 md:pb-10">
      <main className="mx-auto flex max-w-7xl flex-col gap-6">
        <Card className="overflow-hidden">
          <CardContent className="space-y-5 p-5 md:p-6">
            <div className="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
              <div className="flex items-start gap-3 md:gap-4">
                <Button variant="outline" size="icon" aria-label="Volver a la OT" onClick={() => router.push(`/dashboard/ots/${routeId}`)}>
                  <ArrowLeft className="h-4 w-4" />
                </Button>
                <div>
                  <div className="flex flex-wrap items-center gap-2">
                    <h1 className="text-2xl font-bold tracking-tight text-foreground">Fotos y documentos</h1>
                    <Badge variant="primary">{ot.numeroOt}</Badge>
                    <Badge variant="neutral">{getEstadoLabel(ot.estado)}</Badge>
                  </div>
                  <p className="mt-2 text-sm text-muted-foreground">{ot.vehiculo?.placa || 'Sin placa'} | {formatDate(ot.fechaEntrada)}</p>
                  {locked ? <p className="mt-1 text-sm font-medium text-success">La OT ya fue entregada y la galeria queda en modo lectura.</p> : null}
                </div>
              </div>
              <DataRefreshControl updatedAt={dataUpdatedAt} isFetching={isFetching} onRefresh={() => void refetch()} />
            </div>
          </CardContent>
        </Card>

        <div className="grid gap-6 xl:grid-cols-[0.95fr_1.05fr]">
          <div className="space-y-6">
            <DashboardSection title="Evidencia fotográfica" description="Toma fotos directamente o elige de la galeria. Diseñado para usar con una mano en la tablet.">
              <div className="space-y-4">
                <div className="grid gap-3 sm:grid-cols-2">
                  {/* Direct camera button — opens camera immediately */}
                  <label className={`flex cursor-pointer flex-col items-center justify-center gap-3 rounded-xl border-2 border-primary/30 bg-primary-soft px-5 py-8 text-center transition-transform duration-150 ease-out active:scale-[0.98] ${!canMutate ? 'pointer-events-none opacity-60' : ''}`}>
                    <span className="flex h-16 w-16 items-center justify-center rounded-full bg-primary shadow-md">
                      <Camera className="h-8 w-8 text-primary-foreground" />
                    </span>
                    <p className="text-lg font-bold text-primary">Tomar foto</p>
                    <p className="text-sm text-primary">Abre la cámara directamente</p>
                    <input
                      type="file"
                      accept="image/*"
                      capture="environment"
                      className="hidden"
                      disabled={!canMutate}
                      onChange={(event) => {
                        const files = Array.from(event.target.files || []);
                        addSelectedImages(files);
                        event.currentTarget.value = '';
                      }}
                    />
                  </label>

                  {/* Gallery picker — allows multiple selection */}
                  <label className={`flex cursor-pointer flex-col items-center justify-center gap-3 rounded-xl border-2 border-border bg-surface px-5 py-8 text-center transition-[background-color,transform] duration-150 ease-out active:scale-[0.98] [@media(hover:hover)]:hover:bg-muted ${!canMutate ? 'pointer-events-none opacity-60' : ''}`}>
                    <span className="flex h-16 w-16 items-center justify-center rounded-full bg-muted">
                      <ImagePlus className="h-8 w-8 text-muted-foreground" />
                    </span>
                    <p className="text-lg font-bold text-foreground">Elegir de galería</p>
                    <p className="text-sm text-muted-foreground">Selecciona varias imágenes</p>
                    <input
                      type="file"
                      accept="image/*"
                      multiple
                      className="hidden"
                      disabled={!canMutate}
                      onChange={(event) => {
                        const files = Array.from(event.target.files || []);
                        addSelectedImages(files);
                        event.currentTarget.value = '';
                      }}
                    />
                  </label>
                </div>

                {selectedFiles.length > 0 ? (
                  <div className="flex items-center justify-between rounded-xl border border-primary/30 bg-primary-soft px-4 py-3">
                    <span className="text-sm font-semibold text-primary">{selectedFiles.length} foto{selectedFiles.length > 1 ? 's' : ''} seleccionada{selectedFiles.length > 1 ? 's' : ''}</span>
                    <button type="button" className="text-sm font-semibold text-primary hover:underline" onClick={() => setSelectedFiles([])}>Limpiar</button>
                  </div>
                ) : null}

                <div>
                  <label htmlFor="fotos-descripcion-comun" className="mb-2 block text-sm font-medium text-foreground">Descripción común</label>
                  <Input id="fotos-descripcion-comun" value={descripcion} onChange={(event) => setDescripcion(event.target.value)} placeholder="Ejemplo: recepción lateral derecha" />
                </div>

                {selectedPreviews.length ? (
                  <div className="grid gap-3 sm:grid-cols-2">
                    {selectedPreviews.map(({ key, file, previewUrl }) => (
                      <div key={key} className="rounded-xl border border-border bg-surface p-3 shadow-sm">
                        <img src={previewUrl} alt={file.name} className="h-40 w-full rounded-lg object-cover" />
                        <p className="mt-3 truncate text-sm font-semibold text-foreground">{file.name}</p>
                        <p className="text-xs text-muted-foreground">{Math.round(file.size / 1024)} KB</p>
                        {uploadFotosMutation.isPending ? (
                          <div className="mt-3">
                            <div className="flex items-center justify-between text-xs text-muted-foreground"><span>Progreso</span><span>{progressMap[key] || 0}%</span></div>
                            <div className="mt-2 h-2 overflow-hidden rounded-full bg-border"><div className="h-full rounded-full bg-primary transition-[width] duration-150 ease-out" style={{ width: `${progressMap[key] || 0}%` }} /></div></div>
                        ) : null}
                      </div>
                    ))}
                  </div>
                ) : null}

                {uploadFotosMutation.isPending && uploadStatus ? (
                  <div className="rounded-xl border border-primary/30 bg-primary-soft px-4 py-3 text-sm text-primary">{uploadStatus}</div>
                ) : null}

                <Button className="w-full" size="lg" disabled={!canMutate || uploadFotosMutation.isPending || !selectedFiles.length} onClick={() => uploadFotosMutation.mutate()}>
                  {uploadFotosMutation.isPending ? <Loader2 className="mr-2 h-4 w-4 animate-spin" /> : <Upload className="mr-2 h-4 w-4" />}
                  {canMutate ? 'Subir fotos a la OT' : 'No disponible'}
                </Button>
              </div>
            </DashboardSection>

            {usaPdfEscaner ? (
              <DashboardSection title="Documento PDF" description="Integra el escaneo dentro de la OT sin salir del flujo.">
                <div className="space-y-4">
                  {ot.pdfEscaner ? (
                    <a href={normalizePrintAssetUrl(ot.pdfEscaner)} target="_blank" rel="noopener noreferrer" className="flex min-h-11 items-center justify-center gap-2 rounded-xl border border-border bg-muted p-4 text-sm font-semibold text-foreground transition-colors duration-150 ease-out [@media(hover:hover)]:hover:bg-muted/70">
                      <FileText className="h-4 w-4" />Abrir PDF actual
                    </a>
                  ) : (
                    <div className="rounded-xl border border-dashed border-border-strong bg-muted p-4 text-sm text-muted-foreground">Todavía no hay PDF adjunto.</div>
                  )}

                  {uploadPdfMutation.isPending ? (
                    <div>
                      <div className="flex items-center justify-between text-sm text-muted-foreground"><span>Subiendo PDF</span><span>{pdfProgress}%</span></div>
                      <div className="mt-2 h-2 overflow-hidden rounded-full bg-border"><div className="h-full rounded-full bg-primary transition-[width] duration-150 ease-out" style={{ width: `${pdfProgress}%` }} /></div>
                      {pdfStatus ? <p className="mt-2 text-sm text-primary">{pdfStatus}</p> : null}
                    </div>
                  ) : pdfStatus ? (
                    <div className="rounded-xl border border-success/30 bg-success-soft px-4 py-3 text-sm text-success">{pdfStatus}</div>
                  ) : null}

                  <Button variant="outline" size="lg" className="w-full" onClick={() => pdfInputRef.current?.click()} disabled={!canMutate || uploadPdfMutation.isPending}>
                    <Upload className="mr-2 h-4 w-4" />Subir PDF del escaner
                  </Button>
                </div>
              </DashboardSection>
            ) : null}
          </div>

          <div className="space-y-6">
            <DashboardSection title="Galeria" description="Fotos generales de recepción, avance o cierre.">
              {fotosGenerales.length ? (
                <div className="grid gap-4 md:grid-cols-2">
                  {fotosGenerales.map((foto: any) => (
                    <div key={foto.id} className="rounded-xl border border-border bg-surface p-3 shadow-sm">
                      <FotoImage src={foto.url} alt={foto.descripcion || 'Foto OT'} className="h-52 w-full rounded-lg" />
                      <div className="mt-3 flex items-start justify-between gap-3">
                        <div>
                          <p className="text-sm font-semibold text-foreground">{foto.descripcion || 'Sin descripción'}</p>
                          <p className="text-xs text-muted-foreground">Tipo: {foto.tipo}</p>
                        </div>
                        <Button variant="outline" disabled={!canMutate || deleteMutation.isPending} aria-label={`Eliminar foto: ${foto.descripcion || 'sin descripción'}`} onClick={() => deleteMutation.mutate(Number(foto.id))}>
                          <Trash2 className="h-4 w-4" />
                        </Button>
                      </div>
                    </div>
                  ))}
                </div>
              ) : (
                <DashboardEmptyState title="Sin fotos cargadas" description="Agrega imágenes de recepción y seguimiento para que la OT tenga evidencia completa." icon={ImagePlus} className="border-none bg-muted shadow-none" />
              )}
            </DashboardSection>

            <DashboardSection title="Fotos de documento" description="Capturas marcadas como documento dentro de la orden.">
              {fotosDocumento.length ? (
                <div className="grid gap-4 md:grid-cols-2">
                  {fotosDocumento.map((foto: any) => (
                    <div key={foto.id} className="rounded-xl border border-border bg-surface p-3 shadow-sm">
                      <FotoImage src={foto.url} alt={foto.descripcion || 'Documento'} className="h-44 w-full rounded-lg" />
                      <div className="mt-3 flex items-start justify-between gap-3">
                        <div>
                          <p className="text-sm font-semibold text-foreground">{foto.descripcion || 'Documento'}</p>
                          <p className="text-xs text-muted-foreground">Tipo: documento</p>
                        </div>
                        <Button variant="outline" disabled={!canMutate || deleteMutation.isPending} aria-label={`Eliminar documento: ${foto.descripcion || 'sin descripción'}`} onClick={() => deleteMutation.mutate(Number(foto.id))}>
                          <Trash2 className="h-4 w-4" />
                        </Button>
                      </div>
                    </div>
                  ))}
                </div>
              ) : (
                <div className="rounded-xl border border-dashed border-border-strong bg-muted p-5 text-sm text-muted-foreground">No hay fotos marcadas como documento.</div>
              )}
            </DashboardSection>
          </div>
        </div>
      </main>

      <input
        ref={pdfInputRef}
        type="file"
        accept="application/pdf"
        className="hidden"
        onChange={(event) => {
          const file = event.target.files?.[0];
          handlePdfFile(file);
          event.currentTarget.value = '';
        }}
      />
    </div>
  );
}
