import { CreateSaleDto, UpdateSaleDto } from '@dtos/create-sale.dto';
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { ProductDocument } from '@schemas/product.schema';
import { Sale, SaleDocument } from '@schemas/sale.schema';
import { ShoppingDocument } from '@schemas/shopping.schema';
import { UserDocument } from '@schemas/user.schema';
import { Model } from 'mongoose';

@Injectable()
export class SalesService {
  constructor(
    @InjectModel('shoppings')
    private readonly shoppingModel: Model<ShoppingDocument>,
    @InjectModel('users') private userModel: Model<UserDocument>,
    @InjectModel('sales') private saleModel: Model<SaleDocument>,
    @InjectModel('products')
    private readonly productModel: Model<ProductDocument>,
  ) {}

  async create(createSaleDto: CreateSaleDto): Promise<Sale> {
    const createdSale = new this.saleModel(createSaleDto);
    return createdSale.save();
  }

  async findAll(): Promise<Sale[]> {
    return this.saleModel.find().exec();
  }

  async findOne(id: string): Promise<Sale> {
    const sale = await this.saleModel.findById(id).exec();
    if (!sale) {
      throw new HttpException('No encontrado', HttpStatus.NOT_FOUND);
    }
    return sale;
  }

  async update(id: string, updateSaleDto: UpdateSaleDto): Promise<Sale> {
    const updated = await this.saleModel
      .findByIdAndUpdate(id, updateSaleDto, { new: true })
      .exec();
    if (!updated) {
      throw new HttpException('No encontrado', HttpStatus.NOT_FOUND);
    }
    return updated;
  }

  async remove(id: string): Promise<void> {
    const result = await this.saleModel.findByIdAndDelete(id).exec();
    if (!result) {
      throw new HttpException('No encontrado', HttpStatus.NOT_FOUND);
    }
  }

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

    const result = await this.saleModel
      .deleteMany({ businessId: user.businessId })
      .exec();
    if (!result) {
      throw new HttpException('No encontrados', HttpStatus.NOT_FOUND);
    }
  }

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

    const now = new Date();
    const todayUtc = new Date(Date.UTC(
      now.getUTCFullYear(),
      now.getUTCMonth(),
      now.getDate(), 
      0, 0, 0, 0
    ));

    console.log(todayUtc);

    const result = await this.saleModel.aggregate([
      {
        $match: {
          businessId: user.businessId, // opcional
          $or: [{ cancel: { $exists: false } }, { cancel: false }],
        },
      },
      {
        $unwind: '$products',
      },
      {
        $match: {
          $or: [
            { 'products.fecha': { $exists: false } },
            { 'products.fecha': null },
            { 'products.fecha': { $gte: todayUtc } }, // productos no caducados
          ],
        },
      },
      {
        $group: {
          _id: {
            clave: '$products.clave',
            sorteo: '$products.sorteo',
          },
          totalCantidad: { $sum: '$products.cantidad' },
          totalCostoVenta: {
            $sum: {
              $multiply: ['$products.cantidad', '$products.costo_venta'],
            },
          },
          categoria: { $first: '$products.categoria' },
          code_bar: { $first: '$products.code_bar' },
          productId: { $first: '$products.productId' },
          fecha: { $first: '$products.fecha' },
        },
      },
      {
        $project: {
          _id: 0,
          clave: '$_id.clave',
          sorteo: '$_id.sorteo',
          cantidad: '$totalCantidad',
          costo_venta: '$totalCostoVenta',
          categoria: 1,
          code_bar: 1,
          productId: 1,
          fecha: 1,
        },
      },
    ]);

    return result;
  }

  //Funcion para obtener productos *intangibles para punto de venta
  async findAllNoDateProducts(userId: string): Promise<any[]> {
    const user = await this.userModel.findById(userId).exec();
    if (!user) {
      throw new HttpException('Datos incorrectos', HttpStatus.NOT_FOUND);
    }

    const result = await this.shoppingModel.aggregate([
      {
        $match: {
          businessId: user.businessId, // opcional
        },
      },
      {
        $unwind: '$products',
      },
      {
        $match: {
          $or: [
            { 'products.fecha': { $exists: false } },
            { 'products.fecha': null },
          ],
          'products.sorteo': { $ne: '' },
        },
      },
      {
        $group: {
          _id: {
            sorteo: '$products.sorteo',
            productId: '$products.productId',
          },
          totalDotacion: { $sum: '$products.dotacion' },
          totalCosto: { $sum: '$products.costo_total' },
          totalPrecio: { $sum: '$products.precio_total' },
          totalPagoAnticipado: { $sum: '$products.pago_anticipado' },
          totalSaldoPorPagar: { $sum: '$products.saldo_x_pagar' },
          totalDevolucion: { $sum: '$products.devolucion' },
          sorteo: { $first: '$products.sorteo' },
          photo: { $first: '$products.photo' },
          fecha: { $first: '$products.fecha' },
        },
      },
      {
        $lookup: {
          from: 'products',
          localField: '_id.productId', // ✅ corregido
          foreignField: '_id',
          as: 'productDetails',
        },
      },
      {
        $unwind: '$productDetails',
      },
      {
        $project: {
          _id: 0,
          productId: '$_id.productId', // ✅ corregido
          sorteo: '$_id.sorteo',
          photo: '$photo',
          fecha: 1,
          cantidad: { $literal: 0 },
          costo_venta: { $literal: 0 },
          devolucion: '$totalDevolucion',
          dotacion: '$totalDotacion',
          costo_total: '$totalCosto',
          precio_total: '$totalPrecio',
          pago_anticipado: '$totalPagoAnticipado',
          saldo_x_pagar: '$totalSaldoPorPagar',
          productInfo: {
            _id: '$productDetails._id',
            clave: '$productDetails.clave',
            categoria: '$productDetails.categoria',
            code_bar: '$productDetails.code_bar',
            comision: '$productDetails.comision',
            observaciones: '$productDetails.observaciones',
            photo: '$productDetails.photo',
            exists: '$productDetails.exists',
            bet: '$productDetails.bet',
          },
        },
      },
    ]);

    return result;
  }

  //Funcion para obtener productos *intangibles para punto de venta
  async findAllNoProducts(userId: string): Promise<any[]> {
    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, exists: false })
      .sort({ createdAt: -1 })
      .populate('userId')
      .exec();
  }

  //Funcion para obtener productos *tangibles para punto de venta
  async findAllProducts(userId: string): Promise<any[]> {
    const user = await this.userModel.findById(userId).exec();
    if (!user) {
      throw new HttpException('Datos incorrectos', HttpStatus.CONFLICT);
    }

    const now = new Date();
    const startOfDayUTC = new Date(
      Date.UTC(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0, 0),
    );

    const result = await this.shoppingModel.aggregate([
      { $match: { businessId: user.businessId } },

      // Descomponer productos
      { $unwind: '$products' },

      // Filtrar por fecha de productos
      {
        $match: {
          'products.fecha': { $gte: startOfDayUTC },
        },
      },

      // Join manual con products
      {
        $lookup: {
          from: 'products',
          localField: 'products.productId',
          foreignField: '_id',
          as: 'productInfo',
        },
      },
      { $unwind: '$productInfo' },

      // Agrupar por fecha + sorteo + productId
      {
        $group: {
          _id: {
            fecha: '$products.fecha',
            sorteo: '$products.sorteo',
            productId: '$products.productId',
          },
          dotacionTotal: { $sum: '$products.dotacion' },
          precioTotal: { $sum: '$products.precio_total' },
          devolucionTotal: { $sum: '$products.devolucion' },
          photo: { $first: '$products.photo' },

          // Puedes tomar los campos que no se repiten del producto
          productInfo: { $first: '$productInfo' },
        },
      },

      // Dar formato a la salida
      {
        $project: {
          _id: 0,
          fecha: '$_id.fecha',
          sorteo: '$_id.sorteo',
          photo: '$photo',
          productId: '$_id.productId',
          dotacion: '$dotacionTotal',
          precio_total: '$precioTotal',
          devolucion: '$devolucionTotal',
          productInfo: {
            identificador: '$productInfo.identificador',
            categoria: '$productInfo.categoria',
            clave: '$productInfo.clave',
            code_bar: '$productInfo.code_bar',
            observaciones: '$productInfo.observaciones',
            comision: '$productInfo.comision',
            photo: '$productInfo.photo',
            exists: '$productInfo.exists',
            bet: '$productInfo.bet',
          },
        },
      },
    ]);

    return result;
  }

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

    // Fecha actual (inicio del día en UTC)
    const now = new Date();
    const startOfDayUTC = new Date(
      Date.UTC(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0, 0),
    );

    // Fecha límite inferior (hace 60 días)
    const sixtyDaysAgoUTC = new Date(startOfDayUTC);
    sixtyDaysAgoUTC.setUTCDate(sixtyDaysAgoUTC.getUTCDate() - 60);

    const endOfTodayUTC = new Date(
      Date.UTC(
        now.getFullYear(),
        now.getMonth(),
        now.getDate(),
        23,
        59,
        59,
        999,
      ),
    );

    const result = await this.shoppingModel.aggregate([
      { $match: { businessId: user.businessId } },

      // Descomponer productos
      { $unwind: '$products' },

      // Filtrar por fecha de sorteo entre hace 60 días y hoy
      {
        $match: {
          'products.fecha': {
            $gte: sixtyDaysAgoUTC,
            $lte: endOfTodayUTC,
          },
        },
      },

      // Join manual con products
      {
        $lookup: {
          from: 'products',
          localField: 'products.productId',
          foreignField: '_id',
          as: 'productInfo',
        },
      },
      { $unwind: '$productInfo' },

      // Agrupar por fecha + sorteo + productId
      {
        $group: {
          _id: {
            fecha: '$products.fecha',
            sorteo: '$products.sorteo',
            productId: '$products.productId',
          },
          dotacionTotal: { $sum: '$products.dotacion' },
          precioTotal: { $sum: '$products.precio_total' },
          devolucionTotal: { $sum: '$products.devolucion' },
          productInfo: { $first: '$productInfo' },
        },
      },

      {
        $addFields: {
          dias_desde_sorteo: {
            $floor: {
              $divide: [
                { $subtract: [new Date(), '$_id.fecha'] },
                1000 * 60 * 60 * 24, // milisegundos por día
              ],
            },
          },
        },
      },

      // Formato final
      {
        $project: {
          _id: 0,
          fecha: '$_id.fecha',
          sorteo: '$_id.sorteo',
          productId: '$_id.productId',
          dotacion: '$dotacionTotal',
          precio_total: '$precioTotal',
          devolucion: '$devolucionTotal',
          dias_restantes: {
            $subtract: [60, '$dias_desde_sorteo'],
          },
          no_disponible: {
            $gt: ['$dias_desde_sorteo', 60],
          },
          productInfo: {
            categoria: '$productInfo.categoria',
            clave: '$productInfo.clave',
            code_bar: '$productInfo.code_bar',
            observaciones: '$productInfo.observaciones',
            comision: '$productInfo.comision',
            photo: '$productInfo.photo',
            exists: '$productInfo.exists',
            bet: '$productInfo.bet',
          },
        },
      },
    ]);

    return result;
  }
}
