mongoose = require('mongoose').set('debug', false)
const Schema = mongoose.Schema;

const tools = require('../tools/general');

const Producer = require('../models/producer');
const Storehouse = require('../models/storehouse');
const Provider = require('../models/provider');
const CatProd = require('../models/catprod');
const SubCatProd = require('../models/subcatprod');
const Gasordine = require('../models/gasordine');
const Scontistica = require('../models/scontistica');

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

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

mongoose.Promise = global.Promise;
mongoose.level = "F";

// A1P

// Resolving error Unknown modifier: $pushAll
mongoose.plugin(schema => {
  schema.options.usePushEach = true
});

const productSchema = new Schema({
  idapp: {
    type: String,
  },
  active: {
    type: Boolean,
    default: true,
  },
  isbn: {
    type: String,
  },
  idProductInfo: { type: Schema.Types.ObjectId, ref: 'ProductInfo' },
  idProducer: { type: Schema.Types.ObjectId, ref: 'Producer' },
  idStorehouses: [
    { type: Schema.Types.ObjectId, ref: 'Storehouse' }
  ],
  idGasordine: { type: Schema.Types.ObjectId, ref: 'Gasordine' },
  idScontisticas: [
    { type: Schema.Types.ObjectId, ref: 'Scontistica' }
  ],
  idProvider: { type: Schema.Types.ObjectId, ref: 'Provider' },
  prezzo_ivato: {  // Con IVA
    type: Number
  },
  perc_iva: {  // 4, 10, 22 &
    type: Number
  },
  price: {
    type: Number,
    required: true,
  },
  arrvariazioni: [
    {
      active: {
        type: Boolean,
      },
      versione: {
        type: Number,
      },
      status: { //publish
        type: String,
      },
      price: {
        type: Number,
      },
      sale_price: {
        type: Number,
      },
      quantita: {  // in magazzino
        type: Number,
      },
      numpages: {
        type: Number,
      },
      formato: {
        type: String,
      },
      tipologia: {
        type: String,
      },
      edizione: {
        type: String,
      },
      preOrderDate: {
        type: Date
      },
      addtocart_link: {
        type: String
      },
      eta: {
        type: String
      },
    }
  ],
  price_acquistato: {
    type: Number,
    required: true,
  },
  after_price: {
    type: String
  },
  minBuyQty: {  // quantità minima acquistabile
    type: Number,
    default: 1,
    required: true,
  },
  minStepQty: {  // step quantità acquistabile
    type: Number,
    default: 1,
    required: true,
  },
  maxBookableSinglePersQty: {  // quantità massima Pre-ordinabile (singolarmente)
    type: Number,
    default: 0,
  },
  stockQty: {  // in magazzino
    type: Number,
    default: 0,
  },
  stockBloccatiQty: {  // Prenotati Bloccati
    type: Number,
    default: 0,
  },
  bookedQtyOrdered: {  // Quantità Prenotate ordinate (in Lavorazione)
    type: Number,
    default: 0,
  },
  bookedQtyConfirmed: {  // Quantità Prenotate Confermate Totali
    type: Number,
    default: 0,
  },

  // GAS:
  qtyToReachForGas: {  // Quantità minima da raggiungere per fare l'ordine GAS
    type: Number,
    default: 0,
  },
  maxbookableGASQty: {  // Quantità massima (ancora disponibile) Ordine GAS prenotabile (Complessivamente tra tutti gli ordini)
    type: Number,
    default: 0,
  },
  bookedGASQtyOrdered: {  // Quantità Ordine GAS Prenotate Totali
    type: Number,
    default: 0,
  },
  bookedGASQtyConfirmed: {  // Quantità Ordine GAS Confermate Totali
    type: Number,
    default: 0,
  },
  bookableGASBloccatiQty: {  // Quantità Prenotate Bloccate GAS
    type: Number,
    default: 0,
  },

  quantityLow: {  //Soglia disponibilità bassa
    type: Number,
    default: 0,
  },
  visibilityProductOutOfStock: {  // Visibilità prodotto "esaurito"
    type: Boolean,
    default: false,
  },
  canBeShipped: {  // è spedibile
    type: Boolean,
    default: false,
  },
  canBeBuyOnline: {  // è acquistabile online
    type: Boolean,
    default: false,
  },
  stars: {
    type: Number,
    default: 0,
  },
  dateAvailableFrom: {
    type: Date
  },
  note: {
    type: String,
  },
  producer_name: {
    type: String,
  },
  provider_name: {
    type: String,
  },
  magazzino_name: {
    type: String,
  },
  cat_name: {
    type: String,
  },
  subcat_name: {
    type: String,
  },
  sconto1: {
    type: String,
  },
  sconto2: {
    type: String,
  },
  gas_name: {
    type: String,
  },
});

var Product = module.exports = mongoose.model('Product', productSchema);

productSchema.index({ idapp: 1 });

module.exports.getFieldsForSearch = function () {
  return [
    { field: 'name', type: tools.FieldType.string },
    { field: 'description', type: tools.FieldType.string },
  ]
};

module.exports.executeQueryTable = function (idapp, params) {
  params.fieldsearch = this.getFieldsForSearch();
  return tools.executeQueryTable(this, idapp, params);
};

module.exports.executeQueryPickup = async function (idapp, params) {

  let strfind = params.search;

  strfind = strfind.replace(/[-@]/g, '');

  if (strfind === '' && !params.filter) {
    return [];
  }

  let filterfindexact = {};
  if (strfind) {
    filterfindexact = { comune: strfind };
  }

  let limit = 10;
  let risexact = [];

  let filterfind = {
    idapp,
    'productInfo.name': {
      $regex: `\\b${strfind}`, // Usa \\b per trovare solo le parole che iniziano con strfind
      $options: 'i' // Rendi la ricerca case-insensitive
    }
  };

  /*
  let aggr1 = [
    {
      $lookup: {
        from: 'productinfos',
        localField: 'idProductInfo',
        foreignField: '_id',
        as: 'productInfo'
      }
    },
    {
      $lookup: {
        from: 'authors',
        localField: 'idAuthors',
        foreignField: '_id',
        as: 'authors'
      }
    },
    {
      $match: { 'productInfo.name': strfind },
    },
    { $limit: 1 },
    {
      $project: {
        name: { $concat: ["$productInfo.name", " (", "$authors", ")"] },
      },
    },
  ];


  if (params.filter) {
    filterfind = { ...params.filter, ...filterfind };
    limit = 200;
  } else {
    // risexact = await City.find(filterfindexact, {comune: 1, prov: 1, reg: 1}).lean();
    risexact = await City.aggregate(aggr1);
  }
  */

  if (params.filter) {
    filterfind = { ...params.filter, ...filterfind };
    limit = 200;
  }

  let aggr2 = [
    {
      $lookup: {
        from: 'productinfos',
        localField: 'idProductInfo',
        foreignField: '_id',
        as: 'productInfo'
      }
    },
    {
      $unwind: {
        path: '$productInfo',
        preserveNullAndEmptyArrays: true,
      },
    },
    {
      $lookup: {
        from: 'authors',
        localField: 'idAuthors',
        foreignField: '_id',
        as: 'authors'
      }
    },
    {
      $match: filterfind,
    },
    { $limit: limit },
    {
      $project: {
        name: '$productInfo.name',
      },
    },
  ];


  // let ris = await City.find(filterfind, {comune: 1, prov: 1, reg: 1}).lean().limit(limit);
  let ris = await this.aggregate(aggr2).limit(limit);

  return [...risexact, ...ris];

};


module.exports.getProductByCode = function (idapp, code) {
  return Product.findAllIdApp(idapp, code);
}

module.exports.getProductById = async function (id) {
  const arrris = await Product.findAllIdApp('', '', id);
  return arrris && arrris.length > 0 ? arrris[0] : null
}

module.exports.getInStockById = async function (id) {
  const myprod = await Product.findOne({ _id: id });
  if (myprod) {
    let instock = 0;
    if (myprod.idGasordine) {
      instock = myprod.maxbookableGASQty;
    } else {
      instock = myprod.stockQty;
    }
    return instock
  }
  return null;
}

module.exports.isLowQuantityInStockById = async function (id) {
  const instock = await Product.getInStockById(id);
  const myprod = await Product.findOne({ _id: id });
  if (instock) {
    return (instock <= (myprod.quantityLow + 1));
  }
  return false;
}

module.exports.findAllIdApp = async function (idapp, code, id, all) {
  let myfind = {};
  let myqueryadd = {};
  let query = [];

  try {

    if (idapp) {
      myfind = { idapp };
    }

    if (!all) {
      myfind = { ...myfind, active: true }
    }

    if (code) {
      myfind = { ...myfind, code }
    }
    if (id) {
      myqueryadd = {
        $addFields: {
          myId1: {
            $toObjectId: id,
          },
        },
      }
      myfind = {
        $expr: {
          $eq: ["$_id", "$myId1"],
        },
      }

      query.push(myqueryadd);
    }

    // DA TOGLIEREE
    // myfind = { ...myfind, code: '4012824406094' };

    // return await Product.find(myfind);

    query.push(
      { $match: myfind },
      {
        $lookup: {
          from: 'producers',
          localField: 'idProducer',
          foreignField: '_id',
          as: 'producer'
        }
      },
      {
        $unwind: {
          path: '$producer',
          preserveNullAndEmptyArrays: true,
        },
      },
      {
        $lookup: {
          from: 'productinfos',
          localField: 'idProductInfo',
          foreignField: '_id',
          as: 'productInfo'
        }
      },
      {
        $unwind: {
          path: '$productInfo',
          preserveNullAndEmptyArrays: true,
        },
      },
      {
        $lookup: {
          from: 'gasordines',
          localField: 'idGasordine',
          foreignField: '_id',
          as: 'gasordine'
        }
      },
      {
        $unwind: {
          path: '$gasordine',
          preserveNullAndEmptyArrays: true,
        },
      },
      {
        $match: {
          $or: [
            { 'gasordine.active': true },  // Include documents where gasordines.active is true
            { 'gasordine': { $exists: false } }  // Include documents where gasordines array doesn't exist
          ]
        }
      },
      {
        $group: {
          _id: '$_id',
          gasordine: { $first: '$gasordine' },
          originalFields: { $first: '$$ROOT' } // Preserve all existing fields
        }
      },
      {
        $replaceRoot: {
          newRoot: {
            $mergeObjects: ['$originalFields', { gasordine: '$gasordine' }]
          }
        }
      },
      {
        $lookup: {
          from: 'providers',
          localField: 'idProvider',
          foreignField: '_id',
          as: 'provider'
        }
      },
      {
        $unwind: {
          path: '$provider',
          preserveNullAndEmptyArrays: true,
        },
      },
      {
        $lookup: {
          from: 'authors',
          localField: 'productInfo.idAuthors',
          foreignField: '_id',
          as: 'productInfo.authors'
        }
      },
      {
        $lookup: {
          from: 'publishers',
          localField: 'productInfo.idPublisher',
          foreignField: '_id',
          as: 'productInfo.publisher'
        }
      },
      {
        $unwind: {
          path: '$productInfo.publisher',
          preserveNullAndEmptyArrays: true,
        },
      },
      {
        $lookup: {
          from: 'catprods',
          localField: 'productInfo.idCatProds',
          foreignField: '_id',
          as: 'productInfo.catprods'
        }
      },
      {
        $lookup: {
          from: 'subcatprods',
          localField: 'productInfo.idSubCatProds',
          foreignField: '_id',
          as: 'productInfo.subcatprods'
        }
      },
      {
        $lookup: {
          from: 'scontisticas',
          localField: 'idScontisticas',
          foreignField: '_id',
          as: 'scontisticas'
        }
      },
      {
        $lookup: {
          from: 'storehouses',
          localField: 'idStorehouses',
          foreignField: '_id',
          as: 'storehouses'
        }
      },
      {
        $lookup: {
          from: 'orders',
          let: { productId: '$_id' },
          pipeline: [
            {
              $match: {
                $expr: {
                  $and: [
                    { $eq: ['$idProduct', '$$productId'] },
                    {
                      $or: [
                        {
                          $eq: ['$status', shared_consts.OrderStatus.CHECKOUT_SENT]
                        },
                        {
                          $and: [{ $lt: ['$status', shared_consts.OrderStatus.CHECKOUT_SENT] },
                          {
                            $gt: [
                              '$modify_at',
                              { $subtract: [new Date(), 60 * 60 * 1000] } // 1 hour in milliseconds 60 * 60
                            ]
                          }]
                        }
                      ]
                    }
                  ]
                }
              }
            },
            {
              $group: {
                _id: null,
                totalQty: { $sum: '$quantity' },
              }
            }
          ],
          as: 'productOrders'
        }
      },
      {
        $lookup: {
          from: 'orders',
          let: { productId: '$_id' },
          pipeline: [
            {
              $match: {
                $expr: {
                  $and: [
                    { $eq: ['$idProduct', '$$productId'] },
                    {
                      $or: [
                        {
                          $eq: ['$status', shared_consts.OrderStatus.CHECKOUT_SENT]
                        },
                        {
                          $and: [{ $lt: ['$status', shared_consts.OrderStatus.CHECKOUT_SENT] },
                          {
                            $gt: [
                              '$modify_at',
                              { $subtract: [new Date(), 60 * 60 * 1000] } // 1 hour in milliseconds 60 * 60
                            ]
                          }]
                        }
                      ]
                    }
                  ]
                }
              }
            },
            {
              $group: {
                _id: null,
                totalQtyPreordered: { $sum: '$quantitypreordered' }
              }
            }
          ],
          as: 'productPreOrders'
        }
      },
      {
        $addFields: {
          QuantitaOrdinateInAttesa: {
            $ifNull: [
              {
                $cond: {
                  if: { $isArray: '$productOrders' },
                  then: { $arrayElemAt: ['$productOrders.totalQty', 0] },
                  else: 0
                }
              },
              0
            ]
          },
          QuantitaPrenotateInAttesa: {
            $ifNull: [
              {
                $cond: {
                  if: { $isArray: '$productPreOrders' },
                  then: { $arrayElemAt: ['$productPreOrders.totalQtyPreordered', 0] },
                  else: 0
                }
              },
              0
            ]
          },
        },
      },
      {
        $addFields: {
          quantityAvailable: {
            $subtract: ["$stockQty", "$QuantitaOrdinateInAttesa"],
          },
          bookableAvailableQty: {
            $subtract: ["$maxbookableGASQty", "$QuantitaPrenotateInAttesa"],
          }
        }
      },
      {
        $unset: 'productOrders'
      },
      {
        $unset: 'productPreOrders'
      },
      {
        $sort: {
          'productInfo.name': 1 // 1 for ascending order, -1 for descending order
        }
      },
    );

    // console.log('query=', query);

    let ris = await Product.aggregate(query)

    // console.table('ris', ris);

    return ris;

  } catch (e) {
    console.error('E', e);
  }


};

module.exports.getAllProducts = function (query, sort, callback) {
  Product.find(query, null, sort, callback)
}

module.exports.getProductByDepartment = function (query, sort, callback) {
  Product.find(query, null, sort, callback)
}

module.exports.getProductByCatProd = function (query, sort, callback) {
  Product.find(query, null, sort, callback)
}

module.exports.getProductByTitle = function (query, sort, callback) {
  Product.find(query, null, sort, callback)
}

module.exports.filterProductByDepartment = function (department, callback) {
  let regexp = new RegExp(`^${department}$`, 'i')
  var query = { department: { $regex: regexp } };
  Product.find(query, callback)
}

module.exports.filterProductByCatProd = function (catprod, callback) {
  let regexp = new RegExp(`^${catprod}$`, 'i')
  var query = { catprod: { $regex: regexp } };
  Product.find(query, callback);
}

module.exports.filterProductByTitle = function (title, callback) {
  let regexp = new RegExp(`^${title}$`, 'i')
  var query = { title: { $regex: regexp } };
  Product.find(query, callback);
}

module.exports.getProductByID = function (id, callback) {
  Product.findById(id, callback);
}

module.exports.updateProductInOrder = async function (order) {

  if (order.product)
    order.product = await Product.getProductById(order.product._id);

  return order;
}

module.exports.createIndexes((err) => {
  if (err) throw err;
});

module.exports.convertAfterImportALLPROD = async function (idapp, dataObjects) {

  const arrprod = await Product.find({ idapp }).lean();
  for (const prod of arrprod) {
    await this.singlerecconvert_AfterImport_AndSave(prod);
  }

};

module.exports.getArrCatProds = async function (idapp, cosa) {

  try {
    let addquery = [];
    let arr = [];
    if (cosa === shared_consts.PROD.GAS) {
      addquery = [
        { $match: { idapp, idGasordine: { $exists: true, $ne: null, $type: 'objectId' } } }
      ];

      addquery.push(
        {
          $lookup: {
            from: 'gasordines',
            localField: 'idGasordine',
            foreignField: '_id',
            as: 'gasordine'
          }
        }
      );

      addquery.push(
        {
          $match: {
            "gasordine.active": true
          }
        }
      );

    } else if (cosa === shared_consts.PROD.BOTTEGA) {
      addquery = [{
        $match: {
          idapp, $or: [
            { idGasordine: { $exists: false } },
            { idGasordine: { $exists: true, $eq: null } }
          ]
        }
      }]
    } else {
      addquery = [{ $match: { idapp } }];
    }

    let myquery = [...addquery,
    {
      $lookup: {
        from: "productinfos",
        localField: "idProductInfo",
        foreignField: "_id",
        as: "productInfo",
      },
    },
    {
      $lookup: {
        from: "catprods",
        localField: "productInfo.idCatProds",
        foreignField: "_id",
        as: "category"
      }
    },
    { $unwind: "$category" },
    { $group: { _id: "$category._id", name: { $first: "$category.name" } } },
    { $sort: { name: 1 } }
    ];

    arr = await Product.aggregate(myquery, (err, result) => {
      if (err) {
        console.error(err);
        // Gestisci l'errore come desideri
        return [];
      } else {
        const uniqueCategories = result.map(category => category.name);
        // console.log(uniqueCategories);
        return uniqueCategories;
        // Ora uniqueCategories contiene l'array delle categorie univoche utilizzate in tutti i prodotti con active = true
      }
    });
    return arr;
  } catch (e) {
    console.error('err', e);
    return [];
  }


}

module.exports.singlerecconvert_AfterImport_AndSave = async function (idapp, prod, isnuovo) {

  let setta = false;

  try {
    let objtoset = {}
    let rec = null

    // Impostazioni Base:
    if (isnuovo) {
      objtoset = {
        idapp,
        // minBuyQty: 1,
        // minStepQty: 1,
        maxBookableSinglePersQty: 0,
        bookedGASQtyOrdered: 0,
        bookableGASBloccatiQty: 0,
        bookedGASQtyConfirmed: 0,
        // qtyToReachForGas: 0,
        // maxbookableGASQty: 0,
      }
    }

    if (prod.producer_name) {
      // Cerca il produttore
      let recproducer = await Producer.findOne({ idapp, name: prod.producer_name }).lean();
      if (!recproducer) {
        // Non esiste questo produttore, quindi lo creo !
        recproducer = new Producer({ idapp, name: prod.producer_name });
        ris = await recproducer.save();
        recproducer = await Producer.findOne({ idapp, name: prod.producer_name }).lean();
      }

      if (recproducer) {
        objtoset = {
          ...objtoset,
          idProducer: recproducer._id,
        }
        setta = true;
      }
    }

    if (prod.magazzino_name) {
      // Cerca il produttore
      let recstorehouse = await Storehouse.findOne({ idapp, name: prod.magazzino_name }).lean();
      if (!recstorehouse) {
        // Non esiste questo produttore, quindi lo creo !
        recstorehouse = new Storehouse({ idapp, name: prod.magazzino_name });
        ris = await recstorehouse.save();
        recstorehouse = await Storehouse.findOne({ idapp, name: prod.magazzino_name }).lean();
      }

      if (recstorehouse) {
        objtoset = {
          ...objtoset,
          idStorehouses: [recstorehouse._id],
        }
        setta = true;
      }
    }


    if (prod.provider_name) {
      // Cerca il produttore
      let recprovider = await Provider.findOne({ idapp, name: prod.provider_name }).lean();
      if (!recprovider) {
        recprovider = new Provider({ idapp, name: prod.provider_name });
        // Non esiste questo produttore, quindi lo creo !
        ris = await recprovider.save();
        recprovider = await Provider.findOne({ idapp, name: prod.provider_name }).lean();
      }

      if (recprovider) {
        objtoset = {
          ...objtoset,
          idProvider: recprovider._id,
        }
        setta = true;
      }
    }
    if (prod.gas_name) {
      // Cerca il GAS
      rec = await Gasordine.findOne({ idapp, name: prod.gas_name }).lean();
      if (!rec) {
        rec = new Gasordine({ idapp, name: prod.gas_name, active: true });
        // Non esiste questo GAS, quindi lo creo !
        ris = await rec.save();
        rec = await Gasordine.findOne({ idapp, name: prod.gas_name }).lean();
      }

      if (rec) {
        objtoset = {
          ...objtoset,
          idGasordine: rec._id,
        }
        setta = true;
      }
    }

    let arrsconti = []

    if (prod.sconto1) {
      // Cerca la scontistica
      let recscontistica = await Scontistica.findOne({ idapp, code: prod.sconto1 }).lean();
      if (!recscontistica) {
        recscontistica = new Scontistica({ idapp, code: prod.sconto1 });
        // Non esiste questa scontistica, quindi lo creo !
        ris = await recscontistica.save();
        recscontistica = await Scontistica.findOne({ idapp, code: prod.sconto1 }).lean();
      }

      if (recscontistica) {
        arrsconti.push(recscontistica);
      }
    }

    if (prod.sconto2) {
      // Cerca la scontistica
      let recscontistica = await Scontistica.findOne({ idapp, code: prod.sconto2 }).lean();
      if (!recscontistica) {
        recscontistica = new Scontistica({ idapp, code: prod.sconto2 });
        // Non esiste questa scontistica, quindi lo creo !
        ris = await recscontistica.save();
        recscontistica = await Scontistica.findOne({ idapp, code: prod.sconto2 }).lean();
      }

      if (recscontistica) {
        arrsconti.push(recscontistica);
      }
    }

    if (arrsconti.length > 0) {
      objtoset = {
        ...objtoset,
        idScontisticas: arrsconti,
      }
      setta = true;
    }

    // Aggiorna il prezzo ?
    const aggiornaprezzo = false;
    if (aggiornaprezzo) {
      // cerca il prodotto
      const myprodinput = dataObjects.find((rec) => rec._id === prod._id)
      if (myprodinput) {
        objtoset = {
          ...objtoset,
          price: myprodinput.price,
        }
      }
    }

    if (!tools.isObjectEmpty(objtoset)) {
      ris = await Product.findOneAndUpdate({ _id: ObjectID(prod._id) }, { $set: objtoset })

      const objDelete = {
        cat_name: 1,
        subcat_name: 1,
        producer_name: 1,
        provider_name: 1,
        magazzino_name: 1,
        sconto1: 1,
        sconto2: 1,
        gas_name: 1,
      };

      ris = await Product.updateOne({ _id: ObjectID(prod._id) }, { $unset: objDelete })

      if (ris && ris.nModified > 0) {
        console.log('Modificato: ', objtoset.name);
      }

      // const campodarimuovere = 'producer_name';
      // await Product.findOneAndUpdate({ _id: prod._id }, { $unset: { [campodarimuovere]: 1 } })
    }
  } catch (e) {
    console.error('Err', e);
  }

}