"use client";

import { useCallback, useEffect, useState, type ChangeEvent } from "react";
import { useMutation, useQuery, useQueryClient, type QueryClient } from "@tanstack/react-query";

import {
    changeMyEmail,
    changeMyPassword,
    extractAccountProfile,
    getMyProfile,
    mergeAccountProfile,
    updateMyProfile,
    verifyMyEmailOtp,
    type AccountProfile,
} from "@/features/account-setting/service/account-setting-service";
import {
    CURRENT_USER_QUERY_KEY,
    type CurrentUser,
} from "@/features/auth/hook/use-current-user";

type AccountFormState = {
    name: string;
    email: string;
    position: string;
    avatar: string;
};

type PasswordFormState = {
    currentPassword: string;
    newPassword: string;
    confirmPassword: string;
};

type EmailFormState = {
    newEmail: string;
    currentPassword: string;
};

type EmailChangeStep = "form" | "otp";

const ACCOUNT_PROFILE_QUERY_KEY = ["account-profile"];
const MAX_AVATAR_FILE_SIZE = 5 * 1024 * 1024;
const ALLOWED_AVATAR_FILE_TYPES = new Set([
    "image/jpeg",
    "image/png",
    "image/svg+xml",
]);

function createEmptyOtp() {
    return ["", "", "", "", "", ""];
}

function isValidEmail(value: string) {
    return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
}

function getErrorMessage(error: unknown, fallback: string) {
    return error instanceof Error ? error.message : fallback;
}

function syncCurrentUserCache(queryClient: QueryClient, profile: AccountProfile) {
    queryClient.setQueryData<CurrentUser>(CURRENT_USER_QUERY_KEY, (current) => {
        if (!current) {
            return {
                id: profile.id,
                email: profile.email,
                name: profile.name,
                position: profile.position,
                avatar: profile.avatar,
                roles: [],
                isActive: true,
                createdAt: profile.createdAt,
                updatedAt: profile.updatedAt,
            };
        }

        return {
            ...current,
            email: profile.email,
            name: profile.name,
            position: profile.position,
            avatar: profile.avatar,
            updatedAt: profile.updatedAt,
        };
    });
}

export function useAccountSetting() {
    const queryClient = useQueryClient();

    const profileQuery = useQuery({
        queryKey: ACCOUNT_PROFILE_QUERY_KEY,
        queryFn: getMyProfile,
    });

    const [avatarFile, setAvatarFile] = useState<File | null>(null);
    const [avatarPreviewUrl, setAvatarPreviewUrl] = useState("");

    const [form, setForm] = useState<AccountFormState>({
        name: "",
        email: "",
        position: "",
        avatar: "",
    });

    const [passwordForm, setPasswordForm] = useState<PasswordFormState>({
        currentPassword: "",
        newPassword: "",
        confirmPassword: "",
    });

    const [emailForm, setEmailForm] = useState<EmailFormState>({
        newEmail: "",
        currentPassword: "",
    });

    const [emailChangeStep, setEmailChangeStep] =
        useState<EmailChangeStep>("form");
    const [emailOtp, setEmailOtp] = useState<string[]>(createEmptyOtp);
    const [pendingEmail, setPendingEmail] = useState("");
    const [error, setError] = useState("");
    const [successMessage, setSuccessMessage] = useState("");

    const fillForm = useCallback((profile: AccountProfile) => {
        setAvatarFile(null);
        setAvatarPreviewUrl("");
        setForm({
            name: profile.name || "",
            email: profile.email || "",
            position: profile.position || "",
            avatar: profile.avatar || "",
        });
        setEmailForm((current) => ({
            ...current,
            newEmail: profile.email || "",
        }));
    }, []);

    const getCachedProfile = useCallback(() => {
        return queryClient.getQueryData<AccountProfile>(ACCOUNT_PROFILE_QUERY_KEY);
    }, [queryClient]);

    const [prevProfileData, setPrevProfileData] =
        useState<AccountProfile | undefined>(undefined);

    if (profileQuery.data !== prevProfileData) {
        setPrevProfileData(profileQuery.data);
        if (profileQuery.data) {
            fillForm(profileQuery.data);
        }
    }

    useEffect(() => {
        if (!avatarPreviewUrl) {
            return;
        }

        return () => {
            URL.revokeObjectURL(avatarPreviewUrl);
        };
    }, [avatarPreviewUrl]);

    const updateProfileMutation = useMutation({
        mutationFn: updateMyProfile,
        onSuccess: (updatedProfile) => {
            const nextProfile = mergeAccountProfile(
                updatedProfile,
                getCachedProfile(),
            );

            queryClient.setQueryData(ACCOUNT_PROFILE_QUERY_KEY, nextProfile);
            syncCurrentUserCache(queryClient, nextProfile);
            fillForm(nextProfile);
            setSuccessMessage("Profile updated successfully.");
        },
    });

    const changePasswordMutation = useMutation({
        mutationFn: changeMyPassword,
        onSuccess: (response) => {
            setPasswordForm({
                currentPassword: "",
                newPassword: "",
                confirmPassword: "",
            });
            setSuccessMessage(response.message || "Password changed successfully.");
        },
    });

    const changeEmailMutation = useMutation({
        mutationFn: changeMyEmail,
        onSuccess: (response, payload) => {
            setPendingEmail(payload.newEmail);
            setEmailOtp(createEmptyOtp());
            setEmailChangeStep("otp");
            setSuccessMessage(response.message || "OTP code sent to your new email.");
        },
    });

    const resendEmailOtpMutation = useMutation({
        mutationFn: changeMyEmail,
        onSuccess: (response) => {
            setEmailOtp(createEmptyOtp());
            setSuccessMessage(response.message || "OTP code resent.");
        },
    });

    const verifyEmailMutation = useMutation({
        mutationFn: verifyMyEmailOtp,
        onSuccess: (response) => {
            const verifiedProfile = extractAccountProfile(response.data);
            const nextProfile = mergeAccountProfile(
                verifiedProfile,
                getCachedProfile(),
            );

            queryClient.setQueryData(ACCOUNT_PROFILE_QUERY_KEY, nextProfile);
            syncCurrentUserCache(queryClient, nextProfile);
            fillForm(nextProfile);
            setEmailOtp(createEmptyOtp());
            setEmailForm({
                newEmail: nextProfile.email || pendingEmail,
                currentPassword: "",
            });
            setPendingEmail("");
            setEmailChangeStep("form");
            setSuccessMessage(response.message || "Email address changed successfully.");
        },
    });

    const handleInputChange =
        (field: "name" | "position") =>
            (event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
                setForm((current) => ({
                    ...current,
                    [field]: event.target.value,
                }));
                setError("");
                setSuccessMessage("");
            };

    const handlePasswordInputChange =
        (field: keyof PasswordFormState) =>
            (event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
                setPasswordForm((current) => ({
                    ...current,
                    [field]: event.target.value,
                }));
                setError("");
                setSuccessMessage("");
            };

    const handleEmailInputChange =
        (field: keyof EmailFormState) =>
            (event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
                setEmailForm((current) => ({
                    ...current,
                    [field]: event.target.value,
                }));
                setError("");
                setSuccessMessage("");
            };

    const handleAvatarFileChange = (event: ChangeEvent<HTMLInputElement>) => {
        const file = event.target.files?.[0] ?? null;
        event.target.value = "";

        if (!file) {
            return;
        }

        setError("");
        setSuccessMessage("");

        if (!ALLOWED_AVATAR_FILE_TYPES.has(file.type)) {
            setAvatarFile(null);
            setAvatarPreviewUrl("");
            setError("Please upload a JPG, PNG, or SVG image.");
            return;
        }

        if (file.size > MAX_AVATAR_FILE_SIZE) {
            setAvatarFile(null);
            setAvatarPreviewUrl("");
            setError("Avatar image must be 5MB or smaller.");
            return;
        }

        setAvatarFile(file);
        setAvatarPreviewUrl(URL.createObjectURL(file));
    };

    const handleEmailOtpChange = (index: number, value: string) => {
        const digits = value.replace(/\D/g, "").slice(0, 6);

        setEmailOtp((current) => {
            const next = [...current];

            if (digits.length > 1) {
                for (let i = 0; i < 6; i += 1) {
                    next[i] = digits[i] ?? "";
                }

                return next;
            }

            next[index] = digits.slice(0, 1);
            return next;
        });

        setError("");
        setSuccessMessage("");
    };

    const handleBackToEmailForm = () => {
        setEmailChangeStep("form");
        setEmailOtp(createEmptyOtp());
        setError("");
        setSuccessMessage("");
    };

    const handleUpdateProfile = async () => {
        try {
            setError("");
            setSuccessMessage("");

            const name = form.name.trim();
            const position = form.position.trim();

            if (!name) {
                throw new Error("Please enter full name before saving.");
            }

            if (!form.email && !profileQuery.data?.email) {
                throw new Error("Account data is not loaded yet. Please refresh.");
            }

            await updateProfileMutation.mutateAsync({
                name,
                position,
                avatarFile,
            });
        } catch (err) {
            setError(getErrorMessage(err, "Failed to update profile"));
        }
    };

    const handleChangePassword = async () => {
        try {
            setError("");
            setSuccessMessage("");

            const currentPassword = passwordForm.currentPassword;
            const newPassword = passwordForm.newPassword;
            const confirmPassword = passwordForm.confirmPassword;

            if (!currentPassword) {
                throw new Error("Please enter current password.");
            }

            if (!newPassword) {
                throw new Error("Please enter new password.");
            }

            if (newPassword.length < 8) {
                throw new Error("New password must be at least 8 characters.");
            }

            if (newPassword !== confirmPassword) {
                throw new Error("Confirm password does not match.");
            }

            await changePasswordMutation.mutateAsync({
                currentPassword,
                newPassword,
            });
        } catch (err) {
            setError(getErrorMessage(err, "Failed to change password"));
        }
    };

    const handleChangeEmail = async () => {
        try {
            setError("");
            setSuccessMessage("");

            const newEmail = emailForm.newEmail.trim();
            const currentPassword = emailForm.currentPassword;

            if (!newEmail) {
                throw new Error("Please enter new email.");
            }

            if (!isValidEmail(newEmail)) {
                throw new Error("Please enter a valid email address.");
            }

            if (!currentPassword) {
                throw new Error("Please enter current password.");
            }

            await changeEmailMutation.mutateAsync({
                newEmail,
                currentPassword,
            });
        } catch (err) {
            setError(getErrorMessage(err, "Failed to change email"));
        }
    };

    const handleResendEmailOtp = async () => {
        try {
            setError("");
            setSuccessMessage("");

            const newEmail = pendingEmail || emailForm.newEmail.trim();
            const currentPassword = emailForm.currentPassword;

            if (!newEmail) {
                throw new Error("Please enter new email.");
            }

            if (!currentPassword) {
                throw new Error("Please enter current password.");
            }

            await resendEmailOtpMutation.mutateAsync({
                newEmail,
                currentPassword,
            });
        } catch (err) {
            setError(getErrorMessage(err, "Failed to resend OTP"));
        }
    };

    const handleVerifyEmailOtp = async () => {
        try {
            setError("");
            setSuccessMessage("");

            const otp = emailOtp.join("");

            if (otp.length !== 6) {
                throw new Error("Please enter 6 digit OTP code.");
            }

            await verifyEmailMutation.mutateAsync({ otp });
        } catch (err) {
            setError(getErrorMessage(err, "Failed to verify OTP"));
        }
    };

    return {
        profile: profileQuery.data ?? null,
        form,
        avatarPreviewUrl,
        passwordForm,
        emailForm,
        emailChangeStep,
        emailOtp,
        pendingEmail,
        loading: profileQuery.isLoading,
        saving: updateProfileMutation.isPending,
        changingPassword: changePasswordMutation.isPending,
        changingEmail: changeEmailMutation.isPending,
        verifyingEmail: verifyEmailMutation.isPending,
        resendingEmailOtp: resendEmailOtpMutation.isPending,
        error:
            error ||
            (profileQuery.error
                ? getErrorMessage(profileQuery.error, "Failed to load profile")
                : ""),
        successMessage,
        handleInputChange,
        handlePasswordInputChange,
        handleEmailInputChange,
        handleAvatarFileChange,
        handleEmailOtpChange,
        handleBackToEmailForm,
        handleUpdateProfile,
        handleChangePassword,
        handleChangeEmail,
        handleResendEmailOtp,
        handleVerifyEmailOtp,
    };
}