"use client";

import {
    useCallback,
    useMemo,
    useState,
} from "react";
import {
    useQuery,
    useQueryClient,
} from "@tanstack/react-query";

import { DEFAULT_NOTIFICATION_SETTINGS } from "../constants/notification-setting-data";
import {
    getNotificationSetting,
    updateNotificationSetting,
} from "../service/notification-setting-service";
import type {
    NotificationSettingApiData,
    NotificationSettingsState,
} from "../types/notification-setting-types";

export const NOTIFICATION_SETTING_UPDATED_EVENT =
    "notification-setting-updated";

export const NOTIFICATION_SETTING_QUERY_KEY = [
    "notification-setting",
] as const;

type SaveNotificationSettingResult = {
    success: boolean;
    message?: string;
    data?: NotificationSettingApiData;
};

function mapApiDataToState(
    data: NotificationSettingApiData,
): NotificationSettingsState {
    return {
        system: {
            enabled: data.systemEnabled,
            unreadBadge: data.unreadBadge,
        },

        gmail: {
            enabled: data.gmailEnabled,
            email: data.gmailAddress ?? "",
        },
    };
}

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

    return fallback;
}

export function dispatchNotificationSettingUpdated() {
    if (typeof window === "undefined") {
        return;
    }

    window.dispatchEvent(
        new Event(
            NOTIFICATION_SETTING_UPDATED_EVENT,
        ),
    );
}

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

    /*
     * draftSettings មានតម្លៃនៅពេល User ប្តូរ Switch។
     * បើមិនទាន់ប្តូរ យើងប្រើ data ដែលបានមកពី API។
     */
    const [
        draftSettings,
        setDraftSettings,
    ] =
        useState<NotificationSettingsState | null>(
            null,
        );

    const [
        saving,
        setSaving,
    ] = useState(false);

    const [
        saveError,
        setSaveError,
    ] = useState<string | null>(null);

    const [
        savedLastUpdated,
        setSavedLastUpdated,
    ] = useState<string | null>(null);

    /*
     * React Query ទទួលខុសត្រូវលើការទាញ API។
     * មិនចាំបាច់ហៅ setState ក្នុង useEffect ទៀតទេ។
     */
    const settingQuery = useQuery({
        queryKey: NOTIFICATION_SETTING_QUERY_KEY,
        queryFn: getNotificationSetting,
        staleTime: 30_000,
        refetchOnWindowFocus: false,
        retry: 1,
    });

    const serverSettings = useMemo(() => {
        if (!settingQuery.data) {
            return DEFAULT_NOTIFICATION_SETTINGS;
        }

        return mapApiDataToState(
            settingQuery.data,
        );
    }, [settingQuery.data]);

    /*
     * បើ User បានកែ Switch ប្រើ draft។
     * បើមិនទាន់កែ ប្រើ API data។
     */
    const settings =
        draftSettings ?? serverSettings;

    const queryError = settingQuery.error
        ? getErrorMessage(
            settingQuery.error,
            "Failed to load notification settings.",
        )
        : null;

    const error =
        saveError ?? queryError;

    const lastUpdated =
        savedLastUpdated ??
        settingQuery.data?.updatedAt ??
        null;

    const loading =
        settingQuery.isLoading ||
        settingQuery.isFetching &&
        !settingQuery.data;

    /*
     * អនុញ្ញាតឱ្យ Screen ឬ Component ផ្សេង
     * update settings ទាំងមូល បើត្រូវការ។
     */
    const setSettings = useCallback(
        (
            value:
                | NotificationSettingsState
                | ((
                    current: NotificationSettingsState,
                ) => NotificationSettingsState),
        ) => {
            setDraftSettings((currentDraft) => {
                const current =
                    currentDraft ?? serverSettings;

                if (typeof value === "function") {
                    return value(current);
                }

                return value;
            });

            setSaveError(null);
        },
        [serverSettings],
    );

    const loadSettings =
        useCallback(async () => {
            setSaveError(null);

            const result =
                await settingQuery.refetch();

            if (result.data) {
                /*
                 * បន្ទាប់ពី Reload យក Server data ថ្មី
                 * ហើយលុប draft ចាស់។
                 */
                setDraftSettings(null);

                setSavedLastUpdated(
                    result.data.updatedAt,
                );
            }

            return result.data ?? null;
        }, [settingQuery]);

    const updateSystemEnabled =
        useCallback(
            (enabled: boolean) => {
                setDraftSettings(
                    (currentDraft) => {
                        const current =
                            currentDraft ??
                            serverSettings;

                        return {
                            ...current,

                            system: {
                                ...current.system,
                                enabled,

                                /*
                                 * ពេល System OFF
                                 * Unread Badge ត្រូវ OFF ដែរ។
                                 */
                                unreadBadge: enabled
                                    ? current.system
                                        .unreadBadge
                                    : false,
                            },
                        };
                    },
                );

                setSaveError(null);
            },
            [serverSettings],
        );

    const updateUnreadBadge =
        useCallback(
            (unreadBadge: boolean) => {
                setDraftSettings(
                    (currentDraft) => {
                        const current =
                            currentDraft ??
                            serverSettings;

                        return {
                            ...current,

                            system: {
                                ...current.system,
                                unreadBadge:
                                    current.system.enabled
                                        ? unreadBadge
                                        : false,
                            },
                        };
                    },
                );

                setSaveError(null);
            },
            [serverSettings],
        );

    const updateGmailEnabled =
        useCallback(
            (enabled: boolean) => {
                setDraftSettings(
                    (currentDraft) => {
                        const current =
                            currentDraft ??
                            serverSettings;

                        return {
                            ...current,

                            gmail: {
                                ...current.gmail,
                                enabled,
                            },
                        };
                    },
                );

                setSaveError(null);
            },
            [serverSettings],
        );

    const saveSettings = useCallback(
        async (
            loggedInEmail: string,
        ): Promise<SaveNotificationSettingResult> => {
            setSaving(true);
            setSaveError(null);

            try {
                const normalizedEmail =
                    loggedInEmail
                        .trim()
                        .toLowerCase();

                if (
                    settings.gmail.enabled &&
                    !normalizedEmail
                ) {
                    throw new Error(
                        "Logged-in user email was not found.",
                    );
                }

                const data =
                    await updateNotificationSetting({
                        systemEnabled:
                            settings.system.enabled,

                        unreadBadge:
                            settings.system.enabled &&
                            settings.system.unreadBadge,

                        gmailEnabled:
                            settings.gmail.enabled,

                        gmailAddress:
                            settings.gmail.enabled
                                ? normalizedEmail
                                : null,
                    });

                /*
                 * Update React Query cache ដោយផ្ទាល់
                 * ដើម្បីកុំឱ្យ API call ស្ទួន។
                 */
                queryClient.setQueryData(
                    NOTIFICATION_SETTING_QUERY_KEY,
                    data,
                );

                /*
                 * ក្រោយ Save ជោគជ័យ មិនចាំបាច់
                 * រក្សា draft ទៀតទេ ព្រោះ cache
                 * មាន data ថ្មីរួចហើយ។
                 */
                setDraftSettings(null);
                setSavedLastUpdated(
                    data.updatedAt,
                );

                dispatchNotificationSettingUpdated();

                if (
                    typeof window !==
                    "undefined"
                ) {
                    window.dispatchEvent(
                        new Event(
                            "system-notification-updated",
                        ),
                    );
                }

                return {
                    success: true,
                    data,
                };
            } catch (saveSettingError) {
                const message =
                    getErrorMessage(
                        saveSettingError,
                        "Failed to save notification settings.",
                    );

                setSaveError(message);

                return {
                    success: false,
                    message,
                };
            } finally {
                setSaving(false);
            }
        },
        [
            queryClient,
            settings,
        ],
    );

    return {
        settings,
        setSettings,

        loading,
        saving,
        error,
        lastUpdated,

        loadSettings,
        saveSettings,

        updateSystemEnabled,
        updateUnreadBadge,
        updateGmailEnabled,
    };
}