/**
 * @file ssoAuth.js
 * @description SSO-autentisering via Microsoft Entra ID (Azure AD) för Spark+ Extension
 * 
 * Author: Linus Olofsson - Iver 
 * Date: 2026-02-24
 * 
 * @purpose Hantera OAuth2 PKCE-inloggning mot Iver tenant för Spark och Teams
 * @invoked_by teamsButton.js, serviceWorker.js, popup.js
 * 
 * @setup KRAV: Registrera en App Registration i Azure AD (Entra ID)
 *   1. Gå till https://entra.microsoft.com → App registrations → New registration
 *   2. Name: "Spark+ Extension"
 *   3. Supported account types: "Accounts in this organizational directory only"
 *   4. Redirect URI: Välj "Single-page application (SPA)" och ange redirect URI:n
 *      som genereras av chrome.identity.getRedirectURL() — se konsolen vid första körning
 *   5. Under "API permissions", lägg till:
 *      - Microsoft Graph → Delegated → User.Read (för profil)
 *      - Microsoft Graph → Delegated → Presence.Read (för Teams-status, valfritt)
 *   6. Kopiera Application (client) ID och sätt in nedan som SSO_CLIENT_ID
 */

// ===== SSO KONFIGURATION =====
// Iver tenant ID (samma för Spark och Teams)
const SSO_TENANT_ID = '';

// Application (client) ID från Azure AD App Registration
// TODO: Ersätt med din registrerade App Registration Client ID
const SSO_CLIENT_ID = 'REPLACE_WITH_YOUR_CLIENT_ID';

// OAuth2 endpoints för Iver tenant
const SSO_AUTHORITY = `https://login.microsoftonline.com/${SSO_TENANT_ID}`;
const SSO_AUTH_ENDPOINT = `${SSO_AUTHORITY}/oauth2/v2.0/authorize`;
const SSO_TOKEN_ENDPOINT = `${SSO_AUTHORITY}/oauth2/v2.0/token`;

// Microsoft Graph endpoint
const GRAPH_API_BASE = 'https://graph.microsoft.com/v1.0';

// Scopes för Spark och Teams
const SSO_SCOPES = {
    // Grundläggande profil-scope (alltid inkluderad)
    profile: 'openid profile email User.Read',
    // Teams-specifika scopes
    teams: 'openid profile email User.Read Presence.Read',
    // Spark API-scope (lägg till om Spark API kräver specifikt scope)
    spark: 'openid profile email User.Read'
};

// ===== PKCE HELPERS =====

/**
 * Generera en kryptografiskt säker random string för PKCE code_verifier
 */
function generateCodeVerifier() {
    const array = new Uint8Array(32);
    crypto.getRandomValues(array);
    return base64UrlEncode(array);
}

/**
 * Skapa code_challenge från code_verifier med SHA-256
 */
async function generateCodeChallenge(codeVerifier) {
    const encoder = new TextEncoder();
    const data = encoder.encode(codeVerifier);
    const digest = await crypto.subtle.digest('SHA-256', data);
    return base64UrlEncode(new Uint8Array(digest));
}

/**
 * Base64 URL-safe encoding (utan padding)
 */
function base64UrlEncode(buffer) {
    const base64 = btoa(String.fromCharCode(...buffer));
    return base64
        .replace(/\+/g, '-')
        .replace(/\//g, '_')
        .replace(/=+$/, '');
}

// ===== AUTH FLOW =====

/**
 * Starta SSO-inloggning via OAuth2 PKCE
 * Använder chrome.identity.launchWebAuthFlow för att öppna Microsoft login
 * 
 * @param {string} scopeType - 'profile', 'teams', eller 'spark'
 * @returns {Object} - { accessToken, idToken, expiresAt, userProfile }
 */
async function loginWithSSO(scopeType = 'profile') {
    console.log(`Starting SSO login for scope: ${scopeType}`);
    
    // Kontrollera att Client ID är konfigurerat
    if (SSO_CLIENT_ID === 'REPLACE_WITH_YOUR_CLIENT_ID') {
        throw new Error(
            'SSO Client ID är inte konfigurerat. ' +
            'Registrera en App Registration i Azure AD och uppdatera SSO_CLIENT_ID i ssoAuth.js. ' +
            'Se instruktioner i filens header-kommentar.'
        );
    }
    
    // Hämta redirect URL från Chrome extension
    const redirectUrl = chrome.identity.getRedirectURL();
    console.log('Chrome extension redirect URL:', redirectUrl);
    console.log('(Registrera denna URL som Redirect URI i din Azure AD App Registration)');
    
    // Generera PKCE-parametrar
    const codeVerifier = generateCodeVerifier();
    const codeChallenge = await generateCodeChallenge(codeVerifier);
    const state = generateCodeVerifier(); // Random state för CSRF-skydd
    
    // Välj scopes
    const scopes = SSO_SCOPES[scopeType] || SSO_SCOPES.profile;
    
    // Bygg authorization URL
    const authParams = new URLSearchParams({
        client_id: SSO_CLIENT_ID,
        response_type: 'code',
        redirect_uri: redirectUrl,
        scope: scopes,
        state: state,
        code_challenge: codeChallenge,
        code_challenge_method: 'S256',
        prompt: 'select_account', // Låt användaren välja konto
        // login_hint kan sättas om vi redan vet e-postadressen
    });
    
    const authUrl = `${SSO_AUTH_ENDPOINT}?${authParams.toString()}`;
    console.log('Launching auth flow...');
    
    try {
        // Öppna Microsoft login i en popup
        const responseUrl = await chrome.identity.launchWebAuthFlow({
            url: authUrl,
            interactive: true
        });
        
        console.log('Auth flow completed, processing response...');
        
        // Extrahera authorization code från redirect URL
        const url = new URL(responseUrl);
        const code = url.searchParams.get('code');
        const returnedState = url.searchParams.get('state');
        const error = url.searchParams.get('error');
        const errorDescription = url.searchParams.get('error_description');
        
        if (error) {
            throw new Error(`SSO-fel: ${error} - ${errorDescription}`);
        }
        
        if (returnedState !== state) {
            throw new Error('SSO-fel: State mismatch - möjlig CSRF-attack');
        }
        
        if (!code) {
            throw new Error('SSO-fel: Ingen authorization code mottagen');
        }
        
        // Byt authorization code mot tokens
        const tokens = await exchangeCodeForTokens(code, codeVerifier, redirectUrl);
        
        // Hämta användarprofil från Microsoft Graph
        const userProfile = await fetchUserProfile(tokens.access_token);
        
        // Spara tokens och profil i storage
        const authData = {
            accessToken: tokens.access_token,
            idToken: tokens.id_token || null,
            refreshToken: tokens.refresh_token || null,
            expiresAt: Date.now() + (tokens.expires_in * 1000),
            tokenType: tokens.token_type,
            scope: tokens.scope,
            scopeType: scopeType,
            userProfile: userProfile,
            loginTime: Date.now()
        };
        
        await saveAuthData(authData);
        
        console.log('SSO login successful for user:', userProfile.displayName);
        
        return authData;
        
    } catch (error) {
        console.error('SSO login failed:', error);
        
        // Om användaren stänger login-fönstret
        if (error.message?.includes('User interaction required') || 
            error.message?.includes('The user did not approve')) {
            throw new Error('Inloggningen avbröts av användaren.');
        }
        
        throw error;
    }
}

/**
 * Byt authorization code mot access token och refresh token
 */
async function exchangeCodeForTokens(code, codeVerifier, redirectUri) {
    console.log('Exchanging authorization code for tokens...');
    
    const tokenParams = new URLSearchParams({
        client_id: SSO_CLIENT_ID,
        grant_type: 'authorization_code',
        code: code,
        redirect_uri: redirectUri,
        code_verifier: codeVerifier
    });
    
    const response = await fetch(SSO_TOKEN_ENDPOINT, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded'
        },
        body: tokenParams.toString()
    });
    
    if (!response.ok) {
        const errorData = await response.json().catch(() => ({}));
        throw new Error(
            `Token exchange failed: ${errorData.error || response.status} - ${errorData.error_description || response.statusText}`
        );
    }
    
    const tokens = await response.json();
    console.log('Token exchange successful, expires in:', tokens.expires_in, 'seconds');
    
    return tokens;
}

/**
 * Hämta användarprofil från Microsoft Graph
 */
async function fetchUserProfile(accessToken) {
    console.log('Fetching user profile from Microsoft Graph...');
    
    const response = await fetch(`${GRAPH_API_BASE}/me`, {
        headers: {
            'Authorization': `Bearer ${accessToken}`,
            'Content-Type': 'application/json'
        }
    });
    
    if (!response.ok) {
        throw new Error(`Failed to fetch user profile: ${response.status} ${response.statusText}`);
    }
    
    const profile = await response.json();
    
    console.log('User profile fetched:', {
        displayName: profile.displayName,
        mail: profile.mail || profile.userPrincipalName,
        id: profile.id
    });
    
    return {
        id: profile.id,
        displayName: profile.displayName,
        mail: profile.mail || profile.userPrincipalName,
        givenName: profile.givenName,
        surname: profile.surname,
        jobTitle: profile.jobTitle,
        officeLocation: profile.officeLocation,
        userPrincipalName: profile.userPrincipalName
    };
}

/**
 * Hämta Teams-presence (online-status) för inloggad användare
 */
async function fetchTeamsPresence(accessToken) {
    console.log('Fetching Teams presence...');
    
    try {
        const response = await fetch(`${GRAPH_API_BASE}/me/presence`, {
            headers: {
                'Authorization': `Bearer ${accessToken}`,
                'Content-Type': 'application/json'
            }
        });
        
        if (!response.ok) {
            console.warn('Could not fetch Teams presence:', response.status);
            return null;
        }
        
        const presence = await response.json();
        console.log('Teams presence:', presence.availability, presence.activity);
        
        return {
            availability: presence.availability,  // Available, Busy, Away, etc.
            activity: presence.activity            // InACall, InAMeeting, etc.
        };
    } catch (error) {
        console.error('Error fetching Teams presence:', error);
        return null;
    }
}

// ===== TOKEN MANAGEMENT =====

/**
 * Spara auth-data i chrome.storage
 */
async function saveAuthData(authData) {
    await chrome.storage.local.set({ ssoAuthData: authData });
    console.log('Auth data saved to storage');
}

/**
 * Hämta sparad auth-data från chrome.storage
 */
async function getAuthData() {
    const result = await chrome.storage.local.get('ssoAuthData');
    return result.ssoAuthData || null;
}

/**
 * Kontrollera om användaren är inloggad och token fortfarande giltig
 */
async function isAuthenticated() {
    const authData = await getAuthData();
    
    if (!authData || !authData.accessToken) {
        return false;
    }
    
    // Kontrollera om token har gått ut (med 5 min marginal)
    const bufferMs = 5 * 60 * 1000;
    if (authData.expiresAt && (Date.now() + bufferMs) >= authData.expiresAt) {
        console.log('Access token expired or about to expire');
        return false;
    }
    
    return true;
}

/**
 * Hämta giltig access token (logga in om nödvändigt)
 */
async function getValidAccessToken(scopeType = 'profile') {
    const authData = await getAuthData();
    
    if (authData && authData.accessToken) {
        // Kontrollera om token fortfarande är giltig
        const bufferMs = 5 * 60 * 1000;
        if (authData.expiresAt && (Date.now() + bufferMs) < authData.expiresAt) {
            return authData.accessToken;
        }
        
        console.log('Token expired, need to re-authenticate');
    }
    
    // Ingen giltig token, kräv ny inloggning
    const newAuth = await loginWithSSO(scopeType);
    return newAuth.accessToken;
}

/**
 * Hämta inloggad användares profil
 */
async function getCurrentUser() {
    const authData = await getAuthData();
    return authData?.userProfile || null;
}

/**
 * Logga ut - rensa all auth-data
 */
async function logout() {
    console.log('Logging out...');
    
    await chrome.storage.local.remove('ssoAuthData');
    
    console.log('Auth data cleared, user logged out');
    
    return true;
}

/**
 * Hämta redirect URL (för att visa i UI vid konfiguration)
 */
function getRedirectUrl() {
    return chrome.identity.getRedirectURL();
}

// ===== EXPORTS =====
export {
    loginWithSSO,
    logout,
    isAuthenticated,
    getValidAccessToken,
    getCurrentUser,
    getAuthData,
    fetchTeamsPresence,
    fetchUserProfile,
    getRedirectUrl,
    SSO_TENANT_ID,
    SSO_CLIENT_ID,
    SSO_SCOPES
};
