const shared_consts = require('../tools/shared_nodejs');
const cartModel = require('../models/cart');

const { ObjectId } = require('mongodb');

const Gasordine = require('../models/gasordine');

const Order = require('../models/order');
const OrderClass = require('../modules/OrderClass');

const Scontistica = require('../models/scontistica');

class Cart {
  constructor(order, arrorders) {
    this.modify_at = new Date();

    this.items = {};
    if (!!order) {
      this.initializeFromOrder(order);
    } else if (!!arrorders) {
      for (const ord of arrorders) {
        this.items.push(ord);
      }
    }
  }
  async updatecarttotals(updateCalcPrice = true) {
    try {
      this.totalQty = 0;
      this.totalPrice = 0;
      this.totalPriceCalc = 0;
      this.totalPriceIntero = 0;

      for (const key in this.items) {
        const item = this.items[key];
        const order = item.order || item;

        await this.updateOrderTotals(order, updateCalcPrice);
      }

      this.totalPrice = parseFloat(this.totalPrice.toFixed(2));
      this.totalPriceCalc = parseFloat(this.totalPriceCalc.toFixed(2));
      this.totalPriceIntero = parseFloat(this.totalPriceIntero.toFixed(2));
    } catch (e) {
      console.error("Errore durante l'aggiornamento del carrello:", e);
    }
  }

  async init() {
    await this.updatecarttotals(false);
  }

  initializeFromOrder(order) {
    this.idapp = order.idapp || 0;
    this.userId = order.userId || '';
    this.items[order._id] = order;
  }
  static async constructByCart(cart) {
    try {
      const mynewcart = new Cart(null);
      await mynewcart.init();
      mynewcart.idapp = cart.idapp || 0;
      mynewcart.items = cart.items;
      mynewcart.department = cart.department;
      mynewcart.userId = cart.userId || '';
      mynewcart.modify_at = new Date();
      mynewcart.note_ordine_gas = '';
      mynewcart.codice_sconto = cart.codice_sconto;
      mynewcart.descr_sconto = cart.descr_sconto;

      return mynewcart;
    } catch (e) {
      console.log('Error', e);
      return null;
    }
  }

  isAvailableByOrder(order) {
    if (order && order.product) {
      return order.product.quantityAvailable > 0;
    }
    return false;
  }

  isInPreorderByOrder(order) {
    if (order && order.product) {
      return order.product.bookableAvailableQty > 0;
    }
    return false;
  }

  isSameStorehouse(itemorder) {
    try {
      if (this.items.length > 0) {
        const mystorehouse = this.items[0].order.idStorehouse;
        return mystorehouse ? mystorehouse._id.toString() === itemorder.idStorehouse : true;
      } else {
        return true;
      }
    } catch (e) {
      return false;
    }
  }

  async addqty(itemorder) {
    const myitem = this.items.find((rec) => rec.order._id.toString() === itemorder._id);
    if (!!myitem) {
      let stepmin = myitem.order?.product?.minStepQty || 1;
      let step = stepmin;
      if (this.isAvailableByOrder(myitem.order)) {
        if (myitem.order.quantity === 0) step = myitem.order.product.minBuyQty ?? stepmin;
        else if (myitem.order.quantity >= 10) step = stepmin < 2 ? 2 : stepmin;
        else if (myitem.order.quantity >= 20) step = stepmin < 5 ? 5 : stepmin;

        myitem.order.quantity += step;
      } else {
        if (myitem.order.quantitypreordered === 0) step = myitem.order.product.minBuyQty ?? stepmin;
        else if (myitem.order.quantitypreordered >= 10) step = stepmin < 2 ? 2 : stepmin;
        else if (myitem.order.quantitypreordered >= 20) step = stepmin < 5 ? 5 : stepmin;

        myitem.order.quantitypreordered += step;
      }

      myitem.order.modify_at = new Date();

      myitem.order = await Order.updateTotals(myitem.order, this.codice_sconto);

      await this.updatecarttotals(false);
      await this.updateExtraOrder();
      await Order.findOneAndUpdate({ _id: myitem.order._id }, { $set: myitem.order }, { new: false });
      return myitem.order;
    }
  }

  qtaNextSub(myorder, myproduct) {
    try {
      let step = myproduct.minStepQty || 1;
      let minqta = myproduct.minBuyQty || 1;
      if (myproduct.quantityAvailable > 0) {
        if (myorder.quantity === minqta) {
          step = minqta;
        } else {
          if (myorder.quantity - step < 0) {
            step = myorder.quantity - step;
          }
        }
      } else {
        if (myorder.quantitypreordered === minqta) {
          step = minqta;
        } else {
          if (myorder.quantitypreordered - step < 0) {
            step = myorder.quantitypreordered - step;
          }
        }
      }
      return step;
    } catch (e) {
      console.error('Error in qtaNextSub: ', e);
      return 0; // default step value in case of error
    }
  }

  async subqty(itemorder) {
    try {
      const myitem = this.items.find((rec) => rec.order._id.toString() === itemorder._id);
      if (!!myitem) {
        let step = this.qtaNextSub(myitem.order, myitem.order.product);
        if (myitem.order.quantitypreordered - step >= 0) {
          myitem.order.quantitypreordered -= step;
        } else {
          if (myitem.order.quantity - step >= 0) {
            myitem.order.quantity -= step;
          }
        }
        myitem.order = await Order.updateTotals(myitem.order, this.codice_sconto);
        await this.updatecarttotals(false);
        await this.updateExtraOrder();

        await Order.findOneAndUpdate({ _id: myitem.order._id }, { $set: myitem.order }, { new: false });
        return myitem.order;
      }
    } catch (e) {
      console.error('Err: ', e);
    }
  }

  async addItem(itemorder) {
    // this.items.push(itemorder);

    let ind = this.items.length;
    this.items[ind] = {};
    this.items[ind].order = itemorder;
    this.items[ind].order = await Order.updateTotals(this.items[ind].order, this.codice_sconto);
    await this.updatecarttotals(false);
    await this.updateExtraOrder();

    return ind;
  }

  async removeItem(orderId) {
    // this.items.push(itemorder);
    this.items = this.items.filter((item) => item.order._id.toString() !== orderId.toString());
    await this.updatecarttotals(false);
    await this.updateExtraOrder();
  }

  generateModel() {
    try {
      let newCart = new cartModel({
        idapp: this.idapp,
        items: this.generateArray(),
        totalQty: this.totalQty,
        totalPriceCalc: this.totalPriceCalc,
        totalPriceIntero: this.totalPriceIntero,
        totalPrice: this.totalPrice,
        userId: this.userId,
        department: this.department,
        note: this.note,
        codice_sconto: this.codice_sconto,
        descr_sconto: this.descr_sconto,
        note_ordine_gas: this.note_ordine_gas,
        modify_at: this.modify_at,
      });
      return newCart;
    } catch (e) {
      console.error('Err', e);
    }
    return null;
  }

  async updateOrderTotals(order, updateCalcPrice) {
    order.TotalPriceProductCalc = 0;

    try {
      // PROVO A METTERE SEMPRE CHE MI RICALCOLA IL PREZZO !
      // updateCalcPrice = true;

      if (updateCalcPrice) {
        order.TotalPriceProduct = 0;
      }

      const qty = order.quantity + order.quantitypreordered;
      this.totalQty += qty;

      const recscontisticheTrovate = await Cart.getRecSconto(this.idapp, this.codice_sconto, true);

      const scontiDaUsare = recscontisticheTrovate?.length ? recscontisticheTrovate : order.scontisticas || [];
      const priceCalc = this.calcolaPrezzoScontato(order, qty, scontiDaUsare);
      const priceintero = this.calcolaPrezzoScontato(order, qty, []);

      order.TotalPriceProductCalc += priceCalc;

      if (updateCalcPrice) {
        order.TotalPriceProduct += priceCalc;
        order.TotalPriceProductstr = parseFloat(order.TotalPriceProduct.toFixed(2));
      }

      this.totalPrice += order.TotalPriceProduct;
      this.totalPriceCalc += priceCalc;
      this.totalPriceIntero += priceintero;

      // se idGasOrdine === '' allora toglilo
      if (order.idGasordine === '') {
        delete order.idGasordine;
      }
      if (order.idStorehouse === '') {
        delete order.idStorehouse;
      }
      if (order.idProvider === '') {
        delete order.idProvider;
      }
      if (order.idProducer === '') {
        delete order.idProducer;
      }

      // if (updateCalcPrice) {
      // Aggiorna anche l'ordine associato
      await Order.findOneAndUpdate({ _id: order._id }, { $set: order }, { new: false });
    } catch (e) {
      console.error('Err updateOrderTotals', e);
    }
    // if (updateCalcPrice) {
    //   await Order.findOneAndUpdate({ _id: order._id }, { $set: order }, { new: false });
    // }
  }

  calcolaPrezzoScontato(order, qtyTotale, sconti = []) {
    if (!sconti || sconti.length === 0) {
      return order.price * qtyTotale;
    }

    const { sconti_da_applicare, qtanonscontata } = OrderClass.applyNonCumulativeDiscounts(order, sconti);

    const prezzoTotale = OrderClass.calculateDiscountedPrice(order, sconti_da_applicare, qtanonscontata);

    return prezzoTotale;
  }

  async updateExtraOrder() {
    try {
      let arrGas = [];
      const precnoteordgas = this.note_ordine_gas;
      this.note_ordine_gas = '';
      for (const rec in this.items) {
        let order = this.items[rec].order;
        if (!order) {
          order = this.items[rec];
        }
        // Prendo la nota valida e la metto visibile sul Carrello !
        if (order.idGasordine) {
          const recGas = await Gasordine.findOne({ _id: order.idGasordine }).lean();
          if (recGas) {
            if (recGas.note_ordine_gas) {
              if (!arrGas.includes(recGas._id.toString())) {
                if (this.note_ordine_gas) this.note_ordine_gas += '<br>';

                this.note_ordine_gas += '<strong>' + recGas.name + '</strong>' + ':<br>' + recGas.note_ordine_gas;
                arrGas.push(recGas._id.toString());
              }
            }
          }
        }
      }
    } catch (e) {
      console.error('Err:', e);
    }
  }

  generateArray() {
    let arr = [];
    for (let id in this.items) {
      arr.push(this.items[id]);
    }
    return arr;
  }

  static async getRecSconto(idapp, codice_sconto, soloAttivi) {
    const condSconto = { $regex: new RegExp(`^${codice_sconto}$`, 'i') };
    const query = {
      idapp: idapp,
      code: condSconto,
      applica: shared_consts.SCONTI_APPLICA.A_TUTTI,
    };

    if (soloAttivi) {
      query.attivo = true;
    }

    const recscontisticheTrovate = await Scontistica.find(query).lean();

    return recscontisticheTrovate;
  }

  async updateCartIntoDB() {
    const result = await cartModel.updateCartByUserId(this.userId, {
      items: this.items,
      totalQty: this.totalQty,
      totalPrice: this.totalPrice,
      totalPriceCalc: this.totalPriceCalc,
      totalPriceIntero: this.totalPriceIntero,
      userId: this.userId,
    });

    return result;
  }

  async aggiornaCarrello() {
    try {
      // Rifai i calcoli !
      await this.updatecarttotals(true);

      await this.updateExtraOrder();

      // Aggiorna i calcoli sul DB:
      const mycart = await this.updateCartIntoDB();

      // ritorna il carrello aggiornato !
      return await cartModel.getCartCompletoByCartId(mycart._id, this.idapp);
    } catch (e) {
      console.error("Errore durante l'aggiornamento del carrello:", e);
    }
  }

  static async aggiornaCarrelloByCart(mycartpar, userId, idapp) {
    try {
      let mycart = mycartpar;

      if (!mycart) mycart = await cartModel.getCartByUserId(userId, idapp);

      if (!mycart) return null;

      mycart = await cartModel.getCartCompletoByCartId(mycart._id, idapp);

      let newCart = await Cart.constructByCart(mycart);
      if (newCart) return newCart.aggiornaCarrello();

      return newCart;
    } catch (e) {
      console.error('Err AggiornaCarrello', e);
      return null;
    }
  }
}

module.exports = Cart;
