class SecureStorage {
    constructor() {
        this.storageName = 'userFormData';
        this.storageExpireDays = 30;

        this.cookieName = 'userFormData';
        this.cookieExpireDays = 30;
    }

    async deriveKey(userPhrase, salt) {
        const encoder = new TextEncoder();
        const keyMaterial = await crypto.subtle.importKey(
            'raw',
            encoder.encode(userPhrase),
            { name: 'PBKDF2' },
            false,
            ['deriveKey']
        );

        return crypto.subtle.deriveKey(
            {
                name: 'PBKDF2',
                salt: salt,
                iterations: 100000,
                hash: 'SHA-256'
            },
            keyMaterial,
            { name: 'AES-GCM', length: 256 },
            false,
            ['encrypt', 'decrypt']
        );
    }

    async encryptData(data, userPhrase) {
        const encoder = new TextEncoder();
        const dataString = JSON.stringify(data);

        const salt = crypto.getRandomValues(new Uint8Array(16));
        const iv = crypto.getRandomValues(new Uint8Array(12));

        const key = await this.deriveKey(userPhrase, salt);

        const encryptedData = await crypto.subtle.encrypt(
            { name: 'AES-GCM', iv: iv },
            key,
            encoder.encode(dataString)
        );

        const combined = new Uint8Array(salt.length + iv.length + encryptedData.byteLength);
        combined.set(salt, 0);
        combined.set(iv, salt.length);
        combined.set(new Uint8Array(encryptedData), salt.length + iv.length);

        return btoa(String.fromCharCode(...combined));
    }

    async decryptData(encryptedBase64, userPhrase) {
        try {

            const combined = new Uint8Array(
                atob(encryptedBase64).split('').map(char => char.charCodeAt(0))
            );

            const salt = combined.slice(0, 16);
            const iv = combined.slice(16, 28);
            const encryptedData = combined.slice(28);

            const key = await this.deriveKey(userPhrase, salt);

            const decryptedBuffer = await crypto.subtle.decrypt(
                { name: 'AES-GCM', iv: iv },
                key,
                encryptedData
            );

            const decoder = new TextDecoder();
            const dataString = decoder.decode(decryptedBuffer);
            return JSON.parse(dataString);
        } catch (error) {
            throw new Error('Failed to decrypt data. Invalid phrase or corrupted data.');
        }
    }

    setCookie(name, value, days) {
        const expires = new Date();
        expires.setTime(expires.getTime() + (days * 24 * 60 * 60 * 1000));
        document.cookie = `${name}=${value};expires=${expires.toUTCString()};path=/;SameSite=Strict;Secure=${location.protocol === 'https:'}`;
    }

    getCookie(name) {
        const nameEQ = name + "=";
        const ca = document.cookie.split(';');
        for (let i = 0; i < ca.length; i++) {
            let c = ca[i];
            while (c.charAt(0) === ' ') c = c.substring(1, c.length);
            if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length);
        }
        return null;
    }

    deleteCookie(name) {
        document.cookie = `${name}=;expires=Thu, 01 Jan 1970 00:00:00 UTC;path=/;`;
    }

    async saveFormData(formData, userPhrase) {
        try {
            const encryptedData = await this.encryptData(formData, userPhrase);

            const dataSize = encryptedData.length;
            const dataSizeKB = (dataSize / 1024).toFixed(2);
            const dataSizeMB = (dataSize / (1024 * 1024)).toFixed(2);

            localStorage.setItem(this.storageName, encryptedData);

            localStorage.setItem('hasStoredData', 'true');

            const expirationDate = new Date();
            expirationDate.setTime(expirationDate.getTime() + (this.storageExpireDays * 24 * 60 * 60 * 1000));
            localStorage.setItem('dataExpiration', expirationDate.getTime().toString());

            this.deleteCookie(this.cookieName);
            this.deleteCookie('hasStoredData');
            this.deleteCookie('dataExpiration');

            return true;
        } catch (error) {
            console.error('Failed to save form data:', error);

            if (error.name === 'QuotaExceededError') {
                console.error('localStorage quota exceeded! Data is too large.');
            }

            return false;
        }
    }

    async loadFormData(userPhrase) {
        try {

            let encryptedData = localStorage.getItem(this.storageName);
            let source = 'localStorage';

            if (!encryptedData) {
                encryptedData = this.getCookie(this.cookieName);
                source = 'cookie (legacy)';
            }

            if (!encryptedData) {
                console.error('No encrypted data found in localStorage or cookies');
                return null;
            }

            const dataSize = encryptedData.length;
            const dataSizeKB = (dataSize / 1024).toFixed(2);

            const formData = await this.decryptData(encryptedData, userPhrase);

            if (source === 'cookie (legacy)') {
                await this.saveFormData(formData, userPhrase);
            }

            return formData;
        } catch (error) {
            console.error('Failed to load form data:', error);
            console.error('This could be due to: wrong passphrase, corrupted data, or truncated cookie data');
            throw error;
        }
    }

    hasStoredData() {

        if (localStorage.getItem('hasStoredData') === 'true') {
            return true;
        }

        return this.getCookie('hasStoredData') === 'true';
    }

    clearStoredData() {

        localStorage.removeItem(this.storageName);
        localStorage.removeItem('hasStoredData');
        localStorage.removeItem('dataExpiration');

        this.deleteCookie(this.cookieName);
        this.deleteCookie('hasStoredData');
        this.deleteCookie('dataExpiration');
    }

    getExpirationDays() {
        return this.storageExpireDays;
    }

    getRemainingDays() {
        if (!this.hasStoredData()) {
            return null;
        }

        let expirationTimestamp = localStorage.getItem('dataExpiration');

        if (!expirationTimestamp) {
            expirationTimestamp = this.getCookie('dataExpiration');
        }

        if (expirationTimestamp) {
            const expirationDate = new Date(parseInt(expirationTimestamp));
            const now = new Date();
            const timeDiff = expirationDate.getTime() - now.getTime();
            const daysDiff = Math.ceil(timeDiff / (1000 * 3600 * 24));
            return Math.max(0, daysDiff);
        } else {

            return this.storageExpireDays;
        }
    }
}

window.SecureStorage = SecureStorage;