import {
  Injectable,
  UnauthorizedException,
  BadRequestException,
} from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import * as bcrypt from 'bcrypt';
import { UserService } from '../user/user.service';
import { User } from '../user/entities/user.entity';
import { MailsService } from '../mails/mails.service';

@Injectable()
export class AuthService {
  constructor(
    private readonly userService: UserService,
    private readonly jwtService: JwtService,
    private readonly mailsService: MailsService,
  ) {}

  // ✅ Generate 6-digit OTP
  private generateOtp(): string {
    return Math.floor(100000 + Math.random() * 900000).toString();
  }

  // ✅ Register
  async register(createUserDto: any) {
    const existing = await this.userService.findByEmail(createUserDto.email);
    if (existing) throw new BadRequestException('Email already registered.');

    const password = await this.hashPassword(createUserDto.password);
    const otpCode = this.generateOtp();
    const otpExpires = new Date(Date.now() + 10 * 60 * 1000);

    const user = await this.userService.create({
      ...createUserDto,
      password,
      otpCode,
      otpExpires,
      otpAttempts: 0,
      otpType: 'verify',
      isEmailVerified: false,
      isApprovedByAdmin: false,
      status: 'pending',
    });

    await this.mailsService.sendOtpEmail(
      user.email,
      otpCode,
      `${user.firstName} ${user.lastName}`,
      'verify',
    );

    return {
      message: 'Registration successful. Check your email for the OTP code.',
      email: user.email,
    };
  }

  // ✅ Verify email OTP
  async verifyEmailOtp(email: string, otp: string) {
    const user = await this.userService.findByEmail(email);
    if (!user) throw new BadRequestException('User not found.');

    if (user.isEmailVerified)
      throw new BadRequestException('Email already verified.');

    if (user.otpAttempts >= 5)
      throw new BadRequestException(
        'Too many attempts. Please register again.',
      );

    if (user.otpCode !== otp) {
      await this.userService.update(user.id, {
        otpAttempts: user.otpAttempts + 1,
      });
      throw new BadRequestException('Invalid OTP code.');
    }

    if (new Date() > user.otpExpires!)
      throw new BadRequestException('OTP expired. Please request a new one.');

    await this.userService.update(user.id, {
      isEmailVerified: true,
      otpCode: null,
      otpExpires: null,
      otpAttempts: 0,
      otpType: null,
    });

    return {
      message: 'Email verified! Your account is pending admin approval.',
    };
  }

  // ✅ Resend OTP
  async resendOtp(email: string, type: 'verify' | 'reset') {
    const user = await this.userService.findByEmail(email);
    if (!user) throw new BadRequestException('User not found.');

    const otpCode = this.generateOtp();
    const otpExpires = new Date(Date.now() + 10 * 60 * 1000);

    await this.userService.update(user.id, {
      otpCode,
      otpExpires,
      otpAttempts: 0,
      otpType: type,
    });

    await this.mailsService.sendOtpEmail(
      user.email,
      otpCode,
      `${user.firstName} ${user.lastName}`,
      type,
    );

    return { message: 'New OTP sent to your email.' };
  }

  // ✅ Forgot password
  async forgotPassword(email: string) {
    const user = await this.userService.findByEmail(email);
    if (!user)
      return { message: 'If this email exists, an OTP has been sent.' };

    const otpCode = this.generateOtp();
    const otpExpires = new Date(Date.now() + 10 * 60 * 1000);

    await this.userService.update(user.id, {
      otpCode,
      otpExpires,
      otpAttempts: 0,
      otpType: 'reset',
    });

    await this.mailsService.sendOtpEmail(
      user.email,
      otpCode,
      `${user.firstName} ${user.lastName}`,
      'reset',
    );

    return { message: 'OTP sent to your email.' };
  }

  // ✅ Verify reset OTP
  async verifyResetOtp(email: string, otp: string) {
    const user = await this.userService.findByEmail(email);
    if (!user) throw new BadRequestException('User not found.');

    if (user.otpAttempts >= 5)
      throw new BadRequestException(
        'Too many attempts. Please request a new OTP.',
      );

    if (user.otpCode !== otp || user.otpType !== 'reset') {
      await this.userService.update(user.id, {
        otpAttempts: user.otpAttempts + 1,
      });
      throw new BadRequestException('Invalid OTP code.');
    }

    if (new Date() > user.otpExpires!)
      throw new BadRequestException('OTP expired. Please request a new one.');

    await this.userService.update(user.id, {
      otpCode: null,
      otpExpires: null,
      otpAttempts: 0,
      otpType: null,
    });

    return { message: 'OTP verified. You can now reset your password.' };
  }

  // ✅ Reset password
  async resetPassword(email: string, newPassword: string) {
    const user = await this.userService.findByEmail(email);
    if (!user) throw new BadRequestException('User not found.');

    const password = await this.hashPassword(newPassword);
    await this.userService.update(user.id, { password });

    // ✅ Send confirmation email
    await this.mailsService.sendPasswordChangedEmail(
      user.email,
      `${user.firstName} ${user.lastName}`,
    );

    return { message: 'Password reset successfully. You can now login.' };
  }

  // ✅ Login
  async validateUser(email: string, password: string): Promise<User> {
    const user = await this.userService.findByEmail(email);
    if (!user) throw new UnauthorizedException('Invalid credentials.');

    const passwordValid = await bcrypt.compare(password, user.password);
    if (!passwordValid) throw new UnauthorizedException('Invalid credentials.');

    if (!user.isEmailVerified)
      throw new UnauthorizedException('Please verify your email first.');

    if (!user.isApprovedByAdmin)
      throw new UnauthorizedException(
        'Your account is pending admin approval.',
      );

    return user;
  }

  async login(user: User) {
    console.log('🔑 Signing with secret:', !!process.env.JWT_SECRET); // ✅ add
    const fullUser = await this.userService.findOne(user.id);
    const payload = {
      email: fullUser.email,
      sub: fullUser.id,
      roleIds: (fullUser.roles || []).map((r) => r.id),
      roleNames: (fullUser.roles || []).map((r) => r.name),
      // 👇 NEW — brand scheme (QRS or TQS) so the Navbar/Sidebar can show correct branding.
      //         Falls back to 'QRS' if the user doesn't have a primary_scheme set.
      primary_scheme: (fullUser as any).primary_scheme || 'QRS',
    };
    return {
      access_token: this.jwtService.sign(payload),
      user: {
        id: fullUser.id,
        email: fullUser.email,
        firstName: fullUser.firstName,
        lastName: fullUser.lastName,
        roles: fullUser.roles,
        // 👇 NEW — included in user object too so the auth store can read it directly
        primary_scheme: (fullUser as any).primary_scheme || 'QRS',
      },
    };
  }

  async hashPassword(password: string): Promise<string> {
    const salt = await bcrypt.genSalt();
    return bcrypt.hash(password, salt);
  }

  // ✅ Admin approve user
  async approveUser(id: number) {
    const user = await this.userService.findOne(id);
    await this.userService.update(id, {
      isApprovedByAdmin: true,
      status: 'active',
    });
    await this.mailsService.sendApprovalEmail(
      user.email,
      `${user.firstName} ${user.lastName}`,
    );
    return { message: 'User approved successfully.' };
  }

  // ✅ Admin reject user
  async rejectUser(id: number) {
    await this.userService.update(id, {
      status: 'rejected',
      isActive: false,
    });
    return { message: 'User rejected.' };
  }
}