import { Injectable, UnauthorizedException, BadRequestException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, MoreThan } from 'typeorm';
import { JwtService } from '@nestjs/jwt';
import { VerificationCodeEntity } from '../domain/entities/verification-code.entity';
import { UserService } from '../user/user.service';

@Injectable()
export class AuthService {
  constructor(
    @InjectRepository(VerificationCodeEntity)
    private verificationCodeRepo: Repository<VerificationCodeEntity>,
    private userService: UserService,
    private jwtService: JwtService,
  ) {}

  /**
   * Create anonymous session
   */
  async createAnonymousSession(fingerprint: string) {
    if (!fingerprint) {
      throw new BadRequestException('Fingerprint is required');
    }

    const user = await this.userService.createAnonymousUser(fingerprint);

    const token = this.jwtService.sign({
      sub: user.id,
      type: user.type,
    });

    return {
      userId: user.id,
      token,
      expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // 7 days
    };
  }

  /**
   * Send verification code (SMS)
   */
  async sendVerificationCode(phone: string) {
    // Validate phone format
    if (!this.isValidPhone(phone)) {
      throw new BadRequestException('Invalid phone number');
    }

    // Check rate limit (max 5 codes per phone per hour)
    const recentCodes = await this.verificationCodeRepo.count({
      where: {
        phone,
        createdAt: MoreThan(new Date(Date.now() - 60 * 60 * 1000)),
      },
    });

    if (recentCodes >= 5) {
      throw new BadRequestException('Too many verification codes requested');
    }

    // Generate code
    const code = this.generateCode();

    // Save to database
    const expiresAt = new Date(Date.now() + 5 * 60 * 1000); // 5 minutes
    await this.verificationCodeRepo.save({
      phone,
      code,
      expiresAt,
    });

    // TODO: Actually send SMS via Aliyun SMS or other provider
    // For development, just log the code
    console.log(`[DEV] Verification code for ${phone}: ${code}`);

    return {
      sent: true,
      expiresIn: 300, // 5 minutes in seconds
    };
  }

  /**
   * Verify code and login
   */
  async verifyAndLogin(phone: string, code: string, userId?: string) {
    // Find valid verification code
    const verificationCode = await this.verificationCodeRepo.findOne({
      where: {
        phone,
        code,
        isUsed: false,
        expiresAt: MoreThan(new Date()),
      },
      order: { createdAt: 'DESC' },
    });

    if (!verificationCode) {
      throw new UnauthorizedException('Invalid or expired verification code');
    }

    // Mark code as used
    verificationCode.isUsed = true;
    await this.verificationCodeRepo.save(verificationCode);

    // Get or create user
    let user;
    if (userId) {
      // Upgrade anonymous user to registered
      user = await this.userService.upgradeToRegistered(userId, phone);
    } else {
      // Find existing user by phone or create new
      user = await this.userService.findByPhone(phone);
      if (!user) {
        throw new BadRequestException('User not found. Please provide userId to upgrade.');
      }
    }

    // Generate token
    const token = this.jwtService.sign({
      sub: user.id,
      type: user.type,
    });

    return {
      userId: user.id,
      token,
      user: {
        id: user.id,
        type: user.type,
        phone: user.phone,
        nickname: user.nickname,
      },
    };
  }

  /**
   * Refresh token
   */
  async refreshToken(oldToken: string) {
    try {
      const payload = this.jwtService.verify(oldToken, {
        ignoreExpiration: true,
      });

      const user = await this.userService.findById(payload.sub);

      const newToken = this.jwtService.sign({
        sub: user.id,
        type: user.type,
      });

      return {
        token: newToken,
        expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
      };
    } catch (error) {
      throw new UnauthorizedException('Invalid token');
    }
  }

  /**
   * Validate phone format
   */
  private isValidPhone(phone: string): boolean {
    // Chinese phone: 1xx xxxx xxxx
    // HK phone: xxxx xxxx
    const cleanPhone = phone.replace(/[\s-]/g, '');
    return /^1[3-9]\d{9}$/.test(cleanPhone) || /^[2-9]\d{7}$/.test(cleanPhone);
  }

  /**
   * Generate 6-digit verification code
   */
  private generateCode(): string {
    return Math.floor(100000 + Math.random() * 900000).toString();
  }
}
