const express = require('express');
const router = express.Router();
const crypto = require('crypto');
const jwt = require('jsonwebtoken');
const User = require('../models/User');

const TENANT_ID = () => process.env.AZURE_TENANT_ID;
const CLIENT_ID = () => process.env.AZURE_CLIENT_ID;
const CLIENT_SECRET = () => process.env.AZURE_CLIENT_SECRET;
const REDIRECT_URI = () => process.env.AZURE_REDIRECT_URI;
const FRONTEND_URL = () => process.env.FRONTEND_URL || 'https://localhost';

// In-memory CSRF state storage (cleared after 10 minutes)
const pendingStates = new Map();

// In-memory result storage for addin polling (sessionId -> result, TTL 10min)
const addinResults = new Map();

// ── GET /api/auth/microsoft ────────────────────────────────────────────────
// Redirects user to Microsoft login page
router.get('/', (req, res) => {
    if (!TENANT_ID() || !CLIENT_ID()) {
        return res.redirect(`${FRONTEND_URL()}/login?error=azure_not_configured`);
    }

    const state = crypto.randomBytes(16).toString('hex');
    const redirect = req.query.redirect || null;
    pendingStates.set(state, { ts: Date.now(), redirect });
    setTimeout(() => pendingStates.delete(state), 10 * 60 * 1000);

    const params = new URLSearchParams({
        client_id: CLIENT_ID(),
        response_type: 'code',
        redirect_uri: REDIRECT_URI(),
        scope: 'openid profile email User.Read',
        state,
        response_mode: 'query',
        prompt: 'select_account',
    });

    res.redirect(`https://login.microsoftonline.com/${TENANT_ID()}/oauth2/v2.0/authorize?${params}`);
});

// ── GET /api/auth/microsoft/callback ──────────────────────────────────────
// Microsoft redirects here after login
router.get('/callback', async (req, res) => {
    const { code, state, error, error_description } = req.query;

    if (error) {
        console.error('[Azure] OAuth error:', error, error_description);
        return res.redirect(`${FRONTEND_URL()}/login?error=auth_failed`);
    }

    if (!code || !pendingStates.has(state)) {
        return res.redirect(`${FRONTEND_URL()}/login?error=invalid_state`);
    }
    const stateData = pendingStates.get(state);
    const redirectTarget = stateData?.redirect || null;
    pendingStates.delete(state);

    try {
        // 1. Exchange code for access token
        const tokenRes = await fetch(
            `https://login.microsoftonline.com/${TENANT_ID()}/oauth2/v2.0/token`,
            {
                method: 'POST',
                headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
                body: new URLSearchParams({
                    client_id: CLIENT_ID(),
                    client_secret: CLIENT_SECRET(),
                    grant_type: 'authorization_code',
                    code,
                    redirect_uri: REDIRECT_URI(),
                    scope: 'openid profile email User.Read',
                }),
            }
        );

        if (!tokenRes.ok) {
            const errText = await tokenRes.text();
            console.error('[Azure] Token exchange failed:', errText);
            return res.redirect(`${FRONTEND_URL()}/login?error=token_failed`);
        }

        const tokens = await tokenRes.json();

        // 2. Get user profile from Microsoft Graph
        const meRes = await fetch('https://graph.microsoft.com/v1.0/me', {
            headers: { Authorization: `Bearer ${tokens.access_token}` },
        });

        if (!meRes.ok) {
            console.error('[Azure] Graph /me failed');
            return res.redirect(`${FRONTEND_URL()}/login?error=graph_failed`);
        }

        const msUser = await meRes.json();
        const email = (msUser.mail || msUser.userPrincipalName || '').toLowerCase();
        const azureId = msUser.id;

        if (!email || !azureId) {
            return res.redirect(`${FRONTEND_URL()}/login?error=no_email`);
        }

        // 3. Find or create local user
        let user = User.getByAzureId(azureId);

        if (!user) {
            user = User.getByEmail(email);
            if (user) {
                // Link existing user to Azure
                User.update(user.id, { azure_id: azureId });
                user = User.getById(user.id);
            } else {
                // Create new user with 'benutzer' role
                const baseUsername = email.split('@')[0].replace(/[^a-zA-Z0-9._-]/g, '');
                let username = baseUsername;
                let counter = 1;
                while (User.getByUsername(username)) {
                    username = `${baseUsername}${counter++}`;
                }
                user = User.createFromAzure({
                    azure_id: azureId,
                    email,
                    first_name: msUser.givenName || '',
                    last_name: msUser.surname || '',
                    username,
                });
            }
        }

        if (!user.is_active) {
            return res.redirect(`${FRONTEND_URL()}/login?error=account_inactive`);
        }

        // 4. Issue our own JWT
        User.updateLastLogin(user.id);
        const token = jwt.sign(
            { id: user.id, username: user.username, email: user.email, role: user.role_name, roleId: user.role_id },
            process.env.JWT_SECRET,
            { expiresIn: process.env.JWT_EXPIRATION || '8h' }
        );

        const userPayload = encodeURIComponent(JSON.stringify({
            id: user.id,
            username: user.username,
            email: user.email,
            first_name: user.first_name,
            last_name: user.last_name,
            role_name: user.role_name,
            must_change_password: user.must_change_password || false,
        }));

        // 5. Redirect to correct target
        if (redirectTarget === 'share-tool') {
            return res.redirect(`${FRONTEND_URL()}/share-tool.html?token=${token}`);
        }
        res.redirect(`${FRONTEND_URL()}/login?token=${token}&user=${userPayload}`);

    } catch (err) {
        console.error('[Azure] Callback error:', err);
        res.redirect(`${FRONTEND_URL()}/login?error=internal_error`);
    }
});

// ── GET /api/auth/microsoft/addin ─────────────────────────────────────────
// Startet OAuth-Flow für Outlook Add-in (sessionId wird mitgegeben)
router.get('/addin', (req, res) => {
    if (!TENANT_ID() || !CLIENT_ID()) {
        return res.send('<p>Azure nicht konfiguriert.</p>');
    }

    const sessionId = req.query.sessionId || '';
    const state = crypto.randomBytes(16).toString('hex');
    // Store sessionId alongside state so callback can find it
    pendingStates.set(state, { ts: Date.now(), sessionId });
    setTimeout(() => pendingStates.delete(state), 10 * 60 * 1000);

    const params = new URLSearchParams({
        client_id: CLIENT_ID(),
        response_type: 'code',
        redirect_uri: `${FRONTEND_URL()}/api/auth/microsoft/addin-callback`,
        scope: 'openid profile email User.Read',
        state,
        response_mode: 'query',
        prompt: 'select_account',
    });

    res.redirect(`https://login.microsoftonline.com/${TENANT_ID()}/oauth2/v2.0/authorize?${params}`);
});

// ── GET /api/auth/microsoft/addin-callback ────────────────────────────────
// Callback – speichert Ergebnis für Polling, kein messageParent nötig
router.get('/addin-callback', async (req, res) => {
    const { code, state, error } = req.query;

    const storeResult = (sessionId, payload) => {
        if (sessionId) {
            addinResults.set(sessionId, payload);
            setTimeout(() => addinResults.delete(sessionId), 10 * 60 * 1000);
        }
        res.send(`<!DOCTYPE html>
<html><head><meta charset="UTF-8">
<style>body{background:#fff;color:#333;font-family:sans-serif;display:flex;flex-direction:column;align-items:center;justify-content:center;height:100vh;margin:0;gap:12px;}
p{font-size:15px;}small{color:#888;font-size:12px;}</style>
</head><body>
<p>${payload.error ? '&#10060; Anmeldung fehlgeschlagen: ' + payload.error : '&#9989; Anmeldung erfolgreich!'}</p>
<small>${payload.error ? 'Bitte schließen Sie dieses Fenster.' : 'Wird automatisch geschlossen\u2026'}</small>
</body></html>`);
    };

    const stateData = pendingStates.get(state);
    if (error || !code || !stateData) {
        const sessionId = stateData?.sessionId || '';
        pendingStates.delete(state);
        return storeResult(sessionId, { error: error || 'invalid_state' });
    }
    const sessionId = stateData.sessionId;
    pendingStates.delete(state);

    try {
        const tokenRes = await fetch(
            `https://login.microsoftonline.com/${TENANT_ID()}/oauth2/v2.0/token`,
            {
                method: 'POST',
                headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
                body: new URLSearchParams({
                    client_id: CLIENT_ID(),
                    client_secret: CLIENT_SECRET(),
                    grant_type: 'authorization_code',
                    code,
                    redirect_uri: `${FRONTEND_URL()}/api/auth/microsoft/addin-callback`,
                    scope: 'openid profile email User.Read',
                }),
            }
        );

        if (!tokenRes.ok) throw new Error('token_failed');

        const tokens = await tokenRes.json();

        const meRes = await fetch('https://graph.microsoft.com/v1.0/me', {
            headers: { Authorization: `Bearer ${tokens.access_token}` },
        });
        if (!meRes.ok) throw new Error('graph_failed');

        const msUser = await meRes.json();
        const email = (msUser.mail || msUser.userPrincipalName || '').toLowerCase();
        const azureId = msUser.id;

        if (!email || !azureId) return storeResult(sessionId, { error: 'no_email' });

        let user = User.getByAzureId(azureId) || User.getByEmail(email);
        if (!user) return storeResult(sessionId, { error: 'user_not_found' });
        if (!user.is_active) return storeResult(sessionId, { error: 'account_inactive' });

        User.updateLastLogin(user.id);
        const token = jwt.sign(
            { id: user.id, username: user.username, email: user.email, role: user.role_name, roleId: user.role_id },
            process.env.JWT_SECRET,
            { expiresIn: process.env.JWT_EXPIRATION || '8h' }
        );

        storeResult(sessionId, {
            token,
            user: {
                id: user.id,
                username: user.username,
                email: user.email,
                first_name: user.first_name,
                last_name: user.last_name,
                role_name: user.role_name,
            },
        });
    } catch (err) {
        storeResult(sessionId, { error: err.message || 'internal_error' });
    }
});

// ── GET /api/auth/microsoft/addin-result/:sessionId ───────────────────────
// Taskpane pollt diesen Endpoint bis das Login-Ergebnis vorliegt
router.get('/addin-result/:sessionId', (req, res) => {
    const { sessionId } = req.params;
    const result = addinResults.get(sessionId);
    if (!result) return res.json({ pending: true });
    addinResults.delete(sessionId);
    res.json(result);
});

module.exports = router;
