import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import {
  EOtpType,
  getProfile,
  login,
  logout,
  OtpVerificationPayload,
  refreshAuthToken,
  resendOtpCode,
  resendVerificationCode,
  setFayidaPass,
  SetFayidaPasswordPayload,
  setPassword,
  SetPasswordPayload,
  verifyMFAUser,
  verifyOTPCode,
} from "@/shared/services/authService";
import Cookies from "js-cookie";
import { MeDto } from "@/shared/dto/user/meDto";
import { toast } from "sonner";
import { useNavigate } from "react-router-dom";
import { useAuth } from "@/shared/context/AuthContext";
import { useEffect, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { useErrorHandler } from "@/shared/hooks/useErrorHandler";
import { VerifyUserPayload } from "@/record-management/services/api/authService";
import {
  clearRememberMePreference,
  getAuthCookieOptions,
  persistRememberMePreference,
  setAuthCookies,
} from "@/shared/utils/authPersistence";
import { clearComplaintVerification } from "@/complaints/utils/complaintVerificationStorage";

interface LoginPayload {
  email?: string;
  phoneNumber?: string;
  userName?: string;
  password: string;
}
type LoginResponse = { mfaRequired: true } | MeDto; // full user details

export const useAuthUser = () => {
  const queryClient = useQueryClient();
  const navigate = useNavigate();
  const { t } = useTranslation();
  const { handleError } = useErrorHandler(t);
  const delegatedPositionId = Cookies.get("delegatedPositionId");
  const currentPositionId = Cookies.get("current-position-id");

  const {
    setUser,
    user: contextUser,
    selectedPositionId,
    setSelectedPositionId,
    showOtpModal,
    setShowOtpModal,
    rememberMePreference,
    setRememberMePreference,
  } = useAuth();

  const finalizeAuthenticatedSession = async ({
    token,
    refreshToken,
    rememberMe,
  }: {
    token: string;
    refreshToken: string;
    rememberMe: boolean;
  }) => {
    const cookieOptions = getAuthCookieOptions(rememberMe);

    persistRememberMePreference(rememberMe);
    setAuthCookies({ token, refreshToken, rememberMe });
    Cookies.set("i18nextLng", "am", cookieOptions);

    const { data } = await getProfile();
    const userDetails = data as MeDto;

    Cookies.set(
      "auth-user",
      JSON.stringify({
        ...userDetails,
        id: userDetails.id,
        name: userDetails.name,
        permissions: userDetails.permissions,
        roles: userDetails.roles,
      }),
      cookieOptions,
    );

    setUser(userDetails);

    if ((userDetails.employee ?? []).length > 0) {
      const firstPositionId =
        userDetails.employee?.[0]?.positions?.[0]?.employeePositionId;
      if (firstPositionId) {
        setSelectedPositionId(firstPositionId);
        Cookies.set("current-position-id", firstPositionId, cookieOptions);
      }
    }

    queryClient.invalidateQueries({ queryKey: ["authUser"] });

    return userDetails;
  };

  const {
    data: userDetails,
    isLoading,
    isFetching,
    isError,
    refetch,
  } = useQuery<MeDto>({
    queryKey: [
      "authUser",
      selectedPositionId,
      delegatedPositionId,
      currentPositionId,
    ],
    queryFn: async () => {
      const { data } = await getProfile();
      return data as MeDto;
    },
    placeholderData: (previousData) => previousData,
    staleTime: 5 * 60 * 1000,
    retry: false,
    enabled: true,
  });

  useEffect(() => {
    if (userDetails) {
      setUser(userDetails);

      const allPositions = (userDetails.employee ?? []).flatMap(
        (emp) => emp.positions ?? [],
      );

      if (!selectedPositionId && allPositions.length > 0) {
        const fallbackId = allPositions[0]?.employeePositionId;

        if (fallbackId) {
          setSelectedPositionId(fallbackId);
          Cookies.set("current-position-id", fallbackId);
        }
      } else if (selectedPositionId) {
        // Self-heal stale cookies that were set to position.id instead of
        // employeePositionId before this distinction was fixed.
        const matchesEmployeePositionId = allPositions.some(
          (pos) => pos.employeePositionId === selectedPositionId,
        );

        if (!matchesEmployeePositionId) {
          const matchingPosition = allPositions.find(
            (pos) => pos.id === selectedPositionId,
          );

          if (matchingPosition?.employeePositionId) {
            setSelectedPositionId(matchingPosition.employeePositionId);
            Cookies.set(
              "current-position-id",
              matchingPosition.employeePositionId,
            );
          }
        }
      }
    }
  }, [selectedPositionId, setSelectedPositionId, setUser, userDetails]);

  const activePositionLookupId = delegatedPositionId
    ? currentPositionId || selectedPositionId
    : selectedPositionId;

  const filteredUserDetailsCandidate: MeDto | undefined = useMemo(
    () =>
      userDetails
        ? {
            ...userDetails,
            employee: (userDetails.employee ?? []).map((emp) => ({
              ...emp,
              positions: emp.positions.filter(
                (p) =>
                  p.employeePositionId === activePositionLookupId ||
                  p.id === activePositionLookupId,
              ),
            })),
          }
        : undefined,
    [activePositionLookupId, userDetails],
  );

  const hasResolvedActivePosition = useMemo(
    () =>
      !!filteredUserDetailsCandidate?.employee?.some(
        (employee) => employee.positions.length > 0,
      ),
    [filteredUserDetailsCandidate],
  );

  const [lastResolvedUserDetails, setLastResolvedUserDetails] =
    useState<MeDto>();

  useEffect(() => {
    if (filteredUserDetailsCandidate && hasResolvedActivePosition) {
      setLastResolvedUserDetails(filteredUserDetailsCandidate);
    }
  }, [filteredUserDetailsCandidate, hasResolvedActivePosition]);

  const filteredUserDetails =
    hasResolvedActivePosition || !isFetching
      ? filteredUserDetailsCandidate
      : (lastResolvedUserDetails ?? filteredUserDetailsCandidate);

  const selectedPosition = useMemo(
    () =>
      (filteredUserDetails?.employee ?? userDetails?.employee ?? [])
        .flatMap((employee) => employee.positions)
        .find(
          (position) =>
            position.employeePositionId === activePositionLookupId ||
            position.id === activePositionLookupId ||
            position.employeePositionId === selectedPositionId ||
            position.id === selectedPositionId,
        ),
    [
      activePositionLookupId,
      filteredUserDetails?.employee,
      selectedPositionId,
      userDetails?.employee,
    ],
  );

  const selectedPositionPermissionKeys = useMemo(
    () =>
      (filteredUserDetails?.employee ?? [])
        .flatMap((employee) => employee.positions)
        .flatMap((position) =>
          position.permissions.map((permission) => permission.key),
        ),
    [filteredUserDetails],
  );

  const {
    mutate: logoutMutate,
    isError: logoutError,
    isSuccess: logoutSuccess,
  } = useMutation({
    mutationFn: async (redirectPath?: string) => {
      await logout();
      return redirectPath;
    },
    onSuccess: (redirectPath) => {
      // Clear user state
      setUser(null);
      setSelectedPositionId(null);

      // Clear all cookies
      Object.keys(Cookies.get()).forEach((cookieName) => {
        Cookies.remove(cookieName);
      });

      // Clear only auth-related localStorage keys
      clearRememberMePreference();
      setRememberMePreference(false);
      clearComplaintVerification();

      // Clear all React Query cache to prevent stale data from leaking between users
      queryClient.clear();

      // Show success message
      toast.success("Logged out successfully");

      window.location.replace(redirectPath || "/");
    },
    onError: (error, redirectPath) => {
      // Even if API call fails, still clear local data
      setUser(null);
      setSelectedPositionId(null);

      Object.keys(Cookies.get()).forEach((cookieName) => {
        Cookies.remove(cookieName);
      });

      clearRememberMePreference();
      setRememberMePreference(false);
      clearComplaintVerification();
      queryClient.clear();

      handleError(error);

      window.location.replace(redirectPath || "/");
    },
  });

  const {
    mutate: loginMutate,
    isPending: isLoggingInMutating,
    isError: loginError,
    isSuccess: loginSuccess,
  } = useMutation({
    mutationFn: async ({
      payload,
      rememberMeValue,
    }: {
      payload: LoginPayload;
      rememberMeValue: boolean;
    }) => {
      const response = await login(payload);
      if (response.data.mfaRequired) {
        return { mfaRequired: true, rememberMeValue }; // no "data" at all
      }
      return { data: response.data, rememberMeValue };
    },
    onSuccess: async (response) => {
      if ("mfaRequired" in response && response.mfaRequired) {
        setRememberMePreference(response.rememberMeValue);
        setShowOtpModal(true);
        return;
      }

      setRememberMePreference(response.rememberMeValue);

      await finalizeAuthenticatedSession({
        token: response.data.token,
        refreshToken: response.data.refreshToken,
        rememberMe: response.rememberMeValue,
      });

      toast.success("Login Successful", {
        description: "Redirecting...",
      });

      navigate("/homepage");
    },

    onError: (error) => {
      handleError(error);
    },
  });

  const {
    mutate: setPasswordMutate,
    isPending: isSettingPassword,
    isError: setPasswordError,
    isSuccess: setPasswordSuccess,
    error: setPassError,
  } = useMutation({
    mutationFn: async (payload: SetPasswordPayload) => {
      const response = await setPassword(payload);
      return response.data;
    },
    onSuccess: () => {
      toast.success("Password updated successfully", {
        description: "You can now log in with your new password.",
      });
    },
    onError: (error) => {
      handleError(error);
    },
  });

  const {
    mutate: setFayidaPasswordMutate,
    isPending: isSettingFayidaPassword,
    isError: setFayidaPasswordError,
    isSuccess: setFayidaPasswordSuccess,
    error: setFayidaPassError,
  } = useMutation({
    mutationFn: async (payload: SetFayidaPasswordPayload) => {
      const response = await setFayidaPass(payload);
      return response.data;
    },
    onSuccess: () => {
      toast.success("Password updated successfully", {
        description: "You can now log in with your new password.",
      });
    },
    onError: (error) => {
      handleError(error);
    },
  });

  const {
    mutateAsync: verifyOtpMutate,
    isPending: isVerifyingOtp,
    isError: isVerifyOtpError,
    isSuccess: isVerifyOtpSuccess,
    error: setVerifyOtpError,
  } = useMutation({
    mutationFn: async (payload: OtpVerificationPayload) => {
      const response = await verifyOTPCode(payload);
      return response; // return full response (including status, headers, data, etc)
    },
    onSuccess: () => {
      toast.success("Phone Number Verified successfully");
    },
    onError: (error) => {
      handleError(error);
    },
  });

  const {
    mutate: resendVerificationMutate,
    isPending: isResendingCode,
    isError: resendError,
    isSuccess: resendSuccess,
    data: resendData,
    error: resendErrorData,
  } = useMutation({
    mutationFn: async ({
      phoneNumber,
      email,
    }: {
      phoneNumber: string;
      email?: string;
    }) => {
      const response = await resendVerificationCode({ phoneNumber, email });
      return response;
    },
    onSuccess: () => {
      toast.success("Verification code resent successfully!");
    },
    onError: (error) => {
      handleError(error);
    },
  });

  const {
    mutate: resendVerificationCode,
    isPending: isResendingOtpCode,
    isError: resendOtpError,
    isSuccess: resendOtpSuccess,
    data: resendOtpData,
    error: resendOtpErrorData,
  } = useMutation({
    mutationFn: async ({
      email,
      phoneNumber,
    }: {
      phoneNumber: string;
      email?: string;
    }) => {
      const response = await resendOtpCode({ phoneNumber, email });
      return response;
    },
    onSuccess: () => {
      toast.success("OTP code resent successfully!");
    },
    onError: (error) => {
      handleError(error);
    },
  });

  const { mutateAsync: refreshTokenMutate } = useMutation({
    mutationFn: async (refreshToken: string) => {
      const response = await refreshAuthToken({ refreshToken });
      const { token, refreshToken: newRefreshToken } = response.data;
      setAuthCookies({
        token,
        refreshToken: newRefreshToken,
        rememberMe: rememberMePreference,
      });

      return token;
    },
  });

  const {
    mutate: verifyMFAMutate,
    isPending: isMFAPending,
    isError: mfaError,
    isSuccess: mfaSuccess,
  } = useMutation({
    mutationFn: async (payload: VerifyUserPayload) => {
      const response = await verifyMFAUser(payload);
      return response.data;
    },
    onSuccess: async (response) => {
      await finalizeAuthenticatedSession({
        token: response.token,
        refreshToken: response.refreshToken,
        rememberMe: rememberMePreference,
      });

      toast.success("Two Factor Authentication Successful", {
        description: "Redirecting...",
      });

      navigate("/homepage");
    },
    onError: (error) => {
      handleError(error);
    },
  });
  return {
    userDetails: filteredUserDetails,
    unFilteredUserDetails: userDetails,
    selectedPosition,
    selectedPositionPermissionKeys,
    activePositionLookupId,
    selectedPositionId,
    setSelectedPositionId,
    isLoading,
    isFetching,
    isError,
    refetch,
    login: loginMutate,
    isLoggingIn: isLoggingInMutating,
    logout: logoutMutate,
    logoutError,
    logoutSuccess,
    loginSuccess,
    loginError,
    setPassword: setPasswordMutate,
    verifyOtpMutate,
    isVerifyingOtp,
    isVerifyOtpError,
    isVerifyOtpSuccess,
    setVerifyOtpError,
    isSettingPassword,
    setPasswordError: {
      message: setPassError?.message,
    },
    setPasswordSuccess,
    resendVerificationCode: resendVerificationMutate,
    isResendingCode,
    resendError: resendErrorData ? { message: resendErrorData?.message } : null,
    resendSuccess,
    refreshToken: refreshTokenMutate,
    resendOtpCode: resendVerificationCode,
    isResendingOtpCode,
    resendOtpError: resendOtpErrorData
      ? { message: resendOtpErrorData?.message }
      : null,
    resendOtpSuccess,
    resendOtpData,
    resendData,
    setFayidaPassword: setFayidaPasswordMutate,
    setFayidaPassError,
    setFayidaPasswordSuccess,
    setFayidaPasswordError,
    isSettingFayidaPassword,
    verifyMFA: verifyMFAMutate,
    isMFAPending,
    mfaError,
    mfaSuccess,
  };
};
