import * as i0 from '@angular/core';
import { Injectable, InjectionToken } from '@angular/core';
import { SdUtilities, DateUtilities } from '@sd-angular/core/utilities/extensions';
import { BehaviorSubject } from 'rxjs';
import { map } from 'rxjs/operators';

/* eslint-disable @typescript-eslint/no-explicit-any */
class SdCacheService {
    #memoryCache = new Map();
    #subjects = new Map();
    constructor() { }
    create(key, option) {
        if (!key)
            throw new Error('Key is required');
        const hashKey = SdUtilities.hash({ key });
        // 1. Init Subject: Load data có sẵn nếu có
        if (!this.#subjects.has(hashKey)) {
            const existingData = this.#internalGet(hashKey, option);
            this.#subjects.set(hashKey, new BehaviorSubject(existingData ?? option?.default));
        }
        const subject = this.#subjects.get(hashKey);
        // 2. Define helpers
        const get = () => {
            const val = this.#internalGet(hashKey, option);
            // Fix strict type: Ép kiểu về T (chấp nhận rủi ro undefined nếu user không check)
            return (val ?? option?.default);
        };
        const set = (data) => {
            this.#internalSet(hashKey, data, option);
            subject.next(data);
        };
        const has = () => {
            return this.#internalGet(hashKey, option) !== undefined;
        };
        const remove = () => {
            this.#internalRemove(hashKey, option);
            subject.next(undefined);
        };
        const destroy = () => {
            subject.complete();
            this.#subjects.delete(hashKey);
            this.#memoryCache.delete(hashKey);
        };
        // 3. IMPLEMENT LOAD (Get-Or-Set Pattern)
        const load = async (callback) => {
            // Check cache trước
            if (has()) {
                return get();
            }
            // Cache miss -> Gọi API
            try {
                const result = await callback();
                // Chỉ lưu nếu kết quả hợp lệ (khác undefined/null)
                if (result !== undefined && result !== null) {
                    set(result);
                }
                return result;
            }
            catch (error) {
                throw error;
            }
        };
        return {
            get,
            set,
            has,
            remove,
            destroy,
            load,
            observer: subject.asObservable().pipe(map(() => get())),
        };
    }
    // --- PRIVATE HELPERS ---
    #getStorage(option) {
        if (option?.type === 'local')
            return localStorage;
        if (option?.type === 'session')
            return sessionStorage;
        return null;
    }
    #internalGet(key, option) {
        let entry = this.#memoryCache.get(key);
        if (!entry) {
            const storage = this.#getStorage(option);
            if (storage) {
                const raw = storage.getItem(key);
                if (raw) {
                    try {
                        const parsed = JSON.parse(raw);
                        entry = {
                            data: parsed.data,
                            createdOn: new Date(parsed.createdOn),
                        };
                        this.#memoryCache.set(key, entry);
                    }
                    catch (e) {
                        this.#internalRemove(key, option);
                    }
                }
            }
        }
        if (!entry)
            return undefined;
        if (option?.hours && entry.createdOn) {
            const expiredOn = DateUtilities.addHours(entry.createdOn, option.hours);
            if (expiredOn && expiredOn < new Date()) {
                this.#internalRemove(key, option);
                return undefined;
            }
        }
        return this.#deepClone(entry.data);
    }
    #internalSet(key, data, option) {
        // FIX LỖI TẠI ĐÂY: Bỏ "?? null" để giữ nguyên kiểu T
        const clonedData = this.#deepClone(data);
        const entry = {
            data: clonedData,
            createdOn: new Date(),
        };
        this.#memoryCache.set(key, entry);
        const storage = this.#getStorage(option);
        if (storage) {
            try {
                storage.setItem(key, JSON.stringify(entry));
            }
            catch (e) {
                console.error('Storage quota exceeded', e);
            }
        }
    }
    #internalRemove(key, option) {
        this.#memoryCache.delete(key);
        const storage = this.#getStorage(option);
        if (storage) {
            storage.removeItem(key);
        }
    }
    #deepClone(val) {
        if (val === undefined || val === null)
            return val;
        return JSON.parse(JSON.stringify(val));
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SdCacheService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
    static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SdCacheService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SdCacheService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }], ctorParameters: () => [] });

const SD_CACHE_CONFIG = new InjectionToken('sd-cache.configuration');

/**
 * Generated bundle index. Do not edit.
 */

export { SD_CACHE_CONFIG, SdCacheService };
//# sourceMappingURL=sd-angular-core-services-cache.mjs.map
