/**
 * Chroniques de l'Étrange — Système FoundryVTT
 *
 * Chroniques de l'Étrange est un jeu de rôle édité par Antre-Monde Éditions.
 * Ce système FoundryVTT est une implémentation indépendante et n'est pas
 * affilié à Antre-Monde Éditions,
 * mais a été réalisé avec l'autorisation d'Antre-Monde Éditions.
 *
 * @author    LeRatierBretonnien
 * @copyright 2024–2026 LeRatierBretonnien
 * @license   CC BY-NC-SA 4.0 – https://creativecommons.org/licenses/by-nc-sa/4.0/
 */

import { advanceCombatantPosition } from "../../documents/combat.js"

const WHEEL_TEMPLATE = "systems/fvtt-chroniques-de-l-etrange/templates/apps/cde-wheel-app.html"

/**
 * Action costs for common combat actions (in crans).
 * Listed in order from cheapest to most expensive.
 */
export const ACTION_COSTS = [
  { key: "draw",        label: "CDE.ActionCostDraw",        cost: 1 },
  { key: "changestyle", label: "CDE.ActionCostChangeStyle", cost: 1 },
  { key: "defense",     label: "CDE.ActionCostDefense",     cost: 1 },
  { key: "aim",         label: "CDE.ActionCostAim",         cost: 2 },
  { key: "help",        label: "CDE.ActionCostHelp",        cost: 2 },
  { key: "defally",    label: "CDE.ActionCostDefendAlly",  cost: 2 },
  { key: "move",        label: "CDE.ActionCostMove",        cost: 2 },
  { key: "attack",      label: "CDE.ActionCostAttack",      cost: 3 },
  { key: "delay",       label: "CDE.ActionCostDelay",       cost: 6 },
]

/**
 * Wu Xing color segments for the 24-cran wheel.
 * 6 colors × 4 crans = 24. Each colour covers crans [start..start+3].
 * An effect lasting 6 crans returns to the same colour.
 */
const WHEEL_SEGMENTS = [
  { label: "Métal",    color: "#b8c4cc", textColor: "#1a1a1a", crans: [1,  2,  3,  4]  },
  { label: "Eau",      color: "#3a7bd5", textColor: "#ffffff", crans: [5,  6,  7,  8]  },
  { label: "Terre",    color: "#c8a84b", textColor: "#1a1a1a", crans: [9,  10, 11, 12] },
  { label: "Feu",      color: "#d94f3d", textColor: "#ffffff", crans: [13, 14, 15, 16] },
  { label: "Bois",     color: "#4a9b5a", textColor: "#ffffff", crans: [17, 18, 19, 20] },
  { label: "Repère",   color: "#1a1a2e", textColor: "#aaaaaa", crans: [21, 22, 23, 24] },
]

/** Return the segment data for a given cran (1–24). */
function segmentForCran(cran) {
  return WHEEL_SEGMENTS.find(s => s.crans.includes(cran)) ?? WHEEL_SEGMENTS[0]
}

/**
 * Roue d'Initiative — visual initiative wheel for CDE combat.
 *
 * Shows all combatants in the current combat scene on a 24-cran wheel.
 * Provides action-cost buttons to advance a combatant's position.
 *
 * Singleton: open via CDEWheelApp.open().
 */
export class CDEWheelApp extends foundry.applications.api.ApplicationV2 {
  static DEFAULT_OPTIONS = {
    id:       "cde-wheel-app",
    classes:  ["cde-wheel-app"],
    tag:      "div",
    window:   {
      title:     "CDE.InitiativeWheel",
      icon:      "fas fa-circle-notch",
      resizable: true,
    },
    position: { width: 820, height: 620 },
    actions:  {
      advanceCran:   CDEWheelApp.#advanceCran,
      setSurprised:  CDEWheelApp.#setSurprised,
      rollInitiative: CDEWheelApp.#rollInitiative,
    },
  }

  /** @type {CDEWheelApp|null} */
  static #instance = null

  /** Open (or bring to front) the singleton instance. */
  static open() {
    if (!CDEWheelApp.#instance || CDEWheelApp.#instance.rendered === false) {
      CDEWheelApp.#instance = new CDEWheelApp()
      CDEWheelApp.#instance.render(true)
    } else {
      CDEWheelApp.#instance.bringToFront()
    }
    return CDEWheelApp.#instance
  }

  /** Currently selected combatant id (for action panel). */
  #selectedId = null

  async _prepareContext(options) {
    const combat      = game.combat
    const combatants  = combat ? [...combat.combatants.values()] : []
    const sorted      = [...combatants].sort((a, b) => (b.initiative ?? 0) - (a.initiative ?? 0))
    const cranData    = this.#buildCranData(combatants)
    const selected    = this.#selectedId
      ? combatants.find(c => c.id === this.#selectedId)
      : null
    const actionCosts = ACTION_COSTS.map(a => ({
      ...a,
      label: game.i18n.localize(a.label),
    }))

    return {
      hasCombat:   !!combat,
      combatants:  sorted.map(c => ({
        id:         c.id,
        name:       c.name,
        img:        c.token?.texture?.src ?? c.actor?.img ?? "icons/svg/mystery-man.svg",
        initiative: c.initiative ?? "—",
        segment:    segmentForCran(c.initiative ?? 1),
        isActive:   combat?.current?.combatantId === c.id,
        isSelected: c.id === this.#selectedId,
        hasInitiative: c.initiative != null,
      })),
      cranData,
      selected,
      selectedName:  selected?.name ?? null,
      actionCosts,
    }
  }

  async _renderHTML(context, options) {
    return foundry.applications.handlebars.renderTemplate(WHEEL_TEMPLATE, context)
  }

  _replaceHTML(result, content, options) {
    content.innerHTML = result
    this.#bindEvents(content)
  }

  /** Build per-cran data for the SVG wheel. */
  #buildCranData(combatants) {
    const data = []
    for (let cran = 1; cran <= 24; cran++) {
      const segment  = segmentForCran(cran)
      const fighters = combatants.filter(c => Math.round(c.initiative) === cran)
      data.push({ cran, segment, fighters })
    }
    return data
  }

  /** Bind click events for combatant selection. */
  #bindEvents(content) {
    content.querySelectorAll("[data-select-combatant]").forEach(el => {
      el.addEventListener("click", () => {
        this.#selectedId = el.dataset.selectCombatant
        this.render()
      })
    })
  }

  /** Action: advance selected combatant by given cran cost. */
  static async #advanceCran(event, element) {
    const app  = CDEWheelApp.#instance
    if (!app?.#selectedId) return
    const cost = parseInt(element.dataset.cost, 10)
    if (!cost || isNaN(cost)) return
    const combatant = game.combat?.combatants.get(app.#selectedId)
    if (!combatant) return
    await advanceCombatantPosition(combatant, cost)
  }

  /** Action: set selected combatant to surprised (position 1 = reference). */
  static async #setSurprised(event, element) {
    const app = CDEWheelApp.#instance
    if (!app?.#selectedId) return
    const combatant = game.combat?.combatants.get(app.#selectedId)
    if (!combatant) return
    // Update combatant only — updateCombatant hook in system.js syncs actor
    await combatant.update({ initiative: 1 })
  }

  /** Action: open the initiative dialog for the selected combatant. */
  static async #rollInitiative(event, element) {
    const app = CDEWheelApp.#instance
    if (!app?.#selectedId) return
    const combatant = game.combat?.combatants.get(app.#selectedId)
    if (!combatant) return
    await game.combat.rollInitiative([app.#selectedId])
  }

  /** Re-render when combat state changes. */
  static registerHooks() {
    const refresh = () => {
      if (CDEWheelApp.#instance?.rendered) CDEWheelApp.#instance.render()
    }
    Hooks.on("updateCombat",    refresh)
    Hooks.on("updateCombatant", refresh)
    Hooks.on("createCombatant", refresh)
    Hooks.on("deleteCombatant", refresh)
    Hooks.on("updateActor",     (_actor, diff) => {
      if (foundry.utils.hasProperty(diff, "system.initiative")) refresh()
    })
    Hooks.on("deleteCombat", () => {
      if (CDEWheelApp.#instance?.rendered) CDEWheelApp.#instance.render()
    })
  }
}
