/**
 * 登录业务服务
 *
 * 功能描述：
 * - 处理用户登录相关的业务逻辑和流程控制
 * - 整合核心服务，提供完整的登录功能
 * - 处理业务规则、数据格式化和错误处理
 * - 管理JWT令牌刷新和验证码登录
 *
 * 职责分离：
 * - 专注于登录业务流程和规则实现
 * - 调用核心服务完成具体功能
 * - 为控制器层提供登录业务接口
 * - JWT技术实现已移至Core层，符合架构分层原则
 *
 * 最近修改：
 * - 2026-01-12: 代码分离 - 移除注册相关业务逻辑，专注于登录功能
 * - 2026-01-07: 代码规范优化 - 文件夹扁平化，移除单文件文件夹结构
 * - 2026-01-07: 架构优化 - 将JWT技术实现移至login_core模块，符合架构分层原则
 *
 * @author moyin
 * @version 1.1.0
 * @since 2025-12-17
 * @lastModified 2026-01-12
 */

import { Injectable, Logger, Inject } from '@nestjs/common';
import { LoginCoreService, LoginRequest, GitHubOAuthRequest, PasswordResetRequest, VerificationCodeLoginRequest, TokenPair } from '../../core/login_core/login_core.service';
import { Users } from '../../core/db/users/users.entity';
import { ZulipAccountService } from '../../core/zulip_core/services/zulip_account.service';
import { ApiKeySecurityService } from '../../core/zulip_core/services/api_key_security.service';
import { AccountProfilePayload, AccountProfileService } from './account_profile.service';

// Import the interface types we need
interface IZulipAccountsService {
  findByGameUserId(gameUserId: string, includeGameUser?: boolean): Promise<any>;
  create(createDto: any): Promise<any>;
  deleteByGameUserId(gameUserId: string): Promise<boolean>;
}

// 常量定义
const ERROR_CODES = {
  LOGIN_FAILED: 'LOGIN_FAILED',
  GITHUB_OAUTH_FAILED: 'GITHUB_OAUTH_FAILED',
  SEND_CODE_FAILED: 'SEND_CODE_FAILED',
  RESET_PASSWORD_FAILED: 'RESET_PASSWORD_FAILED',
  CHANGE_PASSWORD_FAILED: 'CHANGE_PASSWORD_FAILED',
  VERIFICATION_CODE_LOGIN_FAILED: 'VERIFICATION_CODE_LOGIN_FAILED',
  SEND_LOGIN_CODE_FAILED: 'SEND_LOGIN_CODE_FAILED',
  TOKEN_REFRESH_FAILED: 'TOKEN_REFRESH_FAILED',
  DEBUG_VERIFICATION_CODE_FAILED: 'DEBUG_VERIFICATION_CODE_FAILED',
  TEST_MODE_ONLY: 'TEST_MODE_ONLY',
  INVALID_VERIFICATION_CODE: 'INVALID_VERIFICATION_CODE',
} as const;

const MESSAGES = {
  LOGIN_SUCCESS: '登录成功',
  GITHUB_LOGIN_SUCCESS: 'GitHub登录成功',
  GITHUB_BIND_SUCCESS: 'GitHub账户绑定成功',
  PASSWORD_RESET_SUCCESS: '密码重置成功',
  PASSWORD_CHANGE_SUCCESS: '密码修改成功',
  VERIFICATION_CODE_LOGIN_SUCCESS: '验证码登录成功',
  TOKEN_REFRESH_SUCCESS: '令牌刷新成功',
  DEBUG_INFO_SUCCESS: '调试信息获取成功',
  CODE_SENT: '验证码已发送，请查收',
  VERIFICATION_CODE_ERROR: '验证码错误',
  TEST_MODE_WARNING: '⚠️ 测试模式：验证码已生成但未真实发送。请在控制台查看验证码，或配置邮件服务以启用真实发送。',
} as const;

// JWT相关接口已移至Core层，通过import导入

/**
 * 登录响应数据接口
 */
export interface LoginResponse {
  /** 用户信息 */
  user: {
    id: string;
    username: string;
    nickname: string;
    email?: string;
    phone?: string;
    avatar_url?: string;
    role: number;
    created_at: Date;
  };
  /** 游戏内账号资料 */
  profile?: AccountProfilePayload['profile'];
  /** 访问令牌 */
  access_token: string;
  /** 刷新令牌 */
  refresh_token: string;
  /** 访问令牌过期时间（秒） */
  expires_in: number;
  /** 令牌类型 */
  token_type: string;
  /** 是否为新用户 */
  is_new_user?: boolean;
  /** 消息 */
  message: string;
}

/**
 * 通用响应接口
 */
export interface ApiResponse<T = any> {
  /** 是否成功 */
  success: boolean;
  /** 响应数据 */
  data?: T;
  /** 消息 */
  message: string;
  /** 错误代码 */
  error_code?: string;
}

@Injectable()
export class LoginService {
  private readonly logger = new Logger(LoginService.name);

  constructor(
    private readonly loginCoreService: LoginCoreService,
    private readonly zulipAccountService: ZulipAccountService,
    @Inject('ZulipAccountsService') private readonly zulipAccountsService: IZulipAccountsService,
    private readonly apiKeySecurityService: ApiKeySecurityService,
    private readonly accountProfileService: AccountProfileService,
  ) {}

  /**
   * 用户登录
   *
   * 功能描述：
   * 处理用户登录请求，验证用户凭据并生成JWT令牌
   *
   * 业务逻辑：
   * 1. 调用核心服务进行用户认证
   * 2. 生成JWT访问令牌和刷新令牌
   * 3. 记录登录日志和安全审计
   * 4. 返回用户信息和令牌
   *
   * @param loginRequest 登录请求数据
   * @returns Promise<ApiResponse<LoginResponse>> 登录响应
   *
   * @throws BadRequestException 当登录参数无效时
   * @throws UnauthorizedException 当用户凭据错误时
   * @throws InternalServerErrorException 当系统错误时
   */
  async login(loginRequest: LoginRequest): Promise<ApiResponse<LoginResponse>> {
    const startTime = Date.now();

    try {
      this.logger.log('用户登录尝试', {
        operation: 'login',
        identifier: loginRequest.identifier,
        timestamp: new Date().toISOString(),
      });

      // 1. 调用核心服务进行认证
      const authResult = await this.loginCoreService.login(loginRequest);

      // 2. 验证和更新Zulip API Key（如果用户有Zulip账号关联）
      try {
        const isZulipValid = await this.validateAndUpdateZulipApiKey(authResult.user);
        if (!isZulipValid) {
          // 尝试重新生成API Key（需要密码）
          const regenerated = await this.regenerateZulipApiKey(authResult.user, loginRequest.password);
          if (regenerated) {
            this.logger.log('用户Zulip API Key已重新生成', {
              operation: 'login',
              userId: authResult.user.id.toString(),
            });
          } else {
            this.logger.warn('用户Zulip API Key重新生成失败', {
              operation: 'login',
              userId: authResult.user.id.toString(),
            });
          }
        }
      } catch (zulipError) {
        // Zulip验证失败不影响登录流程，只记录日志
        const err = zulipError as Error;
        this.logger.warn('Zulip API Key验证失败，但不影响登录', {
          operation: 'login',
          userId: authResult.user.id.toString(),
          zulipError: err.message,
        });
      }

      // 3. 生成JWT令牌对（通过Core层）
      const tokenPair = await this.loginCoreService.generateTokenPair(authResult.user);
      const profile = await this.accountProfileService.ensureProfile(authResult.user.id);

      // 4. 格式化响应数据
      const response: LoginResponse = {
        user: this.formatUserInfo(authResult.user),
        profile: (await this.accountProfileService.formatAccountProfileAsync(authResult.user, profile)).profile,
        access_token: tokenPair.access_token,
        refresh_token: tokenPair.refresh_token,
        expires_in: tokenPair.expires_in,
        token_type: tokenPair.token_type,
        is_new_user: authResult.isNewUser,
        message: MESSAGES.LOGIN_SUCCESS
      };

      const duration = Date.now() - startTime;

      this.logger.log('用户登录成功', {
        operation: 'login',
        userId: authResult.user.id.toString(),
        username: authResult.user.username,
        isNewUser: authResult.isNewUser,
        duration,
        timestamp: new Date().toISOString(),
      });

      return {
        success: true,
        data: response,
        message: MESSAGES.LOGIN_SUCCESS
      };
    } catch (error) {
      const duration = Date.now() - startTime;
      const err = error as Error;

      this.logger.error('用户登录失败', {
        operation: 'login',
        identifier: loginRequest.identifier,
        error: err.message,
        duration,
        timestamp: new Date().toISOString(),
      }, err.stack);

      return {
        success: false,
        message: err.message || '登录失败',
        error_code: ERROR_CODES.LOGIN_FAILED
      };
    }
  }

  /**
   * GitHub OAuth登录
   *
   * @param oauthRequest OAuth请求
   * @returns 登录响应
   */
  async githubOAuth(oauthRequest: GitHubOAuthRequest): Promise<ApiResponse<LoginResponse>> {
    try {
      this.logger.log(`GitHub OAuth登录尝试: ${oauthRequest.github_id}`);

      // 调用核心服务进行OAuth认证
      const authResult = await this.loginCoreService.githubOAuth(oauthRequest);

      // 生成JWT令牌对（通过Core层）
      const tokenPair = await this.loginCoreService.generateTokenPair(authResult.user);

      // 格式化响应数据
      const response: LoginResponse = {
        user: this.formatUserInfo(authResult.user),
        access_token: tokenPair.access_token,
        refresh_token: tokenPair.refresh_token,
        expires_in: tokenPair.expires_in,
        token_type: tokenPair.token_type,
        is_new_user: authResult.isNewUser,
        message: authResult.isNewUser ? MESSAGES.GITHUB_BIND_SUCCESS : MESSAGES.GITHUB_LOGIN_SUCCESS
      };

      this.logger.log(`GitHub OAuth成功: ${authResult.user.username} (ID: ${authResult.user.id})`);

      return {
        success: true,
        data: response,
        message: response.message
      };
    } catch (error) {
      this.logger.error(`GitHub OAuth失败: ${oauthRequest.github_id}`, error instanceof Error ? error.stack : String(error));

      return {
        success: false,
        message: error instanceof Error ? error.message : 'GitHub登录失败',
        error_code: ERROR_CODES.GITHUB_OAUTH_FAILED
      };
    }
  }

  /**
   * 发送密码重置验证码
   *
   * @param identifier 邮箱或手机号
   * @returns 响应结果
   */
  async sendPasswordResetCode(identifier: string): Promise<ApiResponse<{ verification_code?: string; is_test_mode?: boolean }>> {
    try {
      this.logger.log(`发送密码重置验证码: ${identifier}`);

      // 调用核心服务发送验证码
      const result = await this.loginCoreService.sendPasswordResetCode(identifier);

      this.logger.log(`密码重置验证码已发送: ${identifier}`);

      // 处理测试模式响应
      if (result.isTestMode) {
        return {
          success: false,
          data: {
            verification_code: result.code,
            is_test_mode: true
          },
          message: MESSAGES.TEST_MODE_WARNING,
          error_code: ERROR_CODES.TEST_MODE_ONLY
        };
      } else {
        return {
          success: true,
          data: {
            is_test_mode: false
          },
          message: MESSAGES.CODE_SENT
        };
      }
    } catch (error) {
      this.logger.error(`发送密码重置验证码失败: ${identifier}`, error instanceof Error ? error.stack : String(error));

      return {
        success: false,
        message: error instanceof Error ? error.message : '发送验证码失败',
        error_code: ERROR_CODES.SEND_CODE_FAILED
      };
    }
  }

  /**
   * 重置密码
   *
   * @param resetRequest 重置请求
   * @returns 响应结果
   */
  async resetPassword(resetRequest: PasswordResetRequest): Promise<ApiResponse> {
    try {
      this.logger.log(`密码重置尝试: ${resetRequest.identifier}`);

      // 调用核心服务重置密码
      await this.loginCoreService.resetPassword(resetRequest);

      this.logger.log(`密码重置成功: ${resetRequest.identifier}`);

      return {
        success: true,
        message: MESSAGES.PASSWORD_RESET_SUCCESS
      };
    } catch (error) {
      this.logger.error(`密码重置失败: ${resetRequest.identifier}`, error instanceof Error ? error.stack : String(error));

      return {
        success: false,
        message: error instanceof Error ? error.message : '密码重置失败',
        error_code: ERROR_CODES.RESET_PASSWORD_FAILED
      };
    }
  }

  /**
   * 修改密码
   *
   * @param userId 用户ID
   * @param oldPassword 旧密码
   * @param newPassword 新密码
   * @returns 响应结果
   */
  async changePassword(userId: bigint, oldPassword: string, newPassword: string): Promise<ApiResponse> {
    try {
      this.logger.log(`修改密码尝试: 用户ID ${userId}`);

      // 调用核心服务修改密码
      await this.loginCoreService.changePassword(userId, oldPassword, newPassword);

      this.logger.log(`修改密码成功: 用户ID ${userId}`);

      return {
        success: true,
        message: MESSAGES.PASSWORD_CHANGE_SUCCESS
      };
    } catch (error) {
      this.logger.error(`修改密码失败: 用户ID ${userId}`, error instanceof Error ? error.stack : String(error));

      return {
        success: false,
        message: error instanceof Error ? error.message : '密码修改失败',
        error_code: ERROR_CODES.CHANGE_PASSWORD_FAILED
      };
    }
  }

  /**
   * 格式化用户信息
   *
   * @param user 用户实体
   * @returns 格式化的用户信息
   */
  private formatUserInfo(user: Users) {
    return {
      id: user.id.toString(), // 将bigint转换为字符串
      username: user.username,
      nickname: user.nickname,
      email: user.email,
      phone: user.phone,
      avatar_url: user.avatar_url,
      role: user.role,
      created_at: user.created_at
    };
  }

  /**
   * 验证码登录
   *
   * @param loginRequest 验证码登录请求
   * @returns 登录响应
   */
  async verificationCodeLogin(loginRequest: VerificationCodeLoginRequest): Promise<ApiResponse<LoginResponse>> {
    try {
      this.logger.log(`验证码登录尝试: ${loginRequest.identifier}`);

      // 调用核心服务进行验证码认证
      const authResult = await this.loginCoreService.verificationCodeLogin(loginRequest);

      // 生成JWT令牌对（通过Core层）
      const tokenPair = await this.loginCoreService.generateTokenPair(authResult.user);

      // 格式化响应数据
      const response: LoginResponse = {
        user: this.formatUserInfo(authResult.user),
        access_token: tokenPair.access_token,
        refresh_token: tokenPair.refresh_token,
        expires_in: tokenPair.expires_in,
        token_type: tokenPair.token_type,
        is_new_user: authResult.isNewUser,
        message: MESSAGES.VERIFICATION_CODE_LOGIN_SUCCESS
      };

      this.logger.log(`验证码登录成功: ${authResult.user.username} (ID: ${authResult.user.id})`);

      return {
        success: true,
        data: response,
        message: MESSAGES.VERIFICATION_CODE_LOGIN_SUCCESS
      };
    } catch (error) {
      this.logger.error(`验证码登录失败: ${loginRequest.identifier}`, error instanceof Error ? error.stack : String(error));

      return {
        success: false,
        message: error instanceof Error ? error.message : '验证码登录失败',
        error_code: ERROR_CODES.VERIFICATION_CODE_LOGIN_FAILED
      };
    }
  }

  /**
   * 发送登录验证码
   *
   * @param identifier 邮箱或手机号
   * @returns 响应结果
   */
  async sendLoginVerificationCode(identifier: string): Promise<ApiResponse<{ verification_code?: string; is_test_mode?: boolean }>> {
    try {
      this.logger.log(`发送登录验证码: ${identifier}`);

      // 调用核心服务发送验证码
      const result = await this.loginCoreService.sendLoginVerificationCode(identifier);

      this.logger.log(`登录验证码已发送: ${identifier}`);

      // 处理测试模式响应
      if (result.isTestMode) {
        return {
          success: false,
          data: {
            verification_code: result.code,
            is_test_mode: true
          },
          message: MESSAGES.TEST_MODE_WARNING,
          error_code: ERROR_CODES.TEST_MODE_ONLY
        };
      } else {
        return {
          success: true,
          data: {
            is_test_mode: false
          },
          message: MESSAGES.CODE_SENT
        };
      }
    } catch (error) {
      this.logger.error(`发送登录验证码失败: ${identifier}`, error instanceof Error ? error.stack : String(error));

      return {
        success: false,
        message: error instanceof Error ? error.message : '发送验证码失败',
        error_code: ERROR_CODES.SEND_LOGIN_CODE_FAILED
      };
    }
  }

  /**
   * 刷新访问令牌
   *
   * 功能描述：
   * 使用有效的刷新令牌生成新的访问令牌，实现无感知的令牌续期
   *
   * 业务逻辑：
   * 1. 验证刷新令牌的有效性和格式
   * 2. 检查用户状态是否正常
   * 3. 生成新的JWT令牌对
   * 4. 返回新的访问令牌和刷新令牌
   *
   * @param refreshToken 刷新令牌字符串
   * @returns Promise<ApiResponse<TokenPair>> 新的令牌对
   *
   * @throws UnauthorizedException 当刷新令牌无效或已过期时
   * @throws NotFoundException 当用户不存在或已被禁用时
   *
   * @example
   * ```typescript
   * const result = await loginService.refreshAccessToken('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...');
   * ```
   */
  async refreshAccessToken(refreshToken: string): Promise<ApiResponse<TokenPair>> {
    try {
      this.logger.log(`刷新访问令牌尝试`);

      // 调用核心服务刷新令牌
      const tokenPair = await this.loginCoreService.refreshAccessToken(refreshToken);

      this.logger.log(`访问令牌刷新成功`);

      return {
        success: true,
        data: tokenPair,
        message: MESSAGES.TOKEN_REFRESH_SUCCESS
      };
    } catch (error) {
      this.logger.error(`访问令牌刷新失败`, error instanceof Error ? error.stack : String(error));

      return {
        success: false,
        message: error instanceof Error ? error.message : '令牌刷新失败',
        error_code: ERROR_CODES.TOKEN_REFRESH_FAILED
      };
    }
  }
  /**
   * 调试验证码信息
   * 仅用于开发和调试
   *
   * @param email 邮箱地址
   * @returns 验证码调试信息
   */
  async debugVerificationCode(email: string): Promise<any> {
    try {
      this.logger.log(`调试验证码信息: ${email}`);

      const debugInfo = await this.loginCoreService.debugVerificationCode(email);

      return {
        success: true,
        data: debugInfo,
        message: MESSAGES.DEBUG_INFO_SUCCESS
      };
    } catch (error) {
      this.logger.error(`获取验证码调试信息失败: ${email}`, error instanceof Error ? error.stack : String(error));

      return {
        success: false,
        message: error instanceof Error ? error.message : '获取调试信息失败',
        error_code: ERROR_CODES.DEBUG_VERIFICATION_CODE_FAILED
      };
    }
  }

  /**
   * 验证并更新用户的Zulip API Key
   *
   * 功能描述：
   * 在用户登录时验证其Zulip账号的API Key是否有效，如果无效则重新获取
   *
   * 业务逻辑：
   * 1. 查找用户的Zulip账号关联
   * 2. 从Redis获取API Key
   * 3. 验证API Key是否有效
   * 4. 如果无效，重新生成API Key并更新存储
   *
   * @param user 用户信息
   * @returns Promise<boolean> 是否验证/更新成功
   * @private
   */
  private async validateAndUpdateZulipApiKey(user: Users): Promise<boolean> {
    const startTime = Date.now();

    this.logger.log('开始验证用户Zulip API Key', {
      operation: 'validateAndUpdateZulipApiKey',
      userId: user.id.toString(),
      username: user.username,
      email: user.email,
    });

    try {
      // 1. 查找用户的Zulip账号关联
      const zulipAccount = await this.zulipAccountsService.findByGameUserId(user.id.toString());
      if (!zulipAccount) {
        this.logger.log('用户没有Zulip账号关联，跳过验证', {
          operation: 'validateAndUpdateZulipApiKey',
          userId: user.id.toString(),
        });
        return true; // 没有关联不算错误
      }

      // 2. 从Redis获取API Key
      const apiKeyResult = await this.apiKeySecurityService.getApiKey(user.id.toString());
      if (!apiKeyResult.success || !apiKeyResult.apiKey) {
        this.logger.warn('用户Zulip API Key不存在，需要重新生成', {
          operation: 'validateAndUpdateZulipApiKey',
          userId: user.id.toString(),
          zulipEmail: zulipAccount.zulipEmail,
          error: apiKeyResult.message,
        });

        return false; // 需要重新生成
      }

      // 3. 验证API Key是否有效
      const validationResult = await this.zulipAccountService.validateZulipAccount(
        zulipAccount.zulipEmail,
        apiKeyResult.apiKey
      );

      if (validationResult.success && validationResult.isValid) {
        this.logger.log('用户Zulip API Key验证成功', {
          operation: 'validateAndUpdateZulipApiKey',
          userId: user.id.toString(),
          zulipEmail: zulipAccount.zulipEmail,
        });
        return true;
      }

      // 4. API Key无效，需要重新生成
      this.logger.warn('用户Zulip API Key无效，需要重新生成', {
        operation: 'validateAndUpdateZulipApiKey',
        userId: user.id.toString(),
        zulipEmail: zulipAccount.zulipEmail,
        validationError: validationResult.error,
      });

      return false; // 需要重新生成

    } catch (error) {
      const err = error as Error;
      const duration = Date.now() - startTime;

      this.logger.error('验证用户Zulip API Key失败', {
        operation: 'validateAndUpdateZulipApiKey',
        userId: user.id.toString(),
        error: err.message,
        duration,
      }, err.stack);

      return false;
    }
  }

  /**
   * 重新生成并更新用户的Zulip API Key
   *
   * 功能描述：
   * 使用用户密码重新生成Zulip API Key并更新存储
   *
   * @param user 用户信息
   * @param password 用户密码（明文）
   * @returns Promise<boolean> 是否更新成功
   * @private
   */
  private async regenerateZulipApiKey(user: Users, password: string): Promise<boolean> {
    const startTime = Date.now();

    this.logger.log('开始重新生成用户Zulip API Key', {
      operation: 'regenerateZulipApiKey',
      userId: user.id.toString(),
      email: user.email,
    });

    try {
      // 1. 查找用户的Zulip账号关联
      const zulipAccount = await this.zulipAccountsService.findByGameUserId(user.id.toString());
      if (!zulipAccount) {
        this.logger.warn('用户没有Zulip账号关联，无法重新生成API Key', {
          operation: 'regenerateZulipApiKey',
          userId: user.id.toString(),
        });
        return false;
      }

      // 2. 重新生成API Key
      const apiKeyResult = await this.zulipAccountService.generateApiKeyForUser(
        zulipAccount.zulipEmail,
        password
      );

      if (!apiKeyResult.success) {
        this.logger.error('重新生成Zulip API Key失败', {
          operation: 'regenerateZulipApiKey',
          userId: user.id.toString(),
          zulipEmail: zulipAccount.zulipEmail,
          error: apiKeyResult.error,
        });
        return false;
      }

      // 3. 更新Redis中的API Key
      await this.apiKeySecurityService.storeApiKey(
        user.id.toString(),
        apiKeyResult.apiKey!
      );

      // 注意：不在登录时建立内存关联，Zulip客户端将在WebSocket连接时创建

      const duration = Date.now() - startTime;

      this.logger.log('重新生成Zulip API Key成功', {
        operation: 'regenerateZulipApiKey',
        userId: user.id.toString(),
        zulipEmail: zulipAccount.zulipEmail,
        duration,
      });

      return true;

    } catch (error) {
      const err = error as Error;
      const duration = Date.now() - startTime;

      this.logger.error('重新生成Zulip API Key失败', {
        operation: 'regenerateZulipApiKey',
        userId: user.id.toString(),
        error: err.message,
        duration,
      }, err.stack);

      return false;
    }
  }
}
