/* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import {
  CreateShoppingDto,
  UpdateShoppingDto,
} from '@dtos/create-shopping.dto';
import {
  HttpException,
  HttpStatus,
  Injectable,
  NotFoundException,
} from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { SaleDocument } from '@schemas/sale.schema';
import { ShoppingDocument } from '@schemas/shopping.schema';
import { UserDocument } from '@schemas/user.schema';
import { Model } from 'mongoose';
import { Finanzas } from '@schemas/finanzas.schema';

@Injectable()
export class ShoppingsService {
  constructor(
    @InjectModel('shoppings')
    private readonly shoppingModel: Model<ShoppingDocument>,
    @InjectModel('users') private userModel: Model<UserDocument>,
    @InjectModel('sales') private saleModel: Model<SaleDocument>,
    @InjectModel('finanzas') private finModel: Model<Finanzas>,
  ) {}

  async create(dto: CreateShoppingDto) {
    const shopping = new this.shoppingModel(dto);
    return shopping.save();
  }

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

    const today = new Date();
    const start = startDate
      ? new Date(startDate)
      : new Date(today.setHours(0, 0, 0, 0));

    const end = endDate
      ? new Date(endDate)
      : new Date(today.setHours(23, 59, 59, 999));

    const query: any = {
      businessId: user.businessId,
      createdAt: {
        $gte: start,
        $lte: end,
      },
    };

    const result = await this.shoppingModel.aggregate([
      {
        $match: query,
      },
      // 👇 Desnormaliza el array "products"
      {
        $unwind: {
          path: '$products',
          preserveNullAndEmptyArrays: true,
        },
      },
      // 👇 Join con "products" collection
      {
        $lookup: {
          from: 'products', // nombre real de la colección en MongoDB
          localField: 'products.productId',
          foreignField: '_id',
          as: 'products.product',
        },
      },
      {
        $unwind: {
          path: '$products.product',
          preserveNullAndEmptyArrays: true,
        },
      },
      // 👇 Reagrupa products en array
      {
        $group: {
          _id: '$_id',
          doc: { $first: '$$ROOT' },
          products: { $push: '$products' },
        },
      },
      {
        $replaceRoot: {
          newRoot: {
            $mergeObjects: ['$doc', { products: '$products' }],
          },
        },
      },
      // 👇 Ya con arrays poblados, calcula totales
      {
        $addFields: {
          totalProducts: {
            costo_total: { $sum: '$products.costo_total' },
            precio_total: { $sum: '$products.precio_total' },
            saldo_x_pagar: { $sum: '$products.saldo_x_pagar' },
          },
          totalSaldos: {
            pago_anticipado: { $sum: '$saldos.pago_anticipado' },
            saldo_x_pagar: { $sum: '$saldos.saldo_x_pagar' },
          },
          totalDevoluciones: {
            devolucion: { $sum: '$devoluciones.devolucion' },
          },
        },
      },
      {
        $project: {
          businessId: 1,
          userEmail: 1,
          createdAt: 1,
          updatedAt: 1,
          products: 1,
          saldos: 1,
          devoluciones: 1,
          totalProducts: 1,
          totalSaldos: 1,
          totalDevoluciones: 1,
        },
      },
      {
        $sort: { createdAt: -1 },
      },
    ]);

    return result;
  }

  async findAllFull(
    userId: string,
    startDate: string | null,
    endDate: string | null,
    isCron: boolean = false,
  ) {
    let query: any = {};
    const today = new Date();
    const start = startDate
      ? new Date(startDate)
      : new Date(today.setHours(0, 0, 0, 0));

    const end = endDate
      ? new Date(endDate)
      : new Date(today.setHours(23, 59, 59, 999));

    if (!isCron) {
      const user = await this.userModel.findById(userId).exec();
      if (!user) {
        throw new HttpException('Datos incorrectos', HttpStatus.NOT_FOUND);
      }

      query = {
        businessId: user.businessId,
        createdAt: {
          $gte: start,
          $lte: end,
        },
      };
    } else {
      //Se llama de cron
      query = {
        createdAt: {
          $gte: start,
          $lte: end,
        },
      };
    }

    const compras = await this.shoppingModel.aggregate([
      {
        $match: query,
      },
      { $unwind: '$products' },
      {
        $match: {
          'products.fecha': { $ne: null },
          'products.sorteo': { $ne: '' },
        },
      },
      {
        $group: {
          _id: {
            fecha: '$products.fecha',
            sorteo: '$products.sorteo',
          },
          businessId: { $first: '$businessId' },
          productId: { $first: '$products._id' },
          identificador: { $first: '$products.identificador' },
          total_comprado: { $sum: '$products.dotacion' },
        },
      },
    ]);

    const ventas = await this.saleModel.aggregate([
      {
        $match: query,
      },
      { $unwind: '$products' },
      {
        $match: {
          'products.fecha': { $ne: null },
          'products.sorteo': { $ne: '' },
        },
      },
      {
        $group: {
          _id: {
            fecha: '$products.fecha',
            sorteo: '$products.sorteo',
          },
          businessId: { $first: '$businessId' },
          productId: { $first: '$products.productId' },
          identificador: { $first: '$products.identificador' },
          total_vendido: { $sum: '$products.cantidad' },
        },
      },
    ]);

    const hoy = this.startOfDay(new Date());
    const resumenMap = new Map<string, any>();

    // Procesamos compras
    for (const c of compras) {
      const key = `${c._id.fecha.toISOString()}|${c._id.sorteo}`;
      resumenMap.set(key, {
        businessId: c.businessId,
        id: c.productId,
        identificador: c.identificador,
        fecha: c._id.fecha,
        sorteo: c._id.sorteo,
        total_comprado: c.total_comprado,
        total_vendido: 0,
      });
    }

    // Procesamos ventas
    for (const v of ventas) {
      const key = `${v._id.fecha.toISOString()}|${v._id.sorteo}`;
      const existente = resumenMap.get(key);
      if (existente) {
        existente.total_vendido = v.total_vendido;
      } else {
        resumenMap.set(key, {
          businessId: v.businessId,
          id: v.productId,
          identificador: v.identificador,
          fecha: v._id.fecha,
          sorteo: v._id.sorteo,
          total_comprado: 0,
          total_vendido: v.total_vendido,
        });
      }
    }

    // Calculamos diferencia y caducado
    const resumenFinal = Array.from(resumenMap.values()).map((item) => ({
      ...item,
      diferencia: item.total_comprado - item.total_vendido,
      caducado: this.startOfDay(item.fecha, true).getTime() < hoy.getTime(),
      fec: this.startOfDay(item.fecha, true).getTime(),
      fec2: hoy.getTime(),
    }));

    return resumenFinal;
  }

  private startOfDay(date: Date, end: boolean = false): Date {
    const fecha = new Date(
      Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()),
    );

    if (end) {
      fecha.setUTCHours(23, 59, 59, 999);
    } else {
      fecha.setUTCHours(0, 0, 0, 0);
    }

    return fecha;
  }

  async findOne(id: string) {
    const shopping = await this.shoppingModel.findById(id);
    if (!shopping) throw new NotFoundException('Compra no encontrada');
    return shopping;
  }

  async update(id: string, dto: UpdateShoppingDto) {
    const shopping = await this.shoppingModel.findByIdAndUpdate(id, dto, {
      new: true,
    });
    if (!shopping) throw new NotFoundException('Compra no encontrada');
    return shopping;
  }

  async remove(id: string) {
    const result = await this.shoppingModel.findByIdAndDelete(id);
    if (!result) throw new NotFoundException('Compra no encontrada');
    return { message: 'Eliminado' };
  }

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

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

  //Para cron services
  async getWeekDevoluciones() {
    const inputDate = new Date();

    // día de la semana en formato JS: 0 = domingo, 1 = lunes, ...
    let day = inputDate.getDay();
    if (day === 0) day = 7; // domingo -> 7

    // lunes de la semana actual (hora local)
    const monday = new Date(inputDate);
    monday.setDate(inputDate.getDate() - (day - 1));
    monday.setHours(0, 0, 0, 0);

    // domingo de la misma semana (hora local)
    const sunday = new Date(monday);
    sunday.setDate(monday.getDate() + 6);
    sunday.setHours(23, 59, 59, 999);

    const compras = await this.shoppingModel.aggregate([
      {
        $addFields: {
          localCreatedAt: {
            $dateAdd: { startDate: '$createdAt', unit: 'hour', amount: -6 },
          },
        },
      },
      {
        $match: {
          localCreatedAt: {
            $gte: monday,
            $lte: sunday,
          },
        },
      },
      { $unwind: '$products' },
      {
        $match: {
          'products.fecha': { $ne: null },
          'products.sorteo': { $ne: '' },
        },
      },
      {
        $group: {
          _id: {
            fecha: '$products.fecha',
            sorteo: '$products.sorteo',
          },
          businessId: { $first: '$businessId' },
          productId: { $first: '$products._id' },
          identificador: { $first: '$products.identificador' },
          total_comprado: { $sum: '$products.dotacion' },
        },
      },
    ]);

    const ventas = await this.saleModel.aggregate([
      {
        $addFields: {
          localCreatedAt: {
            $dateAdd: { startDate: '$createdAt', unit: 'hour', amount: -6 },
          },
        },
      },
      {
        $match: {
          localCreatedAt: {
            $gte: monday,
            $lte: sunday,
          },
        },
      },
      { $unwind: '$products' },
      {
        $match: {
          'products.fecha': { $ne: null },
          'products.sorteo': { $ne: '' },
        },
      },
      {
        $group: {
          _id: {
            fecha: '$products.fecha',
            sorteo: '$products.sorteo',
          },
          businessId: { $first: '$businessId' },
          productId: { $first: '$products.productId' },
          identificador: { $first: '$products.identificador' },
          total_vendido: { $sum: '$products.cantidad' },
        },
      },
    ]);

    const hoy = this.startOfDay(new Date());
    const resumenMap = new Map<string, any>();

    // Procesamos compras
    for (const c of compras) {
      const key = `${c._id.fecha.toISOString()}|${c._id.sorteo}`;
      resumenMap.set(key, {
        businessId: c.businessId,
        id: c.productId,
        identificador: c.identificador,
        fecha: c._id.fecha,
        sorteo: c._id.sorteo,
        total_comprado: c.total_comprado,
        total_vendido: 0,
      });
    }

    // Procesamos ventas
    for (const v of ventas) {
      const key = `${v._id.fecha.toISOString()}|${v._id.sorteo}`;
      const existente = resumenMap.get(key);
      if (existente) {
        existente.total_vendido = v.total_vendido;
      } else {
        resumenMap.set(key, {
          businessId: v.businessId,
          id: v.productId,
          identificador: v.identificador,
          fecha: v._id.fecha,
          sorteo: v._id.sorteo,
          total_comprado: 0,
          total_vendido: v.total_vendido,
        });
      }
    }

    // Calculamos diferencia y caducado
    const resumenFinal = Array.from(resumenMap.values()).map((item) => ({
      ...item,
      diferencia: item.total_comprado - item.total_vendido,
      caducado: this.startOfDay(item.fecha, true).getTime() < hoy.getTime(),
    }));

    return resumenFinal;
  }

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

    const today = new Date();
    const start = this.parseLocalDate(startDate!);

    const end = endDate
      ? this.parseLocalDate(endDate)
      : new Date(today.setHours(23, 59, 59, 999));

    return await this.finModel.find({
      businessId: user.businessId,
      createdAt: {
        $gte: start,
        $lte: end,
      },
    });
  }

  parseLocalDate(dateStr: string) {
    const [year, month, day] = dateStr.split('-').map(Number);
    return new Date(year, month, day, 0, 0, 0, 0);
  }

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

    if (id === 'xyz') {
      const newFin = new this.finModel({
        ...dto,
        businessId: user.businessId,
      });
      return await newFin.save();
    }

    // Si hay id, actualizamos o creamos (upsert)
    return await this.finModel.findByIdAndUpdate(
      id,
      { ...dto, businessId: user.businessId },
      {
        new: true,
        upsert: true,
      },
    );
  }
}

/**
 * Importante es para resumen global, no eliminar
 * const result = await this.shoppingModel.aggregate([
        {
          $match: query,
        },
        { $unwind: '$products' }, // Descompone el array de productos
        {
          $lookup: {
            from: 'products', // Nombre de la colección
            localField: 'products.productId',
            foreignField: '_id',
            as: 'products.productData',
          },
        },
        {
          $unwind: {
            path: '$products.productData',
            preserveNullAndEmptyArrays: true,
          },
        },
        {
          $group: {
            _id: null,
            totalDotacion: { $sum: '$products.dotacion' },
            totalCosto: { $sum: '$products.costo_total' },
            totalPrecio: { $sum: '$products.precio_total' },
            totalAnticipado: { $sum: '$products.pago_anticipado' },
            totalSaldo: { $sum: '$products.saldo_x_pagar' },
            totalDevolucion: { $sum: '$products.devolucion' },
            products: { $push: '$products' }, // Puedes eliminar esto si no necesitas el detalle
          },
        },
      ]);

      // tambien esta es correcta
      const result = await this.shoppingModel.aggregate([
        {
          $match: query,
        },
        {
          $unwind: '$products',
        },
        {
          $lookup: {
            from: 'products',
            localField: 'products.productId',
            foreignField: '_id',
            as: 'products.productData',
          },
        },
        {
          $unwind: {
            path: '$products.productData',
            preserveNullAndEmptyArrays: true,
          },
        },
        {
          $group: {
            _id: '$_id',
            userEmail: { $first: '$userEmail' },
            businessId: { $first: '$businessId' },
            createdAt: { $first: '$createdAt' },
            total_dotacion: { $sum: '$products.dotacion' },
            total_costo: { $sum: '$products.costo_total' },
            total_precio: { $sum: '$products.precio_total' },
            total_anticipado: { $sum: '$products.pago_anticipado' },
            total_saldo: { $sum: '$products.saldo_x_pagar' },
            total_devolucion: { $sum: '$products.devolucion' },
            products: {
              $push: {
                productId: '$products.productId',
                productData: '$products.productData',
                dotacion: '$products.dotacion',
                costo_total: '$products.costo_total',
                precio_total: '$products.precio_total',
                pago_anticipado: '$products.pago_anticipado',
                saldo_x_pagar: '$products.saldo_x_pagar',
                devolucion: '$products.devolucion',
              },
            },
          },
        },
        {
          $group: {
            _id: null,
            documents: { $push: '$$ROOT' },
            resumenGlobal: {
              $sum: {
                $add: [
                  '$total_dotacion',
                  '$total_costo',
                  '$total_precio',
                  '$total_anticipado',
                  '$total_saldo',
                  '$total_devolucion',
                ],
              },
            },
          },
        },
        {
          $group: {
            _id: null,
            documents: { $push: '$$ROOT' },
            total_dotacion: { $sum: '$total_dotacion' },
            total_costo: { $sum: '$total_costo' },
            total_precio: { $sum: '$total_precio' },
            total_anticipado: { $sum: '$total_anticipado' },
            total_saldo: { $sum: '$total_saldo' },
            total_devolucion: { $sum: '$total_devolucion' },
          },
        },
      ]);
 */
