/**
 * API-Client für den OSINT Lagemonitor.
 */

class ApiError extends Error {
    constructor(status, detail) {
        super(detail || `Fehler ${status}`);
        this.name = 'ApiError';
        this.status = status;
        this.detail = detail;
    }
}

const API = {
    baseUrl: '/api',

    _getHeaders() {
        const token = localStorage.getItem('osint_token');
        return {
            'Content-Type': 'application/json',
            'Authorization': token ? `Bearer ${token}` : '',
        };
    },

    async upload(path, formData) {
        const token = localStorage.getItem("osint_token");
        const headers = {};
        if (token) headers["Authorization"] = `Bearer ${token}`;
        const response = await fetch(`${this.baseUrl}${path}`, {
            method: "POST",
            headers,
            body: formData,
        });
        if (response.status === 401) {
            localStorage.removeItem("osint_token");
            localStorage.removeItem("osint_username");
            window.location.href = "/";
            return;
        }
        if (!response.ok) {
            const data = await response.json().catch(() => ({}));
            let d = data.detail;
            if (Array.isArray(d)) d = d.map(e => e.msg || JSON.stringify(e)).join("; ");
            else if (typeof d === "object" && d !== null) d = JSON.stringify(d);
            throw new Error(d || `Fehler ${response.status}`);
        }
        return response.json();
    },

    async _request(method, path, body = null, externalSignal = null) {
        const controller = new AbortController();
        const timeout = setTimeout(() => controller.abort(), 30000);

        // Externen Abort weiterleiten an internen Controller
        if (externalSignal) {
            externalSignal.addEventListener('abort', () => controller.abort(), { once: true });
        }

        const options = {
            method,
            headers: this._getHeaders(),
            signal: controller.signal,
        };
        if (body) {
            options.body = JSON.stringify(body);
        }

        let response;
        try {
            response = await fetch(`${this.baseUrl}${path}`, options);
        } catch (err) {
            clearTimeout(timeout);
            if (err.name === 'AbortError') {
                throw new Error('Zeitüberschreitung bei der Anfrage');
            }
            throw err;
        }
        clearTimeout(timeout);

        if (response.status === 401) {
            localStorage.removeItem('osint_token');
            localStorage.removeItem('osint_username');
            window.location.href = '/';
            return;
        }

        if (!response.ok) {
            const data = await response.json().catch(() => ({}));
            let detail = data.detail;
            if (Array.isArray(detail)) {
                detail = detail.map(e => e.msg || JSON.stringify(e)).join('; ');
            } else if (typeof detail === 'object' && detail !== null) {
                detail = JSON.stringify(detail);
            }

            // Lizenz-Status aus Header auslesen (vom Backend gesetzt bei 403)
            const licStatus = response.headers.get('X-License-Status');
            if (response.status === 403 && licStatus && typeof App !== 'undefined') {
                if (!App.user) App.user = {};
                App.user.read_only = true;
                App.user.read_only_reason = licStatus;
                const warningEl = document.getElementById('header-license-warning');
                if (warningEl) {
                    let text = 'Nur Lesezugriff';
                    if (licStatus === 'budget_exceeded') text = 'Credits aufgebraucht, nur Lesezugriff. Für weitere Aktualisierungen bitte die Verwaltung kontaktieren.';
                    else if (licStatus === 'expired') text = 'Lizenz abgelaufen, nur Lesezugriff';
                    else if (licStatus === 'no_license') text = 'Keine aktive Lizenz, nur Lesezugriff';
                    else if (licStatus === 'org_disabled') text = 'Organisation deaktiviert, nur Lesezugriff';
                    warningEl.textContent = text;
                    warningEl.classList.add('visible');
                }
                if (typeof App._updateRefreshButton === 'function') App._updateRefreshButton(false);
                if (typeof UI !== 'undefined' && UI.showToast) {
                    UI.showToast(detail || 'Lizenz-Beschränkung – nur Lesezugriff', 'error');
                }
            }

            throw new ApiError(response.status, detail);
        }

        if (response.status === 204) return null;
        return response.json();
    },

    // Auth
    getMe() {
        return this._request('GET', '/auth/me');
    },

    // Incidents
    listIncidents(statusFilter = null) {
        const query = statusFilter ? `?status_filter=${statusFilter}` : '';
        return this._request('GET', `/incidents${query}`);
    },

    enhanceDescription(title, description, type, signal = null) {
        return this._request('POST', '/incidents/enhance-description', { title, description, type }, signal);
    },

    createIncident(data) {
        return this._request('POST', '/incidents', data);
    },

    getRefreshingIncidents() {
        return this._request('GET', '/incidents/refreshing');
    },

    getIncident(id) {
        return this._request('GET', `/incidents/${id}`);
    },

    getIncidentSources(id) {
        return this._request('GET', `/incidents/${id}/sources`);
    },

    updateIncident(id, data) {
        return this._request('PUT', `/incidents/${id}`, data);
    },

    deleteIncident(id) {
        return this._request('DELETE', `/incidents/${id}`);
    },

    getArticles(incidentId, { limit = 500, offset = 0, search = null } = {}) {
        const params = new URLSearchParams();
        params.set('limit', String(limit));
        params.set('offset', String(offset));
        if (search) params.set('search', search);
        return this._request('GET', `/incidents/${incidentId}/articles?${params.toString()}`);
    },

    getArticlesSourcesSummary(incidentId) {
        return this._request('GET', `/incidents/${incidentId}/articles/sources-summary`);
    },

    getArticlesTimelineBuckets(incidentId, granularity = 'day') {
        return this._request('GET', `/incidents/${incidentId}/articles/timeline-buckets?granularity=${encodeURIComponent(granularity)}`);
    },

    getFactChecks(incidentId) {
        return this._request('GET', `/incidents/${incidentId}/factchecks`);
    },

    getPipeline(incidentId) {
        return this._request('GET', `/incidents/${incidentId}/pipeline`);
    },

    getSnapshots(incidentId) {
        return this._request('GET', `/incidents/${incidentId}/snapshots`);
    },

    getSnapshot(incidentId, snapshotId) {
        return this._request('GET', `/incidents/${incidentId}/snapshots/${snapshotId}`);
    },

    searchSnapshots(incidentId, query) {
        return this._request('GET', `/incidents/${incidentId}/snapshots/search?q=${encodeURIComponent(query)}`);
    },

    getLocations(incidentId) {
        return this._request('GET', `/incidents/${incidentId}/locations`);
    },

    triggerGeoparse(incidentId) {
        return this._request('POST', `/incidents/${incidentId}/geoparse`);
    },

    getGeoparseStatus(incidentId) {
        return this._request('GET', `/incidents/${incidentId}/geoparse-status`);
    },

    refreshIncident(id) {
        return this._request('POST', `/incidents/${id}/refresh`);
    },

    getRefreshLog(incidentId, limit = 20) {
        return this._request('GET', `/incidents/${incidentId}/refresh-log?limit=${limit}`);
    },

    // === Studio: Ereignis-Timeline, RAG-Chat, modulare Bausteine, Uploads, Faktencheck-Verlauf ===
    // Backend-Endpunkte folgen phasenweise; fehlende liefern vorerst 404 (studio.js faengt das ab).
    getEvents(incidentId, limit = 250) {
        return this._request('GET', `/incidents/${incidentId}/events?limit=${encodeURIComponent(limit)}`);
    },

    // RAG-Chat: inhaltliche Frage über eine konkrete Lage (Studio-UI)
    askIncident(incidentId, message, { conversation_id = null, source_filter = null, scope = 'fall' } = {}) {
        return this._request('POST', `/incidents/${incidentId}/ask`, {
            message,
            conversation_id,
            source_filter,
            scope,
        });
    },

    // Multimodaler Quellen-Ingest (Upload/URL -> Artikel)
    createUploads(incidentId, { files = [], url = null } = {}) {
        const fd = new FormData();
        (files || []).forEach(f => fd.append('files', f));
        if (url) fd.append('url', url);
        return this.upload(`/incidents/${incidentId}/uploads`, fd);
    },
    listUploads(incidentId) {
        return this._request('GET', `/incidents/${incidentId}/uploads`);
    },
    deleteUpload(incidentId, uploadId) {
        return this._request('DELETE', `/incidents/${incidentId}/uploads/${uploadId}`);
    },
    async fetchUploadBlobUrl(incidentId, uploadId) {
        const token = localStorage.getItem('osint_token');
        const headers = {};
        if (token) headers['Authorization'] = `Bearer ${token}`;
        const res = await fetch(`${this.baseUrl}/incidents/${incidentId}/uploads/${uploadId}/file`, { headers });
        if (!res.ok) throw new Error(`Datei konnte nicht geladen werden (${res.status})`);
        const blob = await res.blob();
        return URL.createObjectURL(blob);
    },

    // Modulare Pipeline-Bausteine (Studio)
    runStage(incidentId, stage) {
        return this._request('POST', `/incidents/${incidentId}/run/${stage}`);
    },
    getRunStatus(incidentId) {
        return this._request('GET', `/incidents/${incidentId}/run-status`);
    },
    // Recherche-Angebot des Fall-Chats ausfuehren (Beschreibung ergaenzen + gezielte Recherche)
    clarify(incidentId, { focus, description_addition = '' } = {}) {
        return this._request('POST', `/incidents/${incidentId}/clarify`, { focus, description_addition });
    },
    // Datenstand je Artefakt (erzeugt? wie alt? wie viele neue Artikel seither?)
    getFreshness(incidentId) {
        return this._request('GET', `/incidents/${incidentId}/freshness`);
    },
    // Kann die Websuche gerade Treffer liefern? (online via Claude-WebSearch, Stub)
    getSearchStatus() {
        return this._request('GET', '/system/search-status');
    },
    listFactcheckRuns(incidentId) {
        return this._request('GET', `/incidents/${incidentId}/factcheck-runs`);
    },
    getFactcheckRun(incidentId, runId) {
        return this._request('GET', `/incidents/${incidentId}/factcheck-runs/${runId}`);
    },

    // X-Zugänge (Studio; online-Router folgt in spaeterer Phase)
    listXAccounts() {
        return this._request('GET', '/x/accounts');
    },
    addXAccount(data) {
        return this._request('POST', '/x/accounts', data);
    },
    deleteXAccount(username) {
        return this._request('DELETE', `/x/accounts/${encodeURIComponent(username)}`);
    },

    // Sources (Quellenverwaltung)
    listSources(params = {}) {
        const query = new URLSearchParams();
        if (params.source_type) query.set('source_type', params.source_type);
        if (params.category) query.set('category', params.category);
        if (params.source_status) query.set('source_status', params.source_status);
        if (params.political_orientation) query.set('political_orientation', params.political_orientation);
        if (params.media_type) query.set('media_type', params.media_type);
        if (params.reliability) query.set('reliability', params.reliability);
        if (params.alignment) query.set('alignment', params.alignment);
        if (params.state_affiliated !== undefined && params.state_affiliated !== null) {
            query.set('state_affiliated', String(params.state_affiliated));
        }
        const qs = query.toString();
        return this._request('GET', `/sources${qs ? '?' + qs : ''}`);
    },

    createSource(data) {
        return this._request('POST', '/sources', data);
    },

    updateSource(id, data) {
        return this._request('PUT', `/sources/${id}`, data);
    },

    deleteSource(id) {
        return this._request('DELETE', `/sources/${id}`);
    },

    getSourceStats() {
        return this._request('GET', '/sources/stats');
    },

    discoverMulti(url) {
        return this._request('POST', '/sources/discover-multi', { url });
    },

    getMyExclusions() {
        return this._request('GET', '/sources/my-exclusions');
    },

    blockDomain(domain, notes) {
        return this._request('POST', '/sources/block-domain', { domain, notes });
    },

    unblockDomain(domain) {
        return this._request('POST', '/sources/unblock-domain', { domain });
    },

    deleteDomain(domain) {
        return this._request('DELETE', `/sources/domain/${encodeURIComponent(domain)}`);
    },

    cancelRefresh(id) {
        return this._request('POST', `/incidents/${id}/cancel-refresh`);
    },

    // Notifications
    listNotifications(limit = 50) {
        return this._request('GET', `/notifications?limit=${limit}`);
    },

    markNotificationsRead(ids = null) {
        return this._request('PUT', '/notifications/mark-read', { notification_ids: ids });
    },



    // Subscriptions (E-Mail-Benachrichtigungen)
    getSubscription(incidentId) {
        return this._request('GET', '/incidents/' + incidentId + '/subscription');
    },

    updateSubscription(incidentId, data) {
        return this._request('PUT', '/incidents/' + incidentId + '/subscription', data);
    },

    // Feedback
    sendFeedback(data) {
        return this._request('POST', '/feedback', data);
    },

    async sendFeedbackForm(formData) {
        const token = localStorage.getItem('osint_token');
        const controller = new AbortController();
        const timeout = setTimeout(() => controller.abort(), 60000);
        const resp = await fetch(this.baseUrl + '/feedback', {
            method: 'POST',
            headers: { 'Authorization': token ? 'Bearer ' + token : '' },
            body: formData,
            signal: controller.signal,
        });
        clearTimeout(timeout);
        if (!resp.ok) {
            const err = await resp.json().catch(() => ({}));
            throw new Error(err.detail || 'Fehler ' + resp.status);
        }
    },

    // Export

    // Tutorial-Fortschritt
    getTutorialState() {
        return this._request('GET', '/tutorial/state');
    },

    saveTutorialState(data) {
        return this._request('PUT', '/tutorial/state', data);
    },

    resetTutorialState() {
        return this._request('DELETE', '/tutorial/state');
    },
    exportReport(id, format, scope, sections, includeBranding, creator) {
        const token = localStorage.getItem('osint_token');
        let url = `${this.baseUrl}/incidents/${id}/export?format=${format}`;
        if (sections && sections.length > 0) {
            url += `&sections=${sections.join(',')}`;
        } else if (scope) {
            url += `&scope=${scope}`;
        }
        if (includeBranding === false) {
            url += `&branding=off`;
        }
        if (creator) {
            url += `&creator=${encodeURIComponent(creator)}`;
        }
        return fetch(url, {
            headers: { 'Authorization': `Bearer ${token}` },
        });
    },

    // --- Global Admin: Org-Wechsel (herausnehmbar) ---
    listOrganizations() {
        return this._request('GET', '/auth/organizations');
    },

    switchOrg(organizationId) {
        return this._request('POST', '/auth/switch-org', { organization_id: organizationId });
    },
};
