/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/no-unsafe-call */
import {
  ConflictException,
  HttpException,
  HttpStatus,
  Injectable,
} from '@nestjs/common';
import * as bcrypt from 'bcryptjs';
import { JwtService } from '@nestjs/jwt';
import { InjectModel } from '@nestjs/mongoose';
import { UserDocument } from '@schemas/user.schema';
import { isValidObjectId, Model } from 'mongoose';
import { CreateUserDto, LoginDto } from '@dtos/create-user.dto';
import { CryptoService } from 'src/services/crypto.service';
import { EmailService } from '@modules/email/email.service';
import { MultimediaService } from '@services/multimedia.service';
import { UserLogs, UserLogsDocument } from '@schemas/user-logs.schema';
import { Request } from 'express';

@Injectable()
export class AuthService {
  private dir = './media/users/';

  constructor(
    @InjectModel('users') private userModel: Model<UserDocument>,
    @InjectModel('userlogs') private userlogsModel: Model<UserLogsDocument>,
    private jwtService: JwtService,
    private readonly cryptoService: CryptoService,
    private readonly emailService: EmailService,
    private readonly mediaService: MultimediaService,
  ) {}

  async register(file: Express.Multer.File, createUserDto: CreateUserDto) {
    const email = createUserDto.email.toLowerCase().trim();
    const password = createUserDto.password;

    const existingUser = await this.userModel.findOne({ email });
    if (existingUser) {
      throw new ConflictException('El usuario ya existe');
    }

    let result = 'default.jpg';
    if (file) {
      result = await this.mediaService.saveImage(file, this.dir);
    }

    const hashedPassword = await bcrypt.hash(password, 10);
    const user = new this.userModel({
      email,
      password: hashedPassword,
      photo: result,
      role: createUserDto.role ?? 'user',
      //businessId: createUserDto.businessId,
    });
    await user.save();

    return { message: 'Usuario registrado con éxito' };
  }

  async login(loginDto: LoginDto, req: Request) {
    const { email, password } = loginDto;
    const user = await this.userModel.findOne({ email });
    if (!user) {
      throw new HttpException(
        'Credenciales incorrectas',
        HttpStatus.BAD_REQUEST,
      );
    }

    if (!user.active) {
      throw new HttpException(
        'Cuenta bloqueada o inactiva',
        HttpStatus.FORBIDDEN,
      );
    }

    const isPasswordValid = await bcrypt.compare(password, user.password);
    if (!isPasswordValid) {
      throw new HttpException(
        'Credenciales incorrectas',
        HttpStatus.BAD_REQUEST,
      );
    }

    if (req) {
      await this.logAccess(
        user.businessId as unknown as string,
        user._id as string,
        'login',
        req,
      );
    }
    const payload = { email: user.email, sub: user._id, role: user.role };
    return { access_token: this.jwtService.sign(payload) };
  }

  private async logAccess(
    businessId: string,
    userId: string,
    access: 'login' | 'logout',
    req: Request,
  ): Promise<UserLogs> {
    const ip = this.getClientIp(req);
    const log = new this.userlogsModel({ businessId, userId, ip, access });
    return log.save();
  }

  private getClientIp(req: Request): string {
    const forwarded = req.headers['x-forwarded-for'];
    return typeof forwarded === 'string'
      ? forwarded.split(',')[0].trim()
      : req.socket?.remoteAddress || '0.0.0.0';
  }

  async recoveryStep1(recoverUserDto: any) {
    const email = recoverUserDto.email!.toLowerCase().trim();

    const user = await this.userModel.findOne({ email: email });
    if (!user) {
      throw new HttpException('Usuario no encontrado', HttpStatus.BAD_REQUEST);
    }

    const unix = Math.floor(Date.now() / 1000);
    const payload = this.cryptoService.encrypt(
      JSON.stringify({ email: user.email, sub: user._id }),
    );

    return await this.emailService.sendEmail(
      user.email,
      'Recuperar cuenta',
      `
        <div style="max-width: 600px; margin: auto; padding: 20px; font-family: Arial, sans-serif; color: #2d3748; border: 1px solid #e2e8f0; border-radius: 10px;">
            <div style="text-align: center; margin-bottom: 20px;">
            <h2 style="color: #38a169;">🔐 Recuperar tu cuenta</h2>
            </div>
            <p style="font-size: 16px; text-align: center;">
            Para continuar con el proceso de recuperación de tu cuenta, haz clic en el siguiente botón:
            </p>
            <div style="text-align: center; margin: 30px 0;">
            <a href="${process.env.URL_FRONT}/welcome/confirm-recovery/${unix}/${payload}"
                target="_blank"
                style="background-color: #38a169; color: white; text-decoration: none; padding: 12px 24px; border-radius: 6px; display: inline-block;">
                Recuperar cuenta
            </a>
            </div>
            <p style="font-size: 14px; color: #718096; text-align: center;">
            Si no solicitaste esta acción, puedes ignorar este mensaje.
            </p>
            <div style="margin-top: 40px; border-top: 1px solid #e2e8f0; padding-top: 10px; text-align: center; font-size: 12px; color: #a0aec0;">
            POS • CtrlClick
            </div>
        </div>
        `,
    );
  }

  async recoveryStep2(createUserDto: any) {
    const { payload, password } = createUserDto;
    const data = JSON.parse(this.cryptoService.decrypt(payload ?? ''));

    if (!isValidObjectId(data.sub)) {
      throw new HttpException('ID de usuario inválido', HttpStatus.BAD_REQUEST);
    }

    const user: any = await this.userModel.findById(data.sub);
    if (!user) {
      throw new HttpException('Usuario no encontrado', HttpStatus.BAD_REQUEST);
    }

    const hashedPassword = await bcrypt.hash(password!, 10);
    user.password = hashedPassword;
    return user.save();
  }
}
