const axios = require('axios');
const { Op } = require('sequelize');

const { categorizeError, createAuthErrorResponse } = require('../../helpers/auth-error-handler');
const { getBrowserInfo } = require('../../helpers/get-browser-info');
const { updateOrCreateToken } = require('../../helpers/token-helper');
const { LoginSessions, Users, UsersFlags, UsersPrivacyFlags } = require('../../models');

// Timeout for external API calls (10 seconds)
const API_TIMEOUT = 10000;

/**
 * SSO Login Controller Handles social sign-on login for users who have already authenticated with a
 * social provider on the client side. This endpoint creates or finds the user and returns access
 * tokens.
 *
 * @param {import('express').Request} req The Express request object
 * @param {import('express').Response} res The Express response object
 * @returns {Promise<import('express').Response>} JSON response with tokens and user data
 */
const ssoLogin = async (req, res) => {
  const { provider, providerId, email, firstname, lastname, image } = req.body;

  try {
    // Validate provider type
    const validProviders = ['google', 'facebook', 'apple'];
    if (!validProviders.includes(provider)) {
      return res.status(400).json({
        success: false,
        message: `Invalid provider. Must be one of: ${validProviders.join(', ')}`,
      });
    }

    // Map provider to database fields
    const providerFieldMap = {
      google: { idField: 'google_id', flagField: 'is_google' },
      facebook: { idField: 'facebook_id', flagField: 'is_facebook' },
      apple: { idField: 'apple_id', flagField: 'is_apple' },
    };

    const { idField, flagField } = providerFieldMap[provider];

    // Find existing user by provider ID or email
    let user = await Users.findOne({
      where: {
        [Op.or]: [{ [idField]: providerId }, { email_address: email }],
      },
    });

    // If user exists but doesn't have provider ID, link the account
    if (user && !user[idField]) {
      user[idField] = providerId;
      user[flagField] = true;
      await user.save();
    }

    // If user doesn't exist, create new user
    if (!user) {
      user = await Users.create({
        email_address: email,
        firstname: firstname || '',
        lastname: lastname || '',
        [idField]: providerId,
        [flagField]: true,
        image: image || null,
      });

      // Create default privacy flags for new user
      await UsersPrivacyFlags.create({
        user_id: user.id,
        read_receipts: true,
        view_my_on_listings: true,
        display_my_location: true,
        display_trip_details: true,
        display_duration_of_visit: true,
      });

      // Create default user flags for new user
      await UsersFlags.create({
        user_id: user.id,
        travel_advice: true,
        travel_advice_email: true,
        travel_advice_message: true,
        account_management_and_guidelines: true,
        account_management_and_guidelines_email: true,
        account_management_and_guidelines_message: true,
        travelers_and_host_messages: true,
        travelers_and_host_messages_email: true,
        travelers_and_host_messages_message: true,
      });
    }

    // Reactivate account if it was deactivated
    if (user.status === false) {
      user.status = true;
      await user.save();
    }

    // Track login session
    const ip = req.ip || req.headers['x-forwarded-for'] || 'Unknown';
    let address = null;
    let lat = null;
    let lng = null;

    if (ip !== 'Unknown') {
      try {
        const response = await axios.get(
          `https://ipinfo.io/${ip}/json?token=${process.env.IP_API_KEY}`,
          { timeout: API_TIMEOUT },
        );
        const location = response.data;

        address = location.city || location.region || location.country || 'Unknown';
        const [latitude, longitude] = location.loc ? location.loc.split(',') : [null, null];
        lat = latitude;
        lng = longitude;
      } catch (error) {
        console.error('Geolocation fetch error:', error);
        // Continue without geolocation - not critical for login
      }
    }

    const browserInfo = getBrowserInfo(req);

    await LoginSessions.create({
      user_id: user.id,
      ip_address: ip,
      browser: `${browserInfo.browser} ${browserInfo.version}`,
      os: browserInfo.platform,
      device: browserInfo.device,
      is_mobile_app: browserInfo.isMobileApp,
      address,
      lng,
      lat,
    });

    // Generate tokens
    const { token, refreshToken } = await updateOrCreateToken(user.id, req, {
      isHost: user.is_host,
      hasPendingApplication: user.has_pending_host_application,
    });

    return res.json({
      success: true,
      token,
      refreshToken,
      user: {
        id: user.id,
        image: user.image,
        firstname: user.firstname,
        lastname: user.lastname,
        email: user.email_address,
        phoneNo: user.phone_no,
        isHost: user.is_host,
        hasValidId: !!user?.valid_id_image,
        hasPendingHostApplication: user.has_pending_host_application || false,
        hasIncompleteListing: false,
        hasHostPayoutMethod: false,
        isGoogle: user.is_google,
        isFacebook: user.is_facebook,
        isApple: user.is_apple,
      },
    });
  } catch (error) {
    const errorType = categorizeError(error);

    console.error('[SSO Login Error]', {
      type: errorType,
      provider,
      message: error.message,
      stack: process.env.NODE_ENV === 'development' ? error.stack : undefined,
    });

    // Return user-friendly error response
    const errorResponse = createAuthErrorResponse(error, provider);

    // Add specific HTTP status based on error type
    const statusCode =
      errorType === 'VALIDATION_ERROR' || errorType === 'PROVIDER_ERROR' ? 400 : 500;

    return res.status(statusCode).json(errorResponse);
  }
};

module.exports = {
  ssoLogin,
};
