import { MAX_NOMBRE_ASTRAL, RdDTimestamp, WORLD_TIMESTAMP_SETTING } from "./rdd-timestamp.js";
import { RdDCalendrierEditor } from "./rdd-calendrier-editor.js";
import { RdDResolutionTable } from "../rdd-resolution-table.js";
import { RdDDice } from "../rdd-dice.js";
import { Misc } from "../misc.js";
import { DialogChronologie } from "../dialog-chronologie.js";
import { HIDE_DICE, SYSTEM_RDD, SYSTEM_SOCKET_ID } from "../constants.js";
import { ReglesOptionnelles } from "../settings/regles-optionnelles.js";
import { DialogChateauDormant } from "../sommeil/dialog-chateau-dormant.js";
import { APP_ASTROLOGIE_REFRESH, AppAstrologie } from "../sommeil/app-astrologie.js";
import { AutoAdjustDarkness } from "./auto-adjust-darkness.js";

const TEMPLATE_CALENDRIER = "systems/foundryvtt-reve-de-dragon/templates/time/calendar.hbs";

const INITIAL_CALENDAR_POS = { top: 200, left: 200, horlogeAnalogique: true };
/* -------------------------------------------- */
const { HandlebarsApplicationMixin } = foundry.applications.api;
export class RdDCalendrier extends HandlebarsApplicationMixin(foundry.applications.api.ApplicationV2) {
  static PARTS = {
    main: { template: TEMPLATE_CALENDRIER }
  }

  static DEFAULT_OPTIONS = {
    classes: ["calendar"],
    window: {
      title: "Calendrier",
      controls: []
    },
    position: {
      top: 200,
      left: 200,
      width: 400,
      height: 400
    }
  }

  static initSettings() {
    game.settings.register(SYSTEM_RDD, "liste-nombre-astral", {
      name: "liste-nombre-astral",
      scope: "world",
      config: false,
      default: [],
      type: Object
    });

    game.settings.register(SYSTEM_RDD, "calendrier-pos", {
      name: "calendrierPos",
      scope: "client",
      config: false,
      default: INITIAL_CALENDAR_POS,
      type: Object
    });
  }

  constructor() {
    super({});
    this.horlogeAnalogique = true;
    const savedPos = game.settings.get(SYSTEM_RDD, "calendrier-pos");
    if (savedPos?.top != undefined) {
      this.position = { top: savedPos.top, left: savedPos.left, width: 400, height: 400 }
      this.horlogeAnalogique = savedPos.horlogeAnalogique;
    }
    this.timestamp = RdDTimestamp.getWorldTime();
    if (Misc.isFirstConnectedGM()) {
      RdDTimestamp.setWorldTime(this.timestamp);
      this.rebuildNombresAstraux();
    }
    Hooks.on('updateSetting', async (setting, update, options, id) => this.onUpdateSetting(setting, update, options, id));
  }

  get title() {
    const calendrier = this.timestamp.toCalendrier();
    return `${calendrier.heure.label}, ${calendrier.jourDuMois} ${calendrier.mois.label} ${calendrier.annee} (${calendrier.mois.saison})`;
  }

  savePosition() {
    game.settings.set(SYSTEM_RDD, "calendrier-pos", {
      top: this.position.top,
      left: this.position.left,
      horlogeAnalogique: this.horlogeAnalogique
    });
  }

  getSavePosition() {
    const pos = game.settings.get(SYSTEM_RDD, "calendrier-pos");
    if (pos?.top == undefined) {
      return INITIAL_CALENDAR_POS;
    }
    this.horlogeAnalogique = pos.horlogeAnalogique;
    return pos
  }

  setPosition(position) {
    super.setPosition(position)
    this.savePosition()
  }

  async display() {
    AutoAdjustDarkness.adjust(RdDTimestamp.getWorldTime().darkness);
    this.getSavePosition()
    await this.render({ force: true });
    return this;
  }

  async close() { }

  async onUpdateSetting(setting, update, options, id) {
    if (setting.key == SYSTEM_RDD + '.' + WORLD_TIMESTAMP_SETTING) {
      this.timestamp = RdDTimestamp.getWorldTime();
      this.positionAiguilles()
      this.render();
      Hooks.callAll(APP_ASTROLOGIE_REFRESH)
    }
    if (setting.key == SYSTEM_RDD + '.' + "liste-nombre-astral") {
      Hooks.callAll(APP_ASTROLOGIE_REFRESH)
    }
  }

  _prepareContext() {
    const formData = {};
    this.fillCalendrierData(formData);
    return formData;
  }

  /* -------------------------------------------- */
  fillCalendrierData(formData = {}) {
    foundry.utils.mergeObject(formData, this.timestamp.toCalendrier());
    formData.isGM = game.user.isGM
    formData.heures = RdDTimestamp.definitions()
    formData.horlogeAnalogique = this.horlogeAnalogique
    formData.autoDarkness = AutoAdjustDarkness.isAuto()
    return formData;
  }

  /* -------------------------------------------- */
  _onRender(context, options) {
    super._onRender(context, options);
    this.html = $(this.element);
    this.html.find('.ajout-chronologie').click(ev => DialogChronologie.create());
    this.html.find('.toggle-horloge-analogique').click(ev => this.onToggleHorlogeAnalogique())
    this.html.find('.toggle-auto-darkness').click(ev => this.onToggleAutoDarkness())
    this.html.find('.calendar-btn').click(ev => this.onCalendarButton(ev));
    this.html.find('.horloge-roue .horloge-heure').click(event => {
      const h = this.html.find(event.currentTarget)?.data('heure');
      this.positionnerHeure(Number(h));
    })
    this.positionAiguilles()
    if (!game.user.isGM) return;
    const hdr = this.element.querySelector('.window-header');
    if (!hdr) return;
    if (hdr.querySelector('[data-action="edit-calendar"]')) return;
    const refBtn = hdr.querySelector('[data-action="toggleControls"]');
    if (!refBtn) return;
    const astroBtn = document.createElement('button');
    astroBtn.type = 'button';
    astroBtn.className = 'header-control icon fa-solid fa-moon-over-sun';
    astroBtn.dataset.tooltip = 'Astrologie';
    astroBtn.dataset.action = 'edit-astrology';
    astroBtn.addEventListener('click', () => this.showAstrologieEditor());
    hdr.insertBefore(astroBtn, refBtn);
    const editBtn = document.createElement('button');
    editBtn.type = 'button';
    editBtn.className = 'header-control icon fa-solid fa-calendar-pen';
    editBtn.dataset.tooltip = 'Éditer date';
    editBtn.dataset.action = 'edit-calendar';
    editBtn.addEventListener('click', () => this.showCalendarEditor());
    hdr.insertBefore(editBtn, refBtn);
  }

  positionAiguilles() {
    const timestamp = this.getTimestamp();
    this.html.find(`div.horloge-roue div.horloge-aiguille-heure img`).css(Misc.cssRotation(timestamp.angleHeure));
    this.html.find(`div.horloge-roue div.horloge-aiguille-minute img`).css(Misc.cssRotation(timestamp.angleMinute));
  }

  onToggleHorlogeAnalogique() {
    this.horlogeAnalogique = !this.horlogeAnalogique;
    this.savePosition()
    this.display()
  }

  /* -------------------------------------------- */
  getNombresAstraux() {
    return game.settings.get(SYSTEM_RDD, "liste-nombre-astral") ?? []
  }

  async setNombresAstraux(nombresAstraux) {
    await game.settings.set(SYSTEM_RDD, "liste-nombre-astral", nombresAstraux)
  }

  /* -------------------------------------------- */
  dateCourante() {
    return this.timestamp.formatDate();
  }

  dateReel() {
    return new Date().toLocaleString("sv-SE", {
      year: "numeric",
      month: "2-digit",
      day: "2-digit",
      hour: "2-digit",
      minute: "2-digit"
    });
  }

  isAfterIndexDate(indexDate) {
    return indexDate < this.timestamp.indexDate;
  }

  /* -------------------------------------------- */
  heureCourante() { return RdDTimestamp.definition(this.timestamp.heure); }

  /* -------------------------------------------- */
  getCurrentMinute() { return this.timestamp.indexMinute; }

  getTimestamp() {
    return this.timestamp;
  }
  getTimestampFinChateauDormant(nbJours = 0) {
    return this.timestamp.nouveauJour().addJours(nbJours);
  }

  getTimestampFinHeure(nbHeures = 0) {
    return this.timestamp.nouvelleHeure().addHeures(nbHeures);
  }

  /* -------------------------------------------- */
  getIndexFromDate(jour, mois) {
    const addYear = mois < this.timestamp.mois || (mois == this.timestamp.mois && jour < this.timestamp.jour)
    const time = RdDTimestamp.timestamp(this.timestamp.annee + (addYear ? 1 : 0), mois, jour);
    return time.indexDate;
  }

  /* -------------------------------------------- */
  getJoursSuivants(count) {
    return Misc.intArray(this.timestamp.indexDate, this.timestamp.indexDate + count)
      .map(i => { return { label: RdDTimestamp.formatIndexDate(i), index: i } })
  }

  /* -------------------------------------------- */
  async ajouterNombreAstral(indexDate) {
    const nombreAstral = await RdDDice.rollHeure({ showDice: HIDE_DICE, rollMode: "self" });
    return {
      nombreAstral: nombreAstral,
      lectures: [],
      index: indexDate
    }
  }

  /* -------------------------------------------- */
  async resetNombresAstraux() {
    await Promise.all(game.actors.filter(it => it.type == "personnage").map(async it => await it.deleteNombresAstraux()))
    await this.setNombresAstraux([])
  }

  getNombreAstral(indexDate = undefined) {
    if (indexDate == undefined) {
      indexDate = this.timestamp.indexDate;
    }
    const nombresAstraux = this.getNombresAstraux()
    let astralData = nombresAstraux.find(it => it.index == indexDate);
    return astralData?.nombreAstral ?? 0;
  }

  /* -------------------------------------------- */
  async rebuildNombresAstraux() {
    if (Misc.isFirstConnectedGM()) {
      const nombresAstraux = this.getNombresAstraux()
      let newNombresAstraux = [];
      for (let i = 0; i < MAX_NOMBRE_ASTRAL; i++) {
        let dayIndex = this.timestamp.indexDate + i;
        let na = nombresAstraux.find(it => it.index == dayIndex);
        if (na) {
          newNombresAstraux[i] = na;
        } else {
          newNombresAstraux[i] = await this.ajouterNombreAstral(dayIndex);
        }
      }
      game.actors.filter(it => it.isPersonnage()).forEach(actor => actor.supprimerAnciensNombresAstraux());
      await this.setNombresAstraux(newNombresAstraux);
    }
  }

  /* -------------------------------------------- */
  async setNewTimestamp(newTimestamp) {
    const oldTimestamp = this.timestamp;
    await Promise.all(game.actors.map(actor => actor.onTimeChanging(oldTimestamp, newTimestamp)))
    RdDTimestamp.setWorldTime(newTimestamp);
    if (oldTimestamp.indexDate + 1 == newTimestamp.indexDate && ReglesOptionnelles.isUsing("chateau-dormant-gardien")) {
      await DialogChateauDormant.create();
    }
    this.timestamp = newTimestamp;
    await this.rebuildNombresAstraux();
    this.positionAiguilles()
    this.display();
  }

  /* -------------------------------------------- */
  async onCalendarButton(ev) {
    ev.preventDefault();
    const calendarAvance = ev.currentTarget.attributes['data-calendar-avance'];
    const calendarSet = ev.currentTarget.attributes['data-calendar-set'];
    if (calendarAvance) {
      await this.incrementTime(Number(calendarAvance.value));
    }
    else if (calendarSet) {
      this.positionnerHeure(Number(calendarSet.value));
    }
    this.positionAiguilles()
  }

  /* -------------------------------------------- */
  async incrementTime(minutes = 0) {
    if (game.user.isGM) {
      await this.setNewTimestamp(this.timestamp.addMinutes(minutes));
      Hooks.callAll(APP_ASTROLOGIE_REFRESH);
    }
  }

  /* -------------------------------------------- */
  async incrementerJour() {
    await this.setNewTimestamp(this.timestamp.nouveauJour());
  }

  /* -------------------------------------------- */
  async positionnerHeure(heure) {
    if (game.user.isGM) {
      const indexDate = this.timestamp.indexDate;
      const addDay = this.timestamp.heure < heure ? 0 : 1;
      const newTimestamp = new RdDTimestamp({ indexDate: indexDate + addDay }).addHeures(heure);
      await this.setNewTimestamp(newTimestamp)
      Hooks.callAll(APP_ASTROLOGIE_REFRESH);
    }
  }

  /* -------------------------------------------- */
  getLectureAstrologieDifficulte(dateIndex) {
    let indexNow = this.timestamp.indexDate;
    let diffDay = dateIndex - indexNow;
    return - Math.floor(diffDay / 2);
  }

  /* -------------------------------------------- */
  async requestNombreAstral(request) {
    const actor = game.actors.get(request.id);
    if (Misc.isFirstConnectedGM()) {
      console.log(request);
      let jourDiff = this.getLectureAstrologieDifficulte(request.date);
      let niveau = Number(request.astrologie.system.niveau) + Number(request.conditions) + Number(jourDiff) + Number(request.etat);
      let rollData = {
        caracValue: request.carac_vue,
        finalLevel: niveau,
        showDice: HIDE_DICE,
        rollMode: "blind"
      };
      await RdDResolutionTable.rollData(rollData);
      request.rolled = rollData.rolled;
      request.isValid = request.rolled.isSuccess;
      request.nbAstral = this.getNombreAstral(request.date);

      if (request.rolled.isSuccess) {
        if (request.rolled.isPart) {
          request.competence = actor.getCompetence('Astrologie')
          request.selectedCarac = actor.system.carac["vue"];
          actor.ajoutExperience(request, 'hide');
        }
      }
      else {
        request.nbAstral = await RdDDice.rollTotal("1dhr" + request.nbAstral, {
          rollMode: "self", showDice: HIDE_DICE
        });
      }
      await this.addNbAstralJoueur(actor, request.date, request.nbAstral, request.isValid)
      Hooks.callAll(APP_ASTROLOGIE_REFRESH)
      game.socket.emit(SYSTEM_SOCKET_ID, { msg: "msg_app_astrologie_refresh", data: {} })
    }
  }

  async addNbAstralJoueur(actor, date, nbAstral, isValid) {
    const nombresAstraux = this.getNombresAstraux()
    const astralData = nombresAstraux.find(it => it.index == date)
    if (astralData) {
      astralData.lectures.push({ actorId: actor.id, nombreAstral: nbAstral });
      await this.setNombresAstraux(nombresAstraux);
      await actor.ajouteNombreAstral(date, nbAstral, isValid);
    }
  }

  /* -------------------------------------------- */
  getAjustementAstrologique(heureNaissance, name = undefined) {
    const defHeure = RdDTimestamp.findHeure(heureNaissance);
    if (defHeure) {
      return RdDTimestamp.ajustementAstrologiqueHeure(defHeure.heure, this.getNombreAstral(), this.timestamp.heure);
    }
    else if (name) {
      ui.notifications.warn(name + " n'a pas d'heure de naissance, ou elle est incorrecte : " + heureNaissance);
    }
    else {
      ui.notifications.warn(heureNaissance + " ne correspond pas à une heure de naissance");
    }
    return 0;
  }

  /* -------------------------------------------- */
  async saveEditeur(calendrierData) {
    const newTimestamp = RdDTimestamp.timestamp(
      Number.parseInt(calendrierData.annee),
      calendrierData.mois.heure,
      Number.parseInt(calendrierData.jourMois),
      calendrierData.heure.heure,
      Number.parseInt(calendrierData.minutes)
    );
    await this.setNewTimestamp(newTimestamp);
  }

  /* -------------------------------------------- */
  async showCalendarEditor() {
    const calendrierData = this.fillCalendrierData();
    if (this.editeur == undefined) {
      const html = await foundry.applications.handlebars.renderTemplate('systems/foundryvtt-reve-de-dragon/templates/time/calendar-editor.hbs', calendrierData);
      this.editeur = new RdDCalendrierEditor(html, this, calendrierData)
    }
    this.editeur.updateData(calendrierData);
    this.editeur.render({ force: true });
  }

  /* -------------------------------------------- */
  async showAstrologieEditor() {
    await AppAstrologie.create();
  }

  async onToggleAutoDarkness() {
    await AutoAdjustDarkness.toggle()
    this.display()
  }
}
