// Import necessary modules and components
import { FieldErrors, Resolver, SubmitHandler, useForm } from "react-hook-form";
import { Place, AvailableDays } from "../../data/datasource/api/entities/place";
import { toast } from "react-toastify";
import { useEffect, useState } from "react";
import axios, { AxiosError } from "axios";
import { languaguesList } from "../../core/constants/languages";
import { MIN_NUMBER_PLACE_IMAGES } from "../../core/constants/images_nuber";
import { PlaceDatasourceProd } from "../../data/datasource/api/place_datasource";
import { PlaceRepositoryProd } from "../../data/repository/place_repository";
import { showErrorAxios } from "../../core/utils/Messages";

// Initialize data source and repository
const placeDatasouce = new PlaceDatasourceProd();
const placeRepository = new PlaceRepositoryProd(placeDatasouce);

// Define resolver for form validation
const resolver: Resolver<Place> = async (data) => {
  const errors: FieldErrors<Place> = {};

  // Validate idTown
  if (!data.idTown) {
    errors.idTown = {
      type: "required",
      message: "Debe seleccionar el pueblo mágico al que pertenece el lugar",
    };
  }

  // Validate openAt
  if (!data.openAt && data.openAt !== 0) {
    errors.openAt = {
      type: "required",
      message: "La hora de apertura es requerida",
    };
  }

  // Validate closeAt
  if (!data.closeAt && data.closeAt !== 0) {
    errors.closeAt = {
      type: "required",
      message: "La hora de cierre es requerida",
    };
  }

  // Ensure openAt is before closeAt
  if (data.openAt > data.closeAt) {
    errors.closeAt = {
      type: "required",
      message: "La hora de apertura debe ser antes que la hora de cierre",
    };
  }

  // Validate name
  if (!data.name) {
    errors.name = {
      type: "required",
      message: "El nombre del lugar es requerido",
    };
  } else if (data.name.length > 255) {
    errors.name = {
      type: "maxLength",
      message: "El tamaño no debe sobrepasar los 255 caracteres",
    };
  }

  // Validate categoriesId
  if (!data.categoriesId) {
    errors.categoriesId = {
      type: "required",
      message: "Debe seleccionar al menos una categoría",
    };
  } else {
    if (data.categoriesId.length < 1) {
      errors.categoriesId = {
        type: "required",
        message: "Debe seleccionar al menos una categoría",
      };
    }
  }

  // Validate descriptions for each language
  for (var index = languaguesList.length - 1; index >= 0; index--) {
    if (!data.descriptions || !data.descriptions[index]) {
      errors.descriptions = {
        type: "required",
        message:
          "La descripción del lugar en " +
          languaguesList[index] +
          " es requerida",
      };
    } else if (data.descriptions[index].length > 1024) {
      errors.descriptions = {
        type: "maxLength",
        message: "El tamaño no debe sobrepasar los 1024 caracteres",
      };
    }
  }

  // Validate available days
  if (!data.available) {
    errors.available = {
      type: "required",
      message: "Se requieren los días que está disponible el lugar",
    };
  } else {
    if (data.available === AvailableDays.CUSTOM) {
      if (!data.startDate) {
        errors.startDate = {
          type: "required",
          message: "Se requiere la fecha de inicio",
        };
      }

      if (!data.endDate) {
        errors.endDate = {
          type: "required",
          message: "Se requiere la fecha de cierre",
        };
      }

      if (data.startDate && data.endDate) {
        const startDate = new Date(data.startDate);
        const endDate = new Date(data.endDate);
        if (startDate.getTime() > endDate.getTime()) {
          errors.endDate = {
            type: "required",
            message:
              "La fecha de inicio no puede ser después de la fecha de cierre",
          };
        }
      }
    }
  }

  // Validate latitude and longitude
  if (!data.latitude || !data.longitude) {
    errors.latitude = {
      type: "required",
      message: "Debe de seleccionar la ubicación del lugar",
    };
  }

  // Validate imagesList
  if (!data.imagesList || data.imagesList.length < MIN_NUMBER_PLACE_IMAGES) {
    errors.imagesList = {
      type: "required",
      message:
        "Debe de haber al menos " +
        MIN_NUMBER_PLACE_IMAGES +
        " imagénes representativas del lugar",
    };
  }

  // Return validation results
  return {
    values: Object.keys(errors).length > 0 ? {} : data,
    errors: errors,
  };
};

// Custom hook for managing place-related form and state
export const usePlace = (
  forceRenderList?: () => void,
  setIsWindowActive?: (visibility: boolean) => void
) => {
  // Initialize form methods and state
  const {
    register,
    handleSubmit,
    setValue,
    formState: { errors },
    clearErrors,
    resetField,
    getValues,
  } = useForm<Place>({ resolver });
  const [
    languageDescriptionIndexSelected,
    setLanguageDescriptionIndexSelected,
  ] = useState(0);
  const [descriptions, setDescriptions] = useState<string[]>(
    new Array(languaguesList.length).fill("")
  );
  const [availableDays, setAvailableDays] = useState<AvailableDays>(
    AvailableDays.WEEKEND
  );
  const [placeList, setPlaceList] = useState<Place[]>([]);
  const [pending, setPending] = useState(false);
  const [categoriesId, setCategoriesId] = useState<number[]>([]);
  const [updatedCategories, setUpdatedCategories] = useState(false);

  // Update categoriesId when it changes
  useEffect(() => {
    setValue("categoriesId", categoriesId, {
      shouldValidate: updatedCategories,
    });
    setUpdatedCategories(true);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [categoriesId]);

  // Handle form submission for registering a place
  const onSubmitRegister: SubmitHandler<Place> = (data: Place) => {
    const fetch = async () => {
      try {
        await placeRepository.registerPlace(data).then(() => {
          if (forceRenderList && setIsWindowActive) {
            forceRenderList();
            setIsWindowActive(false);
          }
        });
      } catch (error: any) {
        let errorMessage: string = "Ha ocurrido un error";
        if (axios.isAxiosError(error)) {
          error as AxiosError;
          switch (error.code) {
            case axios.AxiosError.ERR_BAD_REQUEST:
              errorMessage = "Acceso no autorizado";
              break;
            case axios.AxiosError.ERR_NETWORK:
              errorMessage = "Conexión con el servidor fallida";
              break;
          }
        }
        throw new Error(errorMessage);
      }
    };
    toast.promise(fetch(), {
      pending: "Subiendo datos...",
      success: "Los datos se han subido correctamente",
      error: {
        render({ data }) {
          return (data as Error).message;
        },
      },
    });
  };

  // Handle form submission for updating a place
  const onSubmitUpdate: SubmitHandler<Place> = (data: Place) => {
    const fetch = async () => {
      try {
        await placeRepository.updatePlace(data);
        if (forceRenderList && setIsWindowActive) {
          forceRenderList();
          setIsWindowActive(false);
        }
      } catch (error: any) {
        let errorMessage: string = "Ha ocurrido un error";
        if (axios.isAxiosError(error)) {
          error as AxiosError;
          switch (error.code) {
            case axios.AxiosError.ERR_BAD_REQUEST:
              errorMessage = "Acceso no autorizado";
              break;
            case axios.AxiosError.ERR_NETWORK:
              errorMessage = "Conexión con el servidor fallida";
              break;
          }
        }
        throw new Error(errorMessage);
      }
    };
    toast.promise(fetch(), {
      pending: "Subiendo datos...",
      success: "Los datos se han subido correctamente",
      error: {
        render({ data }) {
          return (data as Error).message;
        },
      },
    });
  };

  // Update form time fields
  const updateTimeForm = (time: string, isOpeningHour: boolean) => {
    const timeSplitted = time.split(":");
    let hours = Number(timeSplitted[0]);
    const minutes = Number(timeSplitted[1]);
    hours += minutes / 60;
    hours = Math.round(hours);
    if (isOpeningHour) {
      setValue("openAt", hours, { shouldValidate: true });
    } else {
      setValue("closeAt", hours, { shouldValidate: true });
    }
  };

  // Fetch places by town ID
  const updatePlacesByTown = async (idTown: number) => {
    setPending(true);
    try {
      const places = await placeRepository.getPlacesByTown(idTown);
      setPlaceList(places);
      setPending(false);
    } catch (error: any) {
      if (axios.isAxiosError(error)) {
        error as AxiosError;
        showErrorAxios(error);
      }
      setPending(false);
    }
  };

  // Fetch place by ID
  const getPlaceById = async (idPlace: number): Promise<Place | null> => {
    try {
      const place = await placeRepository.getPlaceById(idPlace);
      return place;
    } catch (error: any) {
      if (axios.isAxiosError(error)) {
        error as AxiosError;
        showErrorAxios(error);
      }
    }
    return null;
  };

  // Return hook methods and state
  return {
    register,
    handleSubmit,
    errors,
    onSubmitRegister,
    onSubmitUpdate,
    setValue,
    languageDescriptionIndexSelected,
    setLanguageDescriptionIndexSelected,
    descriptions,
    setDescriptions,
    clearErrors,
    updateTimeForm,
    availableDays,
    setAvailableDays,
    resetField,
    updatePlacesByTown,
    placeList,
    pending,
    categoriesId,
    setCategoriesId,
    getPlaceById,
    getValues,
  };
};
