import {defineStore, getActivePinia} from 'pinia'
import {config} from '@/config'
import {toRaw} from "vue";
import {getCsrfToken} from "@/backend.js";
import logger from "@/logger.js";

export const useNotificationStore = defineStore('notification', {
    state() {
        return {
            notifications: [],
            sendCache: [],
            autoSubmitInterval: 30000,
            autoSubmitTimer: null,
            spinnerMessage: null
        }
    },
    getters: {
        hasSpinner(state) {
            return state.spinnerMessage !== null;
        },
        spinnerMsg(state) {
            return state.spinnerMessage;
        }
    },
    actions: {
        setSpinner(msg) {
            this.spinnerMessage = msg;
        },
        clearSpinner() {
            this.spinnerMessage = null;
        },
        popNotification() {
            return this.notifications.pop();
        },
        addNotification(notification) {
            this.notifications.push({
                icon: notification.icon ?? null,
                message: notification.message ?? 'Unknown notification',
                title: notification.title ?? 'Notification',
                variant: notification.variant ?? 'success',
                duration: notification.duration ?? 8000
            })
        },
        async addError(errorDto, options = {}, silent = false) {
            const {request = null, store = null, global = false} = options;
            const state = this.captureStoreState(store, global);

            if (!silent) {
                this.addNotification({
                    icon: 'bug',
                    message: errorDto.message ?? 'Unknown error code',
                    title: errorDto.title ?? 'Unknown error',
                    variant: 'exception',
                });
            }

            const error = {
                error: {
                    code: errorDto.code ?? 'Unknown error code',
                    title: errorDto.title ?? 'Unknown error',
                    message: errorDto.message ?? 'Unknown message',
                    trace: errorDto.trace ?? null,
                    traceCombined: errorDto.traceCombined ?? null,
                },
                request: request ? JSON.stringify(request) : null,
                state: state ? JSON.stringify(state) : null,
                timestamp: Date.now()
            }

            logger.log(error);

            this.sendCache.push(error);
            await this.transmitErrors();
        },
        async transmitErrors() {

            const params = {
                method: 'POST',
                credentials: 'include',
                headers: {
                    'Content-Type': 'application/json',
                    'X-XSRF-TOKEN': getCsrfToken()
                },
                body: JSON.stringify(toRaw(this.sendCache))
            };

            const url = `${config.backendUrl}/error/`;

            const response = await fetch(url, params
            ).catch(e => {
                this.startAutoSubmitTimer();
            });

            if (response?.ok) {
                this.stopAutoSubmitTimer()
                this.sendCache = [];
            } else {
                logger.error("Error transmitting errors: " + url, params);
                logger.error(response, await response?.text());
                this.startAutoSubmitTimer();
            }
        },
        startAutoSubmitTimer() {
            if (this.autoSubmitTimer) return

            this.autoSubmitTimer = setTimeout(() => {
                this.transmitErrors()
            }, this.autoSubmitInterval)
        },
        stopAutoSubmitTimer() {
            if (this.autoSubmitTimer) {
                clearTimeout(this.autoSubmitTimer)
                this.autoSubmitTimer = null
            }
        },
        captureStoreState(selectedStore = null, global = false) {
            const storeState = {}

            try {

                if (selectedStore && selectedStore.$id) {
                    storeState[selectedStore.$id] = {
                        ...toRaw(selectedStore.$state)
                    }
                }

                if (global) {
                    const pinia = this.$pinia || getActivePinia()
                    if (pinia && pinia._s) {
                        pinia._s.forEach((store, storeId) => {
                            if (storeId !== 'notification' && storeId !== 'errorLog' && store.$state) {
                                storeState[storeId] = {
                                    ...toRaw(store.$state)
                                }
                            }
                        })
                    }
                }
            } catch (err) {
                logger.warn('Failed to capture store state:', err);
                return {};
            }

            return storeState;
        },
        async submitOnBeforeUnload() {
            if (this.sendCache.length > 0) {
                navigator.sendBeacon(`${config.backendUrl}/error/`, JSON.stringify(
                    toRaw(this.sendCache)
                ))
            }
        }
    },


});

// Global Error Handler Setup
export function setupErrorBuffer() {
    const errorStore = useNotificationStore()

    //Unhandled Promise Rejections
    window.addEventListener('unhandledrejection', (event) => {

        const error = {
            code: "Unhandled rejection",
            title: "Frontend error",
            message: event.reason?.message || 'Unhandled Promise Rejection',
            traceCombined: event.reason?.stack,
        };

        errorStore.addError(error, {global: true}, true).then(r => {
        });
    })

    // // JavaScript Errors
    window.addEventListener('error', (event) => {
        const error = {
            code: "General error",
            title: "Frontend error",
            message: event.reason?.message || 'Unhandled Promise Rejection',
            traceCombined: event.reason?.stack,
        };
        errorStore.addError(error, {global: true}).then(r => {
        });
    })

    window.addEventListener('beforeunload', async () => {
        await errorStore.submitOnBeforeUnload();
    })
}