/**
 * @file auth.js
 * @description Autentiseringshantering för Spark API via token från spark.iver.com
 * 
 * Author: Linus Olofsson - Iver 
 * Date: 2026-02-18
 * Last modified: Linus Olofsson (2026-02-24) - Spark localStorage token extraction
 * 
 * @purpose Hantera Spark Bearer token: hämta från localStorage, auto-refresh, API-requests
 * @invoked_by serviceWorker.js, ticketApi.js
 * @note Token hämtas från spark.iver.com localStorage via content script — INGA nya flikar öppnas
 */

// Spark API-konfiguration
const SPARK_API_BASE = 'https://spark.iver.com/api';

// Token-cache i minnet (service worker)
let cachedToken = null;
let cachedExpiresAt = null;

/**
 * Hämta Spark access_token från en redan öppen spark.iver.com-flik.
 * Använder content script (sparkTokenReader.js) för att läsa localStorage.
 * Om ingen flik är öppen, försöker programmatisk injection via scripting API.
 * ÖPPNAR ALDRIG nya flikar.
 */
async function getSparkToken() {
    // 1. Kolla om vi har en giltig cachad token
    if (cachedToken && cachedExpiresAt) {
        const bufferMs = 2 * 60 * 1000; // 2 min marginal
        if (Date.now() + bufferMs < cachedExpiresAt) {
            return cachedToken;
        }
        console.log('Cached Spark token expired or about to expire, refreshing...');
    }

    // 2. Kolla chrome.storage (kan ha sparats av content script)
    const stored = await chrome.storage.local.get(['sparkAccessToken', 'sparkTokenExpiresAt']);
    if (stored.sparkAccessToken && stored.sparkTokenExpiresAt) {
        const bufferMs = 2 * 60 * 1000;
        if (Date.now() + bufferMs < stored.sparkTokenExpiresAt) {
            cachedToken = stored.sparkAccessToken;
            cachedExpiresAt = stored.sparkTokenExpiresAt;
            console.log('Using Spark token from storage, expires at:', new Date(cachedExpiresAt).toLocaleTimeString());
            return cachedToken;
        }
        console.log('Stored Spark token expired, trying to get fresh one...');
    }

    // 3. Hitta en öppen spark.iver.com-flik och läs token via content script
    const token = await extractTokenFromSparkTab();
    
    if (token) {
        return token;
    }

    // 4. Ingen token tillgänglig
    throw new Error(
        'SPARK_TOKEN_MISSING: Ingen giltig Spark-token hittades. ' +
        'Öppna spark.iver.com och logga in, sedan försök igen.'
    );
}

/**
 * Extrahera token från en öppen spark.iver.com-flik.
 * Försöker först via content script message, sedan via scripting.executeScript.
 * ÖPPNAR ALDRIG nya flikar.
 */
async function extractTokenFromSparkTab() {
    try {
        // Hitta alla öppna spark.iver.com-flikar
        const tabs = await chrome.tabs.query({ url: 'https://spark.iver.com/*' });
        
        if (!tabs || tabs.length === 0) {
            console.warn('No spark.iver.com tabs found');
            return null;
        }

        const sparkTab = tabs[0];
        console.log(`Found Spark tab: ${sparkTab.id} - ${sparkTab.url}`);

        // Försök 1: Skicka meddelande till content script
        try {
            const response = await chrome.tabs.sendMessage(sparkTab.id, { action: 'getSparkToken' });
            
            console.log('Content script response:', JSON.stringify({
                success: response?.success,
                hasToken: !!response?.accessToken,
                tokenPreview: response?.accessToken?.substring(0, 20),
                expiresAt: response?.expiresAt,
                source: response?.source,
                allKeys: response?.allKeys?.map(k => k.key || k)
            }));

            if (response?.success && response.accessToken) {
                await saveSparkToken(response.accessToken, response.expiresAt, response.userInfo);
                return cachedToken;
            } else if (response?.allKeys) {
                console.warn('Token not found! localStorage keys on spark.iver.com:', 
                    response.allKeys.map(k => `${k.key} (${k.valueLength} chars)`).join(', '));
            }
        } catch (msgError) {
            console.log('Content script not responding:', msgError.message, '- trying scripting.executeScript...');
        }

        // Försök 2: Injicera script programmatiskt (ingen ny flik)
        try {
            const results = await chrome.scripting.executeScript({
                target: { tabId: sparkTab.id },
                func: () => {
                    // Sök direkt nyckel
                    const direct = localStorage.getItem('access_token');
                    if (direct) {
                        return {
                            accessToken: direct,
                            expiresAt: localStorage.getItem('expires_at'),
                            source: 'direct'
                        };
                    }
                    // Sök OIDC Client-format
                    for (let i = 0; i < localStorage.length; i++) {
                        const key = localStorage.key(i);
                        if (key && key.startsWith('oidc.user:')) {
                            try {
                                const data = JSON.parse(localStorage.getItem(key));
                                if (data && data.access_token) {
                                    return {
                                        accessToken: data.access_token,
                                        expiresAt: data.expires_at ? String(data.expires_at * 1000) : null,
                                        source: 'oidc.user',
                                        key: key
                                    };
                                }
                            } catch (e) { /* ignore */ }
                        }
                    }
                    // Sök alla nycklar med JWT-värde
                    for (let i = 0; i < localStorage.length; i++) {
                        const key = localStorage.key(i);
                        const val = localStorage.getItem(key);
                        if (val && val.startsWith('eyJ')) {
                            return { accessToken: val, expiresAt: null, source: 'jwt-search', key: key };
                        }
                    }
                    // Dumpa nycklar för debugging
                    const keys = [];
                    for (let i = 0; i < localStorage.length; i++) {
                        const k = localStorage.key(i);
                        keys.push(k);
                    }
                    return { accessToken: null, source: 'not_found', allKeys: keys };
                }
            });

            const data = results?.[0]?.result;
            console.log('scripting.executeScript result:', data?.source, data?.key || '', 
                data?.accessToken ? '(token found)' : '(no token)', 
                data?.allKeys ? 'keys: ' + data.allKeys.join(', ') : '');

            if (data?.accessToken) {
                const expiresAt = data.expiresAt ? parseInt(data.expiresAt, 10) : null;
                await saveSparkToken(data.accessToken, expiresAt, null);
                return cachedToken;
            }
        } catch (scriptError) {
            console.error('scripting.executeScript failed:', scriptError);
        }

        return null;
    } catch (error) {
        console.error('Error extracting token from Spark tab:', error);
        return null;
    }
}

/**
 * Spara token i cache och chrome.storage
 */
async function saveSparkToken(accessToken, expiresAt, userInfo) {
    cachedToken = accessToken;
    cachedExpiresAt = expiresAt || (Date.now() + 3600 * 1000); // Default 1h

    const storageData = {
        sparkAccessToken: accessToken,
        sparkTokenExpiresAt: cachedExpiresAt
    };

    if (userInfo) {
        storageData.sparkUserInfo = userInfo;
    }

    await chrome.storage.local.set(storageData);
    console.log('Spark token saved, expires at:', new Date(cachedExpiresAt).toLocaleTimeString());
}

/**
 * Avkoda JWT-payload utan att verifiera signaturen
 */
function parseJwt(token) {
    try {
        const base64Url = token.split('.')[1];
        const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
        const jsonPayload = decodeURIComponent(
            atob(base64).split('').map(c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)).join('')
        );
        return JSON.parse(jsonPayload);
    } catch (e) {
        console.error('Failed to parse JWT:', e);
        return null;
    }
}

/**
 * Hämta inloggad användares ID från JWT-tokenet (sub-claim).
 * Returnerar user-ID som sträng, eller null om inget token finns.
 */
async function getLoggedInUserId() {
    // Kolla om vi redan cachat user-ID
    const stored = await chrome.storage.local.get(['sparkUserId']);
    if (stored.sparkUserId) {
        return stored.sparkUserId;
    }

    const token = await getSparkToken();
    if (!token) return null;

    const payload = parseJwt(token);
    if (!payload?.sub) {
        console.warn('JWT token has no sub claim');
        return null;
    }

    const userId = payload.sub;
    await chrome.storage.local.set({ sparkUserId: userId });
    console.log('Logged in user ID:', userId);
    return userId;
}

/**
 * Rensa cachad token (t.ex. vid 401)
 */
async function clearSparkToken() {
    cachedToken = null;
    cachedExpiresAt = null;
    await chrome.storage.local.remove(['sparkAccessToken', 'sparkTokenExpiresAt', 'sparkUserInfo', 'sparkUserId', 'sparkUserTitle', 'sparkUserDisplayName']);
    console.log('Spark token cleared');
}

/**
 * Kontrollera om vi har en giltig Spark-token
 */
async function hasValidSparkToken() {
    try {
        await getSparkToken();
        return true;
    } catch {
        return false;
    }
}

/**
 * Skapa auth headers med Spark token
 */
function getAuthHeaders(token) {
    return {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json',
        'Accept': 'application/json, text/plain, */*'
    };
}

/**
 * Gör ett autentiserat API-anrop mot Spark.
 * Hämtar token automatiskt, retry vid 401.
 * ÖPPNAR ALDRIG nya flikar.
 */
async function makeAuthenticatedRequest(url, options = {}) {
    let token = await getSparkToken();
    
    const headers = {
        ...getAuthHeaders(token),
        ...(options.headers || {})
    };
    
    const response = await fetch(url, {
        ...options,
        headers,
        credentials: 'include'
    });
    
    // Vid 401: rensa token och försök en gång till
    if (response.status === 401) {
        console.warn('Spark API returned 401, clearing token and retrying...');
        await clearSparkToken();
        
        // Försök hämta ny token
        try {
            token = await getSparkToken();
        } catch {
            throw new Error('Spark-token utgången. Öppna spark.iver.com och logga in igen.');
        }
        
        const retryHeaders = {
            ...getAuthHeaders(token),
            ...(options.headers || {})
        };
        
        const retryResponse = await fetch(url, {
            ...options,
            headers: retryHeaders,
            credentials: 'include'
        });
        
        if (!retryResponse.ok) {
            throw new Error(`HTTP ${retryResponse.status}: ${retryResponse.statusText} (efter token-refresh)`);
        }
        
        if (retryResponse.status === 204) return null;
        return await retryResponse.json();
    }
    
    if (!response.ok) {
        if (response.status === 460) {
            const errorData = await response.json().catch(() => ({}));
            throw new Error(`API-fel: ${errorData.errorCodes?.join(', ') || 'Okänt fel'}`);
        }
        throw new Error(`HTTP ${response.status}: ${response.statusText}`);
    }
    
    if (response.status === 204) return null;
    return await response.json();
}

export {
    getSparkToken,
    clearSparkToken,
    hasValidSparkToken,
    saveSparkToken,
    getAuthHeaders,
    makeAuthenticatedRequest,
    getLoggedInUserId,
    SPARK_API_BASE
};
