import type {
    ClientNotificationFilters,
    ClientPaginationState,
    Notifications,
} from "@pkg/logic/domains/notifications/data";
import {
    archiveSC,
    deleteNotificationsSC,
    getNotificationsSQ,
    getUnreadCountSQ,
    markAllReadSC,
    markReadSC,
    markUnreadSC,
    unarchiveSC,
} from "./notifications.remote";
import { user } from "$lib/global.stores";
import { toast } from "svelte-sonner";
import { get } from "svelte/store";

class NotificationViewModel {
    notifications = $state([] as Notifications);
    loading = $state(false);
    selectedIds = $state(new Set<number>());

    pagination = $state<ClientPaginationState>({
        page: 1,
        pageSize: 20,
        total: 0,
        totalPages: 0,
        sortBy: "createdAt",
        sortOrder: "desc",
    });

    filters = $state<ClientNotificationFilters>({
        userId: get(user)?.id!,
        isArchived: false,
    });

    unreadCount = $state(0);

    private getFetchQueryInput() {
        return {
            filters: {
                userId: this.filters.userId,
                isRead: this.filters.isRead,
                isArchived: this.filters.isArchived,
                type: this.filters.type,
                category: this.filters.category,
                priority: this.filters.priority,
                search: this.filters.search,
            },
            pagination: {
                page: this.pagination.page,
                pageSize: this.pagination.pageSize,
                sortBy: this.pagination.sortBy,
                sortOrder: this.pagination.sortOrder,
            },
        };
    }

    private async runCommand(
        fn: (payload: { notificationIds: number[] }) => Promise<any>,
        notificationIds: number[],
        errorMessage: string,
        after: Array<() => Promise<void>>,
    ) {
        try {
            const result = await fn({ notificationIds });
            if (result?.error) {
                toast.error(result.error.message || errorMessage, {
                    description: result.error.description || "Please try again later",
                });
                return;
            }

            for (const action of after) {
                await action();
            }
        } catch (error) {
            toast.error(errorMessage, {
                description:
                    error instanceof Error ? error.message : "Please try again later",
            });
        }
    }

    async fetchNotifications() {
        this.loading = true;
        try {
            const result = await getNotificationsSQ(this.getFetchQueryInput());
            if (result?.error || !result?.data) {
                toast.error(
                    result?.error?.message || "Failed to fetch notifications",
                    {
                        description:
                            result?.error?.description || "Please try again later",
                    },
                );
                return;
            }

            this.notifications = result.data.data as Notifications;
            this.pagination.total = result.data.total;
            this.pagination.totalPages = result.data.totalPages;
        } catch (error) {
            toast.error("Failed to fetch notifications", {
                description:
                    error instanceof Error ? error.message : "Please try again later",
            });
        } finally {
            this.loading = false;
        }
    }

    async markAsRead(notificationIds: number[]) {
        await this.runCommand(markReadSC, notificationIds, "Failed to mark as read", [
            () => this.fetchNotifications(),
            () => this.fetchUnreadCount(),
        ]);
    }

    async markAsUnread(notificationIds: number[]) {
        await this.runCommand(
            markUnreadSC,
            notificationIds,
            "Failed to mark as unread",
            [() => this.fetchNotifications(), () => this.fetchUnreadCount()],
        );
    }

    async archive(notificationIds: number[]) {
        await this.runCommand(archiveSC, notificationIds, "Failed to archive", [
            () => this.fetchNotifications(),
        ]);
    }

    async unarchive(notificationIds: number[]) {
        await this.runCommand(unarchiveSC, notificationIds, "Failed to unarchive", [
            () => this.fetchNotifications(),
        ]);
    }

    async deleteNotifications(notificationIds: number[]) {
        await this.runCommand(
            deleteNotificationsSC,
            notificationIds,
            "Failed to delete",
            [() => this.fetchNotifications(), () => this.fetchUnreadCount()],
        );
    }

    async markAllAsRead() {
        try {
            const result = await markAllReadSC({});
            if (result?.error) {
                toast.error(result.error.message || "Failed to mark all as read", {
                    description: result.error.description || "Please try again later",
                });
                return;
            }
            await this.fetchNotifications();
            await this.fetchUnreadCount();
        } catch (error) {
            toast.error("Failed to mark all as read", {
                description:
                    error instanceof Error ? error.message : "Please try again later",
            });
        }
    }

    async fetchUnreadCount() {
        try {
            const result = await getUnreadCountSQ();
            if (result?.error) {
                return;
            }
            if (result?.data !== undefined && result?.data !== null) {
                this.unreadCount = result.data as number;
            }
        } catch {
            // Intentionally silent - unread count is non-critical UI data.
        }
    }

    toggleSelection(id: number) {
        if (this.selectedIds.has(id)) {
            this.selectedIds.delete(id);
        } else {
            this.selectedIds.add(id);
        }
    }

    selectAll() {
        this.notifications.forEach((n) => this.selectedIds.add(n.id));
    }

    clearSelection() {
        this.selectedIds.clear();
    }

    goToPage(page: number) {
        this.pagination.page = page;
        this.fetchNotifications();
    }

    setPageSize(pageSize: number) {
        this.pagination.pageSize = pageSize;
        this.pagination.page = 1;
        this.fetchNotifications();
    }

    setSorting(
        sortBy: ClientPaginationState["sortBy"],
        sortOrder: ClientPaginationState["sortOrder"],
    ) {
        this.pagination.sortBy = sortBy;
        this.pagination.sortOrder = sortOrder;
        this.pagination.page = 1;
        this.fetchNotifications();
    }

    setFilters(newFilters: Partial<ClientNotificationFilters>) {
        this.filters = { ...this.filters, ...newFilters };
        this.pagination.page = 1;
        this.fetchNotifications();
    }

    clearFilters() {
        this.filters = { userId: get(user)?.id!, isArchived: false };
        this.pagination.page = 1;
        this.fetchNotifications();
    }
}

export const notificationViewModel = new NotificationViewModel();
