const axios = require('axios');
const User = require('../models/User');
const LoginHistory = require('../models/LoginHistory');
const Invitation = require('../models/Invitation');
const { generateToken, generateRefreshToken } = require('../utils/jwt');
const env = require('../config/env');
const configService = require('./configService');

/**
 * Authentication Service
 * Handles WeChat login and token management
 */

/**
 * Generate unique invitation code
 * @returns {string} 8-character invitation code
 */
const generateInvitationCode = () => {
  const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
  let code = '';
  for (let i = 0; i < 8; i++) {
    code += chars.charAt(Math.floor(Math.random() * chars.length));
  }
  return code;
};

/**
 * Authenticate user with WeChat
 * @param {string} code - WeChat authorization code
 * @param {Object} userInfo - WeChat user info (nickname, avatar)
 * @param {string} invitationCode - Optional invitation code
 * @param {Object} deviceInfo - Device information
 * @returns {Object} User and tokens
 */
const wechatLogin = async (code, userInfo = {}, invitationCode = null, deviceInfo = {}) => {
  // Exchange code for openId and session_key from WeChat
  // In production, this would call WeChat API
  // For now, we'll simulate it
  let openId;
  
  if (env.nodeEnv === 'test' || env.nodeEnv === 'development') {
    // For testing/development, use a fixed test openId to avoid creating duplicate users
    // In real WeChat environment, the code would be exchanged for a consistent openId
    openId = 'test_user_' + (userInfo.nickname || 'default');
  } else {
    // Production: Call WeChat API
    const wechatUrl = `https://api.weixin.qq.com/sns/jscode2session`;
    const params = {
      appid: env.wechat.appId,
      secret: env.wechat.appSecret,
      js_code: code,
      grant_type: 'authorization_code',
    };
    
    const response = await axios.get(wechatUrl, { params });
    
    if (response.data.errcode) {
      throw new Error(`WeChat authentication failed: ${response.data.errmsg}`);
    }
    
    openId = response.data.openid;
  }

  // Find or create user
  let user = await User.findOne({ where: { wechatOpenId: openId } });
  
  if (!user) {
    // Get default avatar from config
    let defaultAvatar = null;
    try {
      const avatarConfig = await configService.getConfig('default_avatar');
      if (avatarConfig && avatarConfig.value) {
        defaultAvatar = avatarConfig.value;
      }
    } catch (e) {
      console.error('Failed to get default avatar config:', e);
    }

    // Create new user
    const userData = {
      wechatOpenId: openId,
      nickname: userInfo.nickname || 'User',
      avatar: userInfo.avatar || defaultAvatar,
      invitationCode: await generateUniqueInvitationCode(),
    };

    // Handle invitation
    let inviterId = null;
    if (invitationCode) {
      const inviter = await User.findOne({ where: { invitationCode } });
      if (inviter) {
        userData.invitedBy = inviter.id;
        inviterId = inviter.id;
      }
    }

    user = await User.create(userData);

    // Record invitation relationship
    if (inviterId) {
      await Invitation.create({
        inviterId,
        inviteeId: user.id,
        invitationCode,
        registeredAt: new Date(),
        rewardStatus: 'pending',
      });
    }
  } else {
    // Update existing user info if provided
    if (userInfo.nickname) user.nickname = userInfo.nickname;
    if (userInfo.avatar) user.avatar = userInfo.avatar;
    await user.save();
  }

  // Generate tokens
  const accessToken = generateToken({
    userId: user.id,
    type: 'user',
  });

  const refreshToken = generateRefreshToken({
    userId: user.id,
    type: 'user',
  });

  // Record login history
  await LoginHistory.create({
    userId: user.id,
    userType: 'user',
    ipAddress: deviceInfo.ipAddress || null,
    userAgent: deviceInfo.userAgent || null,
    deviceInfo: deviceInfo.device || null,
    loginAt: new Date(),
  });

  return {
    user,
    accessToken,
    refreshToken,
  };
};

/**
 * Generate unique invitation code (check for duplicates)
 * @returns {string} Unique invitation code
 */
const generateUniqueInvitationCode = async () => {
  let code;
  let exists = true;
  
  while (exists) {
    code = generateInvitationCode();
    const user = await User.findOne({ where: { invitationCode: code } });
    exists = !!user;
  }
  
  return code;
};

/**
 * Refresh access token
 * @param {string} refreshToken - Refresh token
 * @returns {Object} New access token
 */
const refreshAccessToken = async (refreshToken) => {
  const { verifyToken } = require('../utils/jwt');
  
  // Verify refresh token
  let decoded;
  try {
    decoded = verifyToken(refreshToken);
  } catch (error) {
    throw new Error('Invalid or expired refresh token');
  }

  // Check token type
  if (decoded.type !== 'user') {
    throw new Error('Invalid token type');
  }

  // Verify user still exists and is active
  const user = await User.findByPk(decoded.userId);
  
  if (!user) {
    throw new Error('User not found');
  }

  if (user.status === 'suspended') {
    throw new Error('Account suspended');
  }

  // Generate new access token
  const accessToken = generateToken({
    userId: user.id,
    type: 'user',
  });

  return {
    accessToken,
    user,
  };
};

module.exports = {
  wechatLogin,
  refreshAccessToken,
  generateInvitationCode,
  generateUniqueInvitationCode,
};
