const axios = require('axios');
const jwt = require('jsonwebtoken');
const crypto = require('crypto');
const db = require('../db');
const { sendVerificationEmail } = require('../utils/emailService');
const xpService = require('../utils/xpService');
const authController = require('./authController'); // We might reuse some logic if needed
const { isDisposableEmail } = require('../utils/disposableEmailChecker');

// Helper: Get base URLs without trailing slashes
const getBaseUrls = () => {
    const api = (process.env.API_URL || 'http://localhost:5001').replace(/\/$/, '');
    const client = (process.env.CLIENT_ORIGIN || 'http://localhost:5173').replace(/\/$/, '');
    return { api, client };
};

// Helper to get OAuth settings from DB
const getOAuthSettings = async () => {
    const keys = [
        'google_enabled', 'google_client_id', 'google_client_secret',
        'linkedin_enabled', 'linkedin_client_id', 'linkedin_client_secret',
        'oauth_redirect_base'
    ];
    
    // Construct placeholder query
    const placeholders = keys.map(() => '?').join(',');
    const result = await db.query(
        `SELECT \`key\`, value FROM system_settings WHERE \`key\` IN (${placeholders})`,
        keys
    );

    const settings = {};
    result.rows.forEach(row => {
        let val = row.value;
        try {
            if (val.startsWith('"')) val = JSON.parse(val);
            if (val === 'true') val = true;
            if (val === 'false') val = false;
        } catch (e) {}
        settings[row.key] = val;
    });
    return settings;
};

// Helper: Handle User Login/Registration from OAuth Profile
const handleOAuthUser = async (req, res, profile, provider, customRedirect = null, platform = 'web') => {
    const { client } = getBaseUrls();
    const { email, name, surname, id: providerId } = profile;

    if (!email) {
        return res.redirect(`${client}/login?error=No email provided by ${provider}`);
    }

    try {
        // 1. Check if user exists by email
        const userResult = await db.query('SELECT * FROM users WHERE email = ?', [email]);
        let user = userResult.rows[0];
        let isNewUser = false;

        if (user) {
            // User exists.
            // If they registered via password, we can just log them in (account linking implied by email trust)
            // Ideally we should record that they also linked this provider, but for MVP we just log them in.
        } else {
            // Block disposable emails for new users
            if (isDisposableEmail(email)) {
                console.log(`[SECURITY] Blocked disposable email via OAuth (${provider}): ${email}`);
                return res.redirect(`${client}/login?error=Temporary email addresses are not allowed. Please use a permanent email.`);
            }

            // Create User
            isNewUser = true;
            const username = email.split('@')[0] + crypto.randomBytes(3).toString('hex');
            const placeholderPassword = crypto.randomBytes(16).toString('hex'); // Unusable password
            const verificationToken = null; // Verified by OAuth provider
            
            // Insert
            const insertResult = await db.query(
                `INSERT INTO users (name, surname, email, username, password, role, is_verified, subscription_plan, registration_source, created_at) 
                 VALUES (?, ?, ?, ?, ?, 'normal', 1, 'free', ?, NOW())`,
                [name, surname || '', email, username, placeholderPassword, platform]
            );
            const newUserId = insertResult.insertId;
            
            // Fetch newly created user
            const newUserResult = await db.query('SELECT * FROM users WHERE id = ?', [newUserId]);
            user = newUserResult.rows[0];

            // Auto-enroll in default path (similar to register)
            try {
                await db.query('INSERT INTO user_path_progress (user_id, learning_path_id, percentage_completed) VALUES (?, 9, 0)', [user.id]);
            } catch (e) {}
        }

        // 2. Generate Session & Token (Copying logic from authController.login)
        const sessionToken = crypto.randomBytes(32).toString('hex');
         // Store session token in database based on platform
         if (platform === 'mobile' || platform === 'ios' || platform === 'android') {
            await db.query('UPDATE users SET mobile_session_token = ? WHERE id = ?', [sessionToken, user.id]);
        } else {
            await db.query('UPDATE users SET session_token = ? WHERE id = ?', [sessionToken, user.id]);
        }

        const tokenPayload = { 
            id: user.id, 
            sessionToken,
            type: platform
        };

        const token = jwt.sign(tokenPayload, process.env.JWT_SECRET, {
            expiresIn: '720h', 
        });

        // 3. Redirect to Frontend
        // We pass the token in the URL fragment or query param so frontend can catch it
        // Ideally use a temporary one-time code, but for simplicity/directness we use the token.
        // SECURITY NOTE: In production, fragments (#) are safer than query params (?) for tokens to avoid server logging.
        // But frontend 'Login.jsx' logic handles query params: `?token=...`
        if (customRedirect) {
            const separator = customRedirect.includes('?') ? '&' : '?';
            return res.redirect(`${customRedirect}${separator}token=${token}`);
        }
        return res.redirect(`${client}/login?token=${token}`);

    } catch (error) {
        console.error('OAuth User Handling Error:', error);
        return res.redirect(`${client}/login?error=Authentication failed`);
    }
};

// --- GOOGLE ---

exports.googleAuth = async (req, res) => {
    try {
        const settings = await getOAuthSettings();
        if (!settings.google_enabled || !settings.google_client_id) {
            return res.status(400).send('Google Auth is not enabled or configured.');
        }

        const { api } = getBaseUrls();
        const redirectUri = `${api}/api/auth/google/callback`; // Server-side callback
        const scope = 'https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email';

        // Encode state for mobile redirect
        const stateObj = { 
            redirect_to: req.query.redirect_to || null, 
            platform: req.query.platform || 'web' 
        };
        const state = Buffer.from(JSON.stringify(stateObj)).toString('base64');
        
        const url = `https://accounts.google.com/o/oauth2/v2/auth?client_id=${settings.google_client_id}&redirect_uri=${encodeURIComponent(redirectUri)}&response_type=code&scope=${encodeURIComponent(scope)}&access_type=offline&prompt=consent&state=${state}`;

        res.redirect(url);
    } catch (error) {
        console.error(error);
        res.status(500).send('Server error initializing Google Auth');
    }
};

exports.googleCallback = async (req, res) => {
    const { code, state } = req.query;
    const { client, api } = getBaseUrls();
    if (!code) return res.redirect(`${client}/login?error=No code provided`);

    let customRedirect = null;
    let platform = 'web';

    if (state) {
        try {
            const decoded = JSON.parse(Buffer.from(state, 'base64').toString('utf-8'));
            if (decoded.redirect_to) customRedirect = decoded.redirect_to;
            if (decoded.platform) platform = decoded.platform;
        } catch (e) {
            console.error('Failed to decode state:', e);
        }
    }

    try {
        const settings = await getOAuthSettings();
        const redirectUri = `${api}/api/auth/google/callback`;

        // Exchange code for tokens
        const tokenResponse = await axios.post('https://oauth2.googleapis.com/token', {
            client_id: settings.google_client_id,
            client_secret: settings.google_client_secret,
            code,
            grant_type: 'authorization_code',
            redirect_uri: redirectUri
        });

        const { access_token } = tokenResponse.data;

        // Get User Profile
        const profileResponse = await axios.get('https://www.googleapis.com/oauth2/v2/userinfo', {
            headers: { Authorization: `Bearer ${access_token}` }
        });

        const profile = {
            id: profileResponse.data.id,
            email: profileResponse.data.email,
            name: profileResponse.data.given_name,
            surname: profileResponse.data.family_name
        };

        return handleOAuthUser(req, res, profile, 'Google', customRedirect, platform);

    } catch (error) {
        console.error('Google Callback Error:', error.response?.data || error.message);
        return res.redirect(`${client}/login?error=Google authentication failed`);
    }
};

// --- LINKEDIN ---

exports.linkedinAuth = async (req, res) => {
    try {
        const settings = await getOAuthSettings();
        if (!settings.linkedin_enabled || !settings.linkedin_client_id) {
            return res.status(400).send('LinkedIn Auth is not enabled or configured.');
        }

        const { api } = getBaseUrls();
        const redirectUri = `${api}/api/auth/linkedin/callback`;
        
        // Enhance state with redirect_to
        const stateObj = { 
            csrf: crypto.randomBytes(16).toString('hex'),
            redirect_to: req.query.redirect_to || null,
            platform: req.query.platform || 'web'
        };
        const state = Buffer.from(JSON.stringify(stateObj)).toString('base64');

        const scope = 'openid profile email'; 
        // Note: New LinkedIn v2 API uses OIDC scopes. "r_liteprofile r_emailaddress" is legacy. "openid profile email" is current.

        const url = `https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_id=${settings.linkedin_client_id}&redirect_uri=${encodeURIComponent(redirectUri)}&state=${state}&scope=${encodeURIComponent(scope)}`;

        res.redirect(url);
    } catch (error) {
        console.error(error);
        res.status(500).send('Server error initializing LinkedIn Auth');
    }
};

exports.linkedinCallback = async (req, res) => {
    const { code, state } = req.query;
    // const { state } = req.query; // Should verify state

    const { client, api } = getBaseUrls();

    if (!code) return res.redirect(`${client}/login?error=No code provided`);

    let customRedirect = null;
    let platform = 'web';

    if (state) {
        try {
            const decoded = JSON.parse(Buffer.from(state, 'base64').toString('utf-8'));
            if (decoded.redirect_to) customRedirect = decoded.redirect_to;
            if (decoded.platform) platform = decoded.platform;
        } catch (e) { console.error('LinkedIn State Decode Error', e); }
    }

    try {
        const settings = await getOAuthSettings();
        const redirectUri = `${api}/api/auth/linkedin/callback`;

        // Exchange code for access token
        const params = new URLSearchParams();
        params.append('grant_type', 'authorization_code');
        params.append('code', code);
        params.append('redirect_uri', redirectUri);
        params.append('client_id', settings.linkedin_client_id);
        params.append('client_secret', settings.linkedin_client_secret);

        const tokenResponse = await axios.post('https://www.linkedin.com/oauth/v2/accessToken', params, {
            headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
        });

        const { access_token } = tokenResponse.data;

        // Get User Profile (OpenID Connect)
        // LinkedIn UserInfo Endpoint for OIDC
        const profileResponse = await axios.get('https://api.linkedin.com/v2/userinfo', {
            headers: { Authorization: `Bearer ${access_token}` }
        });

        // Map LinkedIn profile to our structure
        const profile = {
            id: profileResponse.data.sub,
            email: profileResponse.data.email,
            name: profileResponse.data.given_name,
            surname: profileResponse.data.family_name
        };

        return handleOAuthUser(req, res, profile, 'LinkedIn', customRedirect, platform);

    } catch (error) {
        console.error('LinkedIn Callback Error:', error.response?.data || error.message);
        return res.redirect(`${client}/login?error=LinkedIn authentication failed`);
    }
};
