/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
import {
  CreateCorteCajaDto,
  UpdateCorteCajaDto,
} from '@dtos/create-corte-caja.dto';
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { CashCountDocument } from '@schemas/cash-count.schema';
import { ComisionDocument } from '@schemas/comision.schema';
import { SaleDocument } from '@schemas/sale.schema';
import { UserDocument } from '@schemas/user.schema';
import { Model } from 'mongoose';

@Injectable()
export class CashCountService {
  constructor(
    @InjectModel('users') private userModel: Model<UserDocument>,
    @InjectModel('sales') private saleModel: Model<SaleDocument>,
    @InjectModel('cashcount')
    private readonly corteCajaModel: Model<CashCountDocument>,
    @InjectModel('comision')
    private readonly comisionModel: Model<ComisionDocument>,
  ) {}

  async create(dto: CreateCorteCajaDto, userId: string) {
    const user = await this.userModel.findById(userId).exec();
    if (!user) {
      throw new HttpException('Datos incorrectos', HttpStatus.NOT_FOUND);
    }

    const now = new Date();

    // Obtener la hora UTC actual y restar 6 horas para convertirla a hora de México
    const mexicoOffset = -6;
    const mexicoNow = new Date(now.getTime() + mexicoOffset * 60 * 60 * 1000);

    // Obtener inicio del día en hora de México
    const startOfDay = new Date(
      Date.UTC(
        mexicoNow.getUTCFullYear(),
        mexicoNow.getUTCMonth(),
        mexicoNow.getUTCDate(),
        6, // 00:00 en México equivale a 06:00 UTC
        0,
        0,
        0,
      ),
    );

    // Obtener fin del día en hora de México
    const endOfDay = new Date(
      Date.UTC(
        mexicoNow.getUTCFullYear(),
        mexicoNow.getUTCMonth(),
        mexicoNow.getUTCDate(),
        6 + 23, // 23:59:59.999 en México equivale a 05:59:59.999 UTC del día siguiente
        59,
        59,
        999,
      ),
    );

    const existe = await this.corteCajaModel.findOne({
      businessId: user.businessId,
      turno: dto.turno,
      createdAt: {
        $gte: startOfDay,
        $lte: endOfDay,
      },
    });

    if (existe) {
      throw new HttpException('Ya existe el corte', HttpStatus.CONFLICT);
    }

    const corte = await this.corteCajaModel.create(dto);

    const fechaCorte = corte.createdAt; // fecha real
    const turno = corte.turno;

    await this.saleModel.updateMany(
      {
        businessId: corte.businessId,
        turno_terminado: '',
        createdAt: {
          $gte: startOfDay,
          $lte: fechaCorte,
        },
      },
      {
        $set: {
          turno_terminado: turno,
          cashcountId: corte._id,
        },
      },
    );

    return await this.getCommissionsTurn(corte);
  }

  private async getCommissionsTurn(
    corte: CashCountDocument,
  ): Promise<CashCountDocument> {
    const start = new Date(corte.createdAt!);
    start.setHours(0, 0, 0, 0);

    const end = new Date(corte.createdAt!);
    end.setHours(23, 59, 59, 999);

    const comision = await this.saleModel.aggregate([
      {
        $match: {
          businessId: corte.businessId,
          createdAt: {
            $gte: start,
            $lte: end,
          },
          turno_terminado: { $eq: corte.turno },
          cancel: { $ne: true },
        },
      },
      { $unwind: '$products' },
      {
        $lookup: {
          from: 'products',
          localField: 'products.productId',
          foreignField: '_id',
          as: 'productInfo',
        },
      },
      { $unwind: '$productInfo' },
      {
        $addFields: {
          productCommission: {
            $multiply: [
              { $multiply: ['$products.costo_venta', '$products.cantidad'] },
              { $divide: ['$productInfo.comision', 100] },
            ],
          },
        },
      },
      {
        $group: {
          _id: { venta: '$_id', turno: '$turno_terminado' },
          totalSale: { $first: '$total' },
          createdAt: { $first: '$createdAt' },
          payment_type: { $first: '$payment_type' },
          commissionTotal: { $sum: '$productCommission' },
          products: {
            $push: {
              productId: '$products.productId',
              identificador: '$productInfo.identificador',
              categoria: '$products.categoria',
              clave: '$products.clave',
              sorteo: '$products.sorteo',
              fecha: '$products.fecha',
              cantidad: '$products.cantidad',
              costo_venta: '$products.costo_venta',
              comision: '$productInfo.comision',
              commission: '$productCommission',
            },
          },
        },
      },
      { $sort: { createdAt: -1 } },
      {
        $group: {
          _id: '$_id.turno',
          ventas: {
            $push: {
              ventaId: '$_id.venta',
              totalSale: '$totalSale',
              createdAt: '$createdAt',
              payment_type: '$payment_type',
              commissionTotal: '$commissionTotal',
              products: '$products',
            },
          },
          commissionTotalTurno: { $sum: '$commissionTotal' },
        },
      },
      { $sort: { _id: 1 } },
    ]);

    await this.comisionModel.create({
      cashcountId: corte._id,
      businessId: corte.businessId,
      turno: corte.turno,
      ventas: comision[0].ventas,
      commissionTotalTurno: comision[0].commissionTotalTurno,
    });

    return corte;
  }

  async findAll(userId: string, startDate: string, endDate: string) {
    const user = await this.userModel.findById(userId).exec();
    if (!user) {
      throw new HttpException('Datos incorrectos', HttpStatus.NOT_FOUND);
    }

    const selectedDate = new Date(startDate + 'T00:00:00-06:00'); // Inicio del día en hora de México
    const nextDate = new Date(endDate + 'T23:59:59.999-06:00');

    return this.corteCajaModel.aggregate([
      {
        $match: {
          businessId: user.businessId,
          createdAt: {
            $gte: selectedDate,
            $lte: nextDate,
          },
        },
      },
      {
        $lookup: {
          from: 'comisions',
          localField: '_id',
          foreignField: 'cashcountId',
          as: 'comisionInfo',
        },
      },
      {
        $sort: { createdAt: -1 },
      },
    ]);
  }

  async findOne(id: string) {
    const found = await this.corteCajaModel.findById(id).exec();
    if (!found)
      throw new HttpException('Datos incorrectos', HttpStatus.NOT_FOUND);
    return found;
  }

  async update(id: string, dto: UpdateCorteCajaDto) {
    const updated = await this.corteCajaModel
      .findByIdAndUpdate(id, dto, { new: true })
      .exec();
    if (!updated)
      throw new HttpException('Datos incorrectos', HttpStatus.NOT_FOUND);
    return updated;
  }

  async remove(id: string) {
    await this.saleModel.updateMany(
      {
        cashcountId: id,
      },
      {
        $set: {
          turno_terminado: '',
        },
        $unset: {
          cashcountId: null,
        },
      },
    );

    const deleted = await this.corteCajaModel.findByIdAndDelete(id).exec();
    if (!deleted)
      throw new HttpException('Datos incorrectos', HttpStatus.NOT_FOUND);
    return deleted;
  }
}
