/* eslint-disable no-console */
/* eslint-disable global-require */
/* eslint-disable import/no-dynamic-require */
const fs = require("fs");
const parser = require("xml2json");
const htmlparser = require("node-html-parser").parse;
const moment = require("moment");
const Mongoose = require("mongoose");
const iconv = require("iconv-lite");
const request = require("request");
const config = require("./config");

// TODO: envoyer mail lors d'une erreur

Mongoose.set("useNewUrlParser", true);
Mongoose.set("useFindAndModify", false);
Mongoose.set("useCreateIndex", true);
Mongoose.set("debug", config.mongodbDebug);

Mongoose.connect(config.mongodbUrl, config.mondeDbOptions);

// Initialisation de la connexion à MongoDB
const db = () => {
  const m = {};

  fs.readdirSync("./models")
    .filter(file => {
      return (
        file.indexOf(".") !== 0 &&
        file.slice(-3) === ".js" &&
        file !== "index.js"
      );
    })
    .forEach(file => {
      const model = require(`./models/${file.replace(".js", "")}`)(Mongoose);
      m[model.modelName] = model;
    });

  return m;
};

const models = db();

let foundGasStations = 0; // Nombre total de station trouvée dans le fichier XML
let done = 0; // Nombre de stations mise à jour

/**
 * Fonction permettant d'extraire et de convertir la liste des prix des carburants d'une station
 * @param {Object} station
 */
const extractPrice = station => {
  const prices = [];

  if (station.prix) {
    for (let i = 0; i < station.prix.length; i += 1) {
      const currentPrice = station.prix[i];
      prices.push({
        gasType: currentPrice.nom,
        price: currentPrice.valeur,
        updatedAt: moment(currentPrice.maj)
      });
    }
  }

  return prices;
};

/**
 * Fonction permettant de transformer une string en latitude ou longitude
 * @param {String} value
 */
const convertValueToPosition = value => {
  return Number(value) / 100000;
};

/**
 * Fonction permettant de mettre à jour les prix d'une station
 * @param {Object} station
 * @param {Function} callback
 */
const createOrUpdateGasStation = (station, callback) => {
  models.Stations.findOne({
    stationId: station.stationId
  })
    .then(item => {
      if (item) {
        item
          .update({
            name: item.name || null,
            stationId: station.stationId,
            location: station.location,
            prices: station.prices,
            services: station.services,
            postCode: station.postCode,
            address: station.address,
            city: station.city
          })
          .then(() => {
            callback(null);
          })
          .catch(callback);
      } else {
        // Create item
        const newItem = new models.Stations(station);
        newItem
          .save()
          .then(() => {
            callback(null);
          })
          .catch(callback);
      }
    })
    .catch(callback);
};

/**
 * Fonction permettant de retrouver le nom d'une station
 * @param {Number} start
 * @param {Function} callback
 */
const getStationName = (start, callback) => {
  models.Stations.find({
    name: null
  })
    .skip(start)
    .limit(1)
    .then(items => {
      if (items && items.length === 1) {
        const item = items[0];
        const options = {
          method: "POST",
          url: `https://www.prix-carburants.gouv.fr/map/recupererInfosPdv/${item.stationId}`,
          headers: {
            "X-Requested-With": "XMLHttpRequest"
          }
        };

        request(options, (err, response, body) => {
          if (err) {
            callback(err);
          } else {
            const html = htmlparser(body);
            const name = html
              .querySelector("strong")?
              .toString()?
              .replace(/[<>/]/g, "")?
              .replace(/strong/g, "") || null;

            if (name) {
              item.name = name;
              item.save(errUpdate => {
                if (errUpdate) {
                  callback(err);
                } else {
                  getStationName(start + 1, callback);
                }
              });
            }
          }
        });
      } else {
        callback(null);
      }
    })
    .catch(callback);
};

/**
 * Fonction permettant de stoper le script une fois celui-ci terminé
 */
const next = () => {
  if (done === foundGasStations) {
    console.log("All price updated!");
    // Une fois tous les prix mis à jour on maj le nom de la station
    getStationName(0, err => {
      if (err) {
        console.log("Done with error:", err);
      } else {
        console.log(`Done for ${done} stations`);
      }
      process.exit();
    });
  }
};

// INFO: le script commence réellement ici
fs.readFile("public/gas-stations.xml", function(err, data) {
  if (err) {
    console.log("ERR:", err);
    process.exit();
    return false;
  }

  const json = parser.toJson(iconv.decode(data, "ISO-8859-1"));
  const rawStations = JSON.parse(json).pdv_liste.pdv;
  const stations = [];

  // Parcours de la liste des stations issues du XML pour en extraire les informations
  for (let i = 0; i < rawStations.length; i += 1) {
    const currentStation = rawStations[i];
    stations.push({
      stationId: currentStation.id,
      location: {
        type: "Point",
        coordinates: [
          convertValueToPosition(currentStation.longitude),
          convertValueToPosition(currentStation.latitude)
        ]
      },
      prices: extractPrice(currentStation),
      services:
        currentStation.services && currentStation.services.service
          ? currentStation.services.service
          : [],
      postCode: currentStation.cp.toString(),
      address: currentStation.adresse,
      city: currentStation.ville
    });
  }

  foundGasStations = stations.length;

  // Pour chaque station correctement formatée on maj ses prix (ou on l'insère en base)
  stations.forEach(type => {
    createOrUpdateGasStation(type, () => {
      done += 1;
      next();
    });
  });
  return true;
});
