/**
 * Reusable data cache with fetch deduplication, invalidation, and subscribers.
 */

import { fetchWithAuth } from './api.js';

export class DataCache {
    /**
     * @param {Object} opts
     * @param {string} opts.endpoint - API path (e.g. '/picture-sources')
     * @param {function} opts.extractData - Extract array from response JSON
     * @param {*} [opts.defaultValue=[]] - Initial/fallback value
     */
    constructor({ endpoint, extractData, defaultValue = [] }) {
        this._endpoint = endpoint;
        this._extractData = extractData;
        this._defaultValue = defaultValue;
        this._data = defaultValue;
        this._loading = false;
        this._promise = null;
        this._subscribers = [];
        this._fresh = false;  // true after first successful fetch, cleared on invalidate
    }

    get data() { return this._data; }
    get loading() { return this._loading; }

    /**
     * Fetch from API. Deduplicates concurrent calls.
     * Returns cached data immediately if already fetched and not invalidated.
     * @param {Object} [opts]
     * @param {boolean} [opts.force=false] - Force re-fetch even if cache is fresh
     */
    async fetch({ force = false } = {}) {
        if (!force && this._fresh) return this._data;
        if (this._promise) return this._promise;
        this._loading = true;
        this._promise = this._doFetch();
        try {
            return await this._promise;
        } finally {
            this._promise = null;
            this._loading = false;
        }
    }

    async _doFetch() {
        try {
            const resp = await fetchWithAuth(this._endpoint);
            if (!resp.ok) {
                console.error(`[DataCache] ${this._endpoint}: HTTP ${resp.status}`);
                return this._data;
            }
            const json = await resp.json();
            this._data = this._extractData(json);
            this._fresh = true;
            this._notify();
            return this._data;
        } catch (err) {
            if (err.isAuth) return this._data;
            console.error(`Cache fetch ${this._endpoint}:`, err);
            return this._data;
        }
    }

    /** Mark cache as stale; next fetch() will re-request. */
    invalidate() {
        this._fresh = false;
    }

    /** Manually set cache value (e.g. after a create/update call). */
    update(value) {
        this._data = value;
        this._fresh = true;
        this._notify();
    }

    subscribe(fn) { this._subscribers.push(fn); }
    unsubscribe(fn) { this._subscribers = this._subscribers.filter(f => f !== fn); }

    _notify() {
        for (const fn of this._subscribers) fn(this._data);
    }
}
