// Authentication routes

import { Router } from 'express';
import { authConfig } from '../config/auth.js';
import oidc from '../utils/oidc.js';

const router = Router();

/**
 * GET /auth/login
 * Redirect to OIDC provider for authentication
 */
router.get('/login', async (req, res) => {
  if (!authConfig.app.authEnabled) {
    return res.redirect(authConfig.app.frontendUrl);
  }

  try {
    // Generate PKCE and state values
    const state = oidc.generateState();
    const nonce = oidc.generateNonce();
    const codeVerifier = oidc.generateCodeVerifier();

    // Store in session for validation
    req.session.authState = state;
    req.session.authNonce = nonce;
    req.session.codeVerifier = codeVerifier;

    // Store return URL if provided
    const returnTo = req.query.returnTo || '/';
    req.session.returnTo = returnTo;

    // Get authorization URL
    const authUrl = oidc.getAuthorizationUrl(state, nonce, codeVerifier);

    // Explicitly save session before redirect (required for async stores like Redis)
    await new Promise((resolve, reject) => {
      req.session.save((err) => {
        if (err) reject(err);
        else resolve();
      });
    });

    console.log(`[Auth] Login initiated, session ID: ${req.session.id}, state: ${state.substring(0, 10)}...`);
    res.redirect(authUrl);
  } catch (error) {
    console.error('[Auth] Login error:', error);
    res.status(500).json({ error: 'Failed to initiate login' });
  }
});

/**
 * GET /auth/callback
 * Handle OIDC callback after authentication
 */
router.get('/callback', async (req, res) => {
  if (!authConfig.app.authEnabled) {
    return res.redirect(authConfig.app.frontendUrl);
  }

  try {
    const { code, state, error, error_description } = req.query;

    // Check for error from provider
    if (error) {
      console.error(`[Auth] Provider error: ${error} - ${error_description}`);
      return res.redirect(`${authConfig.app.frontendUrl}/login?error=${encodeURIComponent(error_description || error)}`);
    }

    // Validate state
    console.log(`[Auth] Callback received, session ID: ${req.session?.id}, stored state: ${req.session?.authState?.substring(0, 10) || 'NONE'}..., received state: ${state?.substring(0, 10)}...`);
    if (state !== req.session.authState) {
      console.error(`[Auth] State mismatch - expected: ${req.session.authState}, got: ${state}`);
      return res.redirect(`${authConfig.app.frontendUrl}/login?error=invalid_state`);
    }

    // Exchange code for tokens (nonce validation happens inside openid-client)
    const tokenSet = await oidc.exchangeCode(code, req.session.codeVerifier, req.session.authNonce);

    // Get claims from validated token
    const claims = oidc.getIdTokenClaims(tokenSet);

    // Extract user info
    const userInfo = await oidc.getUserInfo(tokenSet.access_token);

    // Debug: Log claims and userInfo to see available fields
    console.log('[Auth] ID Token claims:', JSON.stringify(claims, null, 2));
    console.log('[Auth] UserInfo:', JSON.stringify(userInfo, null, 2));

    // Extract groups from claims or userInfo
    let groups = claims.groups || userInfo.groups || [];

    // Filter to allowed groups
    groups = groups.filter(g => authConfig.groups.allowedGroups.includes(g));

    // Check if user has any allowed group
    if (groups.length === 0) {
      console.error(`[Auth] User ${userInfo.email} has no allowed groups`);
      return res.redirect(`${authConfig.app.frontendUrl}/login?error=no_access`);
    }

    // Create user session
    const user = {
      id: claims.sub,
      email: userInfo.email || claims.email,
      name: userInfo.name || claims.name || userInfo.preferred_username,
      avatar: userInfo.picture || claims.picture || null,
      groups,
      isAdmin: groups.includes(authConfig.groups.admin),
    };

    // Store user and tokens in session
    req.session.user = user;
    req.session.tokens = {
      accessToken: tokenSet.access_token,
      refreshToken: tokenSet.refresh_token,
      idToken: tokenSet.id_token,
      expiresAt: tokenSet.expires_at ? tokenSet.expires_at * 1000 : Date.now() + 3600000,
    };

    // Debug: Log token info
    console.log(`[Auth] Token info - expires_at: ${tokenSet.expires_at}, calculated expiresAt: ${req.session.tokens.expiresAt}, has refresh_token: ${!!tokenSet.refresh_token}`);

    // Clear auth state
    delete req.session.authState;
    delete req.session.authNonce;
    delete req.session.codeVerifier;

    // Get return URL
    const returnTo = req.session.returnTo || '/';
    delete req.session.returnTo;

    console.log(`[Auth] User ${user.email} logged in successfully (groups: ${groups.join(', ')})`);

    // Redirect to frontend
    res.redirect(`${authConfig.app.frontendUrl}${returnTo}`);
  } catch (error) {
    console.error('[Auth] Callback error:', error);
    res.redirect(`${authConfig.app.frontendUrl}/login?error=auth_failed`);
  }
});

/**
 * POST /auth/logout
 * Clear session and optionally redirect to OIDC logout
 */
router.post('/logout', (req, res) => {
  if (!authConfig.app.authEnabled) {
    return res.json({ success: true });
  }

  const idToken = req.session.tokens?.idToken;
  const userEmail = req.session.user?.email;

  // Destroy session
  req.session.destroy((err) => {
    if (err) {
      console.error('[Auth] Logout error:', err);
      return res.status(500).json({ error: 'Logout failed' });
    }

    // Clear cookie
    res.clearCookie(authConfig.session.name);

    console.log(`[Auth] User ${userEmail || 'unknown'} logged out`);

    // Return logout URL if available
    if (idToken) {
      try {
        const logoutUrl = oidc.getEndSessionUrl(idToken, authConfig.app.frontendUrl);
        return res.json({ success: true, logoutUrl });
      } catch (e) {
        // Issuer might not support end_session
      }
    }

    res.json({ success: true });
  });
});

/**
 * GET /auth/user
 * Get current authenticated user
 */
router.get('/user', (req, res) => {
  console.log(`[Auth] /user called, session ID: ${req.session?.id}, has user: ${!!req.session?.user}`);

  if (!authConfig.app.authEnabled) {
    return res.json({
      user: { id: 'anonymous', email: 'anonymous@local', name: 'Anonymous', groups: ['agent-admin'], isAdmin: true },
      authEnabled: false,
    });
  }

  if (!req.session || !req.session.user) {
    console.log(`[Auth] /user - not authenticated, session: ${JSON.stringify(req.session || {})}`);
    return res.status(401).json({ error: 'Not authenticated' });
  }

  console.log(`[Auth] /user - returning user: ${req.session.user.email}`);
  res.json({
    user: req.session.user,
    authEnabled: true,
    expiresAt: req.session.tokens?.expiresAt,
  });
});

/**
 * POST /auth/refresh
 * Refresh access token
 */
router.post('/refresh', async (req, res) => {
  console.log(`[Auth] /refresh called, session ID: ${req.session?.id}, has refreshToken: ${!!req.session?.tokens?.refreshToken}`);

  if (!authConfig.app.authEnabled) {
    return res.json({ success: true });
  }

  if (!req.session || !req.session.tokens?.refreshToken) {
    console.log('[Auth] /refresh - no refresh token in session');
    return res.status(401).json({ error: 'No refresh token' });
  }

  try {
    const newTokenSet = await oidc.refreshTokens(req.session.tokens.refreshToken);

    req.session.tokens = {
      accessToken: newTokenSet.access_token,
      refreshToken: newTokenSet.refresh_token || req.session.tokens.refreshToken,
      idToken: newTokenSet.id_token || req.session.tokens.idToken,
      expiresAt: newTokenSet.expires_at ? newTokenSet.expires_at * 1000 : Date.now() + 3600000,
    };

    console.log(`[Auth] Token refreshed for user ${req.session.user?.email}`);

    res.json({
      success: true,
      expiresAt: req.session.tokens.expiresAt,
    });
  } catch (error) {
    console.error('[Auth] Token refresh failed:', error.message);
    res.status(401).json({ error: 'Token refresh failed', message: error.message });
  }
});

/**
 * GET /auth/status
 * Check auth status and config
 */
router.get('/status', (req, res) => {
  res.json({
    authEnabled: authConfig.app.authEnabled,
    isAuthenticated: !!(req.session && req.session.user),
    user: req.session?.user ? {
      email: req.session.user.email,
      name: req.session.user.name,
      isAdmin: req.session.user.isAdmin,
    } : null,
  });
});

export default router;
