import { useState, useRef } from "react"
import { Archivo } from "../models/Archivo.model"
import { auth } from "../services";


interface UseFileType {
  archivo: Archivo | undefined,
  error: string | undefined,
  progreso: number | undefined,
  agregar: (archivo: Archivo) => void,
  subir: () => Promise<Archivo>,
  limpiar: () => void,
  descargar: () => void,
}

export function useFile(): UseFileType {

  const [archivo, setArchivo] = useState<Archivo | undefined>();
  const [error, setError] = useState<string | undefined>();
  const [progreso, setProgreso] = useState<number | undefined>();

  const archivoRef = useRef<Archivo | undefined>(undefined);

  const actualizar = (actualizacion: Partial<Archivo>) => {
    if (archivoRef.current) {
      const archivoActualizado = new Archivo({
        ...archivoRef.current,
        ...actualizacion,
      });
      archivoRef.current = archivoActualizado;
      setArchivo(archivoActualizado);
    }
  };

  const agregar = (archivo: Archivo) => {
    archivoRef.current = archivo;
    setArchivo(archivo);
  };

  const limpiar = () => {
    archivoRef.current = undefined;
    setArchivo(undefined);
    setProgreso(undefined);
    setError(undefined);
  };

  const subir = async (): Promise<Archivo> => {
    if (!archivoRef.current || !archivoRef.current.referencia) {
      throw new Error("El archivo no está definido o falta la referencia");
    }

    if (archivoRef.current.enNube !== null) {
      throw new Error("El archivo ya se ha subido");
    }

    actualizar({ estatus: Archivo.ESTATUS.SUBIENDO, progreso: 0 });

    try {
      // Solicitar la URL al servidor
      const res = await auth.get("aspirante/archivo/generar-url.json", {
        nombre: archivoRef.current.nombre,
        tipo: archivoRef.current.mimetype,
        peso: archivoRef.current.peso,
      });

      if (res.status >= 400) {
        throw new Error(`Error al generar URL: ${res.detalle}`);
      }

      const media = res.detalle as Archivo;

      actualizar({ ...media });

      // Subir el archivo a la URL obtenida
      return await new Promise<Archivo>((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open("PUT", media.urlNube!, true);

        xhr.upload.onprogress = (event) => {
          if (event.lengthComputable) {
            const porcentaje = parseInt(`${(event.loaded / event.total) * 100}`);
            actualizar({ progreso: porcentaje });
            setProgreso(porcentaje);
          }
        };

        xhr.onload = async () => {
          if (xhr.status >= 200 && xhr.status < 300) {

            let resArchivo = await auth.post("aspirante/archivo/confirmar-subida.json", { id: media.id });
            let detalle = resArchivo.detalle as Archivo;

            actualizar({ ...detalle, estatus: Archivo.ESTATUS.COMPLETADO, progreso: 100 });
            resolve(archivoRef.current as Archivo);

          } else {
            const mensajeError = `Error en la subida: ${xhr.statusText}`;
            actualizar({ estatus: Archivo.ESTATUS.ERROR });
            setError(mensajeError);
            reject(new Error(mensajeError));
          }
        };

        xhr.onerror = () => {
          const mensajeError = "Error de red o conexión interrumpida";
          actualizar({ estatus: Archivo.ESTATUS.ERROR });
          setError(mensajeError);
          reject(new Error(mensajeError));
        };

        xhr.send(archivoRef.current!.referencia);
      });
    } catch (err) {
      const mensajeError = err instanceof Error ? err.message : "Error desconocido";
      setError(mensajeError);
      actualizar({ estatus: Archivo.ESTATUS.ERROR });
      throw err;
    }
  };

  const descargar = async () => {
    if(!archivoRef.current?.id) {
      return;
    }

    let blob = await auth.getBlob("aspirante/archivo/descarga.json", { id: archivoRef.current.id });
    const urlBlob = URL.createObjectURL(new Blob([blob]));

    const enlace = document.createElement('a');
    enlace.href = urlBlob;
    enlace.setAttribute("download", archivoRef.current.nombre);

    document.body.appendChild(enlace);
    enlace.click();
    document.body.removeChild(enlace);

    URL.revokeObjectURL(urlBlob);
  }

  return ({
    archivo,
    error,
    progreso,
    agregar,
    subir,
    limpiar,
    descargar
  });
}