import { DIR } from '@common/constants/game-types.constant';
import { CreateProductDto, UpdateProductDto } from '@dtos/create-product.dto';
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Product, ProductDocument } from '@schemas/product.schema';
import { UserDocument } from '@schemas/user.schema';
import { MultimediaService } from '@services/multimedia.service';
import { isValidObjectId, Model } from 'mongoose';
import { Types } from 'mongoose';

@Injectable()
export class ProductsService {
  private dir = DIR + '/products/';
  constructor(
    @InjectModel('products')
    private readonly productModel: Model<ProductDocument>,
    @InjectModel('users') private userModel: Model<UserDocument>,
    private readonly mediaService: MultimediaService,
  ) {}

  async create(
    photo: Express.Multer.File,
    createDto: CreateProductDto,
    userId: string,
  ): Promise<Product> {
    const user = await this.userModel.findById(userId).exec();
    if (!user) {
      throw new HttpException('Datos incorrectos', HttpStatus.NOT_FOUND);
    }

    const exists = await this.productModel
      .find({ 
        businessId: user.businessId,
        identificador: createDto.identificador 
      })
      .exec();

    if (exists && exists.length > 0) {
      throw new HttpException('Producto existente', HttpStatus.CONFLICT);
    }

    let result = 'default.jpg';
    if (photo) {
      result = await this.mediaService.saveImage(photo, this.dir);
    }
    return this.productModel.create({
      ...createDto,
      photo: result,
    });
  }

  async findAll(userId: string): Promise<Product[]> {
    const user = await this.userModel.findById(userId).exec();
    if (!user) {
      throw new HttpException('Datos incorrectos', HttpStatus.NOT_FOUND);
    }

    return await this.productModel
      .find({ businessId: user.businessId })
      .sort({ createdAt: -1 })
      .populate('userId')
      .exec();
  }

  async findOne(id: string): Promise<Product> {
    const product = await this.productModel.findById(id).populate('userId');
    if (!product)
      throw new HttpException('Producto no encontrado', HttpStatus.NOT_FOUND);
    return product;
  }

  async updateOne(
    photo: Express.Multer.File,
    image: string,
    del: boolean,
  ): Promise<any> {
    if (del) {
      await this.mediaService.removeImage(this.dir, image);
    }

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

    return { photo: result };
  }

  async update(
    photo: Express.Multer.File,
    updateDto: UpdateProductDto,
    del: boolean,
  ): Promise<Product> {
    const exists: ProductDocument | null = await this.productModel
      .findOne({ identificador: updateDto.identificador })
      .exec();

    if (
      exists &&
      (exists._id as Types.ObjectId).toString() !== updateDto._id.toString()
    ) {
      throw new HttpException('Producto ya existente', HttpStatus.CONFLICT);
    }

    if (del) {
      await this.mediaService.removeImage(this.dir, updateDto.photo!);
      updateDto = {
        ...updateDto,
        photo: 'default.jpg',
      };
    }

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

    const product = await this.productModel.findByIdAndUpdate(
      updateDto._id,
      updateDto,
      {
        new: true,
      },
    );
    if (!product)
      throw new HttpException('Producto no encontrado', HttpStatus.NOT_FOUND);
    return product;
  }

  async removeMany(ids: string[]): Promise<void> {
    for (const id of ids) {
      if (!isValidObjectId(id)) {
        throw new HttpException(
          'ID de producto inválido',
          HttpStatus.BAD_REQUEST,
        );
      }

      const deleted = await this.productModel.findById(id).exec();
      if (!deleted) {
        throw new HttpException('El producto no existe', HttpStatus.NOT_FOUND);
      }

      await this.mediaService.removeImage(this.dir, deleted.photo);
      await this.productModel.findByIdAndDelete(id);
    }
  }

  async removeAll(userId: string): Promise<void> {
    const user = await this.userModel.findById(userId).exec();
    if (!user) {
      throw new HttpException('Datos incorrectos', HttpStatus.NOT_FOUND);
    }

    await this.productModel.deleteMany({ businessId: user.businessId });
  }

  async remove(id: string): Promise<void> {
    if (!isValidObjectId(id)) {
      throw new HttpException(
        'ID de producto inválido',
        HttpStatus.BAD_REQUEST,
      );
    }

    const deleted = await this.productModel.findById(id).exec();
    if (!deleted) {
      throw new HttpException('El producto no existe', HttpStatus.NOT_FOUND);
    }

    await this.mediaService.removeImage(this.dir, deleted.photo);
    await this.productModel.findByIdAndDelete(id);
  }
}
