import { Injectable, Inject, UnauthorizedException, ForbiddenException, ConflictException, BadRequestException } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { ConfigService } from '@nestjs/config';
import {
  UserAggregate,
  Phone,
  AccountSequence,
  Capability,
  USER_REPOSITORY,
  UserRepository,
  SYNCED_LEGACY_USER_REPOSITORY,
  SyncedLegacyUserRepository,
  REFRESH_TOKEN_REPOSITORY,
  RefreshTokenRepository,
  SMS_VERIFICATION_REPOSITORY,
  SmsVerificationRepository,
  SmsVerificationType,
  UserRegisteredEvent,
  LegacyUserMigratedEvent,
} from '@/domain';
import { OutboxService } from './outbox.service';
import { CapabilityService } from './capability.service';

export interface LoginResult {
  accessToken: string;
  refreshToken: string;
  expiresIn: number;
  user: {
    accountSequence: string;
    phone: string;
    source: 'V1' | 'V2';
    kycStatus: string;
  };
}

export interface RegisterDto {
  phone: string;
  password: string;
  smsCode: string;
}

export interface LoginDto {
  phone: string;
  password: string;
  deviceInfo?: string;
  ipAddress?: string;
}

export interface LoginBySmsDto {
  phone: string;
  smsCode: string;
  deviceInfo?: string;
  ipAddress?: string;
}

@Injectable()
export class AuthService {
  constructor(
    @Inject(USER_REPOSITORY)
    private readonly userRepository: UserRepository,
    @Inject(SYNCED_LEGACY_USER_REPOSITORY)
    private readonly syncedLegacyUserRepository: SyncedLegacyUserRepository,
    @Inject(REFRESH_TOKEN_REPOSITORY)
    private readonly refreshTokenRepository: RefreshTokenRepository,
    @Inject(SMS_VERIFICATION_REPOSITORY)
    private readonly smsVerificationRepository: SmsVerificationRepository,
    private readonly jwtService: JwtService,
    private readonly configService: ConfigService,
    private readonly outboxService: OutboxService,
    private readonly capabilityService: CapabilityService,
  ) {}

  /**
   * 用户注册（2.0 新用户）
   */
  async register(dto: RegisterDto): Promise<LoginResult> {
    const phone = Phone.create(dto.phone);

    // 检查手机号是否已存在于 V2 用户
    const existingUser = await this.userRepository.findByPhone(phone);
    if (existingUser) {
      throw new ConflictException('手机号已注册');
    }

    // 检查是否存在未迁移的 V1 用户
    const legacyUser = await this.syncedLegacyUserRepository.findByPhone(phone);
    if (legacyUser && !legacyUser.migratedToV2) {
      throw new ConflictException('此手机号已在1.0系统注册，请直接登录进行迁移');
    }

    // 生成 V2 账户序列号
    const accountSequence = await this.userRepository.getNextAccountSequence();

    // 创建新用户
    const user = await UserAggregate.create(phone, dto.password, accountSequence);
    const savedUser = await this.userRepository.save(user);

    // 发布用户注册事件
    await this.outboxService.publish(
      new UserRegisteredEvent(
        accountSequence.value,
        phone.value,
        'V2',
        new Date(),
      ),
    );

    // 生成 tokens
    return this.generateTokens(savedUser);
  }

  /**
   * 密码登录
   */
  async loginByPassword(dto: LoginDto): Promise<LoginResult> {
    const phone = Phone.create(dto.phone);

    // 先尝试从 V2 用户表查找
    let user = await this.userRepository.findByPhone(phone);

    if (user) {
      // V2 用户登录
      return this.handleV2Login(user, dto.password, dto.deviceInfo, dto.ipAddress);
    }

    // 尝试从同步的 V1 用户表查找
    const legacyUser = await this.syncedLegacyUserRepository.findByPhone(phone);
    if (!legacyUser) {
      throw new UnauthorizedException('该手机号未注册');
    }

    if (legacyUser.migratedToV2) {
      // 已迁移但找不到 V2 用户，数据异常
      throw new UnauthorizedException('账户状态异常，请联系客服');
    }

    // V1 用户首次登录，进行迁移
    return this.migrateAndLogin(legacyUser, dto.password, dto.deviceInfo, dto.ipAddress);
  }
  /**
   * 验证码登录
   */
  async loginBySms(dto: LoginBySmsDto): Promise<LoginResult> {
    const phone = Phone.create(dto.phone);
    await this.verifySmsCode(phone, dto.smsCode, SmsVerificationType.LOGIN);

    let user = await this.userRepository.findByPhone(phone);
    if (user) {
      if (!user.canLogin) {
        if (user.isLocked) {
          throw new UnauthorizedException('账户已被锁定，请稍后再试');
        }
        throw new UnauthorizedException('账户已被禁用');
      }

      // 检查 LOGIN 能力是否被限制
      const loginEnabled = await this.capabilityService.isCapabilityEnabled(
        user.accountSequence.value,
        Capability.LOGIN,
      );
      if (!loginEnabled) {
        throw new ForbiddenException('您的登录功能已被限制，请联系客服');
      }

      user.recordLoginSuccess(dto.ipAddress);
      await this.userRepository.save(user);
      return this.generateTokens(user, dto.deviceInfo, dto.ipAddress);
    }

    const legacyUser = await this.syncedLegacyUserRepository.findByPhone(phone);
    if (!legacyUser) {
      throw new UnauthorizedException('手机号未注册');
    }
    if (legacyUser.migratedToV2) {
      throw new UnauthorizedException('账户状态异常，请联系客服');
    }
    return this.performMigration(legacyUser, dto.deviceInfo, dto.ipAddress);
  }

  /**
   * 验证短信验证码
   */
  private async verifySmsCode(phone: Phone, code: string, type: SmsVerificationType): Promise<void> {
    const verification = await this.smsVerificationRepository.findLatestValid(phone, type);
    if (!verification) {
      throw new BadRequestException('验证码已过期或不存在');
    }
    if (verification.attempts >= 5) {
      throw new BadRequestException('验证码尝试次数过多，请重新获取');
    }
    if (verification.code !== code) {
      await this.smsVerificationRepository.incrementAttempts(verification.id);
      throw new BadRequestException('验证码错误');
    }
    await this.smsVerificationRepository.markAsVerified(verification.id);
  }

  /**
   * 处理 V2 用户登录
   */
  private async handleV2Login(
    user: UserAggregate,
    password: string,
    deviceInfo?: string,
    ipAddress?: string,
  ): Promise<LoginResult> {
    if (!user.canLogin) {
      if (user.isLocked) {
        const remainingSeconds = user.getLockRemainingSeconds();
        const minutes = Math.ceil(remainingSeconds / 60);
        throw new UnauthorizedException(`账户已被锁定，请${minutes}分钟后再试`);
      }
      throw new UnauthorizedException('账户已被禁用');
    }

    // 检查 LOGIN 能力是否被限制
    const loginEnabled = await this.capabilityService.isCapabilityEnabled(
      user.accountSequence.value,
      Capability.LOGIN,
    );
    if (!loginEnabled) {
      throw new ForbiddenException('您的登录功能已被限制，请联系客服');
    }

    const isValid = await user.verifyPassword(password);
    if (!isValid) {
      const result = user.recordLoginFailure();
      await this.userRepository.save(user);

      if (result.remainingAttempts === 0) {
        throw new UnauthorizedException(`密码错误次数过多，账户已锁定${result.lockMinutes}分钟`);
      }
      throw new UnauthorizedException(`密码错误，还剩${result.remainingAttempts}次尝试机会`);
    }

    user.recordLoginSuccess(ipAddress);
    await this.userRepository.save(user);

    return this.generateTokens(user, deviceInfo, ipAddress);
  }

  /**
   * 迁移 V1 用户并登录
   */
  private async migrateAndLogin(
    legacyUser: { accountSequence: AccountSequence; phone: Phone; passwordHash: string; nickname: string | null },
    password: string,
    deviceInfo?: string,
    ipAddress?: string,
  ): Promise<LoginResult> {
    // 验证 V1 用户密码
    const bcrypt = await import('bcrypt');
    const isValid = await bcrypt.compare(password, legacyUser.passwordHash);
    if (!isValid) {
      throw new UnauthorizedException('密码错误');
    }

    // 创建 V2 用户（保留原 accountSequence）
    const user = UserAggregate.reconstitute({
      phone: legacyUser.phone,
      passwordHash: legacyUser.passwordHash,
      accountSequence: legacyUser.accountSequence,
      status: 'ACTIVE' as any,
      kycStatus: 'PENDING' as any,
      loginFailCount: 0,
    });

    user.recordLoginSuccess(ipAddress);
    const savedUser = await this.userRepository.save(user);

    // 标记 V1 用户已迁移
    await this.syncedLegacyUserRepository.markAsMigrated(legacyUser.accountSequence);

    // 发布迁移事件
    await this.outboxService.publish(
      new LegacyUserMigratedEvent(
        legacyUser.accountSequence.value,
        legacyUser.phone.value,
        legacyUser.nickname || '',
        new Date(),
      ),
    );

    return this.generateTokens(savedUser, deviceInfo, ipAddress);
  }

  /**
   * 执行用户迁移（通用）
   */
  private async performMigration(
    legacyUser: { accountSequence: AccountSequence; phone: Phone; passwordHash: string; nickname: string | null },
    deviceInfo?: string,
    ipAddress?: string,
  ): Promise<LoginResult> {
    const user = UserAggregate.reconstitute({
      phone: legacyUser.phone,
      passwordHash: legacyUser.passwordHash,
      accountSequence: legacyUser.accountSequence,
      status: 'ACTIVE' as any,
      kycStatus: 'PENDING' as any,
      loginFailCount: 0,
    });

    user.recordLoginSuccess(ipAddress);
    const savedUser = await this.userRepository.save(user);
    await this.syncedLegacyUserRepository.markAsMigrated(legacyUser.accountSequence);

    await this.outboxService.publish(
      new LegacyUserMigratedEvent(
        legacyUser.accountSequence.value,
        legacyUser.phone.value,
        legacyUser.nickname || '',
        new Date(),
      ),
    );

    return this.generateTokens(savedUser, deviceInfo, ipAddress);
  }

  /**
   * 刷新 token
   */
  async refreshToken(token: string): Promise<{ accessToken: string; expiresIn: number }> {
    const tokenData = await this.refreshTokenRepository.findByToken(token);
    if (!tokenData || tokenData.revokedAt || tokenData.expiresAt < new Date()) {
      throw new UnauthorizedException('无效的刷新令牌');
    }

    const user = await this.userRepository.findById(tokenData.userId);
    if (!user || !user.canLogin) {
      throw new UnauthorizedException('账户不可用');
    }

    // 检查 LOGIN 能力是否被限制
    const loginEnabled = await this.capabilityService.isCapabilityEnabled(
      user.accountSequence.value,
      Capability.LOGIN,
    );
    if (!loginEnabled) {
      throw new ForbiddenException('您的登录功能已被限制，请联系客服');
    }

    const accessToken = this.generateAccessToken(user);
    const expiresIn = this.configService.get<number>('JWT_EXPIRES_IN_SECONDS', 3600);

    return { accessToken, expiresIn };
  }

  /**
   * 登出
   */
  async logout(refreshToken: string): Promise<void> {
    await this.refreshTokenRepository.revoke(refreshToken);
  }

  /**
   * 登出所有设备
   */
  async logoutAll(userId: bigint): Promise<void> {
    await this.refreshTokenRepository.revokeAllByUserId(userId);
  }

  /**
   * 生成 tokens
   */
  private async generateTokens(
    user: UserAggregate,
    deviceInfo?: string,
    ipAddress?: string,
  ): Promise<LoginResult> {
    const accessToken = this.generateAccessToken(user);
    const refreshToken = await this.generateRefreshToken(user, deviceInfo, ipAddress);
    const expiresIn = this.configService.get<number>('JWT_EXPIRES_IN_SECONDS', 3600);

    return {
      accessToken,
      refreshToken,
      expiresIn,
      user: {
        accountSequence: user.accountSequence.value,
        phone: user.phone.value,
        source: user.source,
        kycStatus: user.kycStatus,
      },
    };
  }

  /**
   * 生成 access token
   */
  private generateAccessToken(user: UserAggregate): string {
    const payload = {
      sub: user.accountSequence.value,
      type: 'access',
      userId: user.accountSequence.value,
      accountSequence: user.accountSequence.value,
      phone: user.phone.value,
      source: user.source,
    };
    return this.jwtService.sign(payload);
  }

  /**
   * 生成 refresh token
   */
  private async generateRefreshToken(
    user: UserAggregate,
    deviceInfo?: string,
    ipAddress?: string,
  ): Promise<string> {
    const crypto = await import('crypto');
    const token = crypto.randomBytes(64).toString('hex');
    const expiresInDays = this.configService.get<number>('JWT_REFRESH_EXPIRES_DAYS', 30);
    const expiresAt = new Date(Date.now() + expiresInDays * 24 * 60 * 60 * 1000);

    await this.refreshTokenRepository.create({
      userId: user.id!,
      token,
      deviceInfo,
      ipAddress,
      expiresAt,
    });

    return token;
  }
}
