/**
 * 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/
 */

/**
 * Loksyu / TinJi settings-based helpers.
 *
 * Data is stored as world settings instead of singleton Actor documents.
 * Socket-based replication allows non-GM players to update Loksyu and
 * TinJi — the GM processes the actual writes.
 */

import { SYSTEM_ID, WU_XING_CYCLE, ASPECT_FACES } from "../../config/constants.js"

const SOCKET_CHANNEL = `system.${SYSTEM_ID}`

/** Read the current loksyu data object from world settings */
export function getLoksyuData() {
  return game.settings.get(SYSTEM_ID, "loksyuData") ?? {
    wood: {yin:0,yang:0}, fire: {yin:0,yang:0}, earth: {yin:0,yang:0}, metal: {yin:0,yang:0}, water: {yin:0,yang:0},
  }
}

/** Write the loksyu data object to world settings (GM only). */
async function writeLoksyuData(data) {
  await game.settings.set(SYSTEM_ID, "loksyuData", data)
  Hooks.callAll("cde:loksyuUpdated", data)
}

/** Write TinJi value to world settings (GM only). */
async function writeTinjiValue(value) {
  value = Math.max(0, value)
  await game.settings.set(SYSTEM_ID, "tinjiData", value)
  Hooks.callAll("cde:tinjiUpdated", value)
}

/** Write the loksyu data object — non-GM emits via socket. */
export async function setLoksyuData(data) {
  if (game.user.isGM) return writeLoksyuData(data)
  game.socket.emit(SOCKET_CHANNEL, { action: "setLoksyuData", data })
}

/** Read current TinJi value from world settings */
export function getTinjiValue() {
  return game.settings.get(SYSTEM_ID, "tinjiData") ?? 0
}

/** Write TinJi value — non-GM emits via socket. */
export async function setTinjiValue(value) {
  if (game.user.isGM) return writeTinjiValue(value)
  game.socket.emit(SOCKET_CHANNEL, { action: "setTinjiValue", value })
}

/**
 * Non-GM: request an atomic Loksyu die draw via socket. The GM reads
 * current state, decrements, and writes — avoiding stale-read races.
 */
export function requestLoksyuDraw(aspect, order) {
  game.socket.emit(SOCKET_CHANNEL, { action: "loksyuDraw", aspect, order })
}

/**
 * Non-GM: request an atomic TinJi spend via socket. The GM reads current
 * value, decrements if > 0, and writes.
 */
export function requestTinjiSpend() {
  game.socket.emit(SOCKET_CHANNEL, { action: "tinjiSpend" })
}

/**
 * Register the socket listener that processes Loksyu/TinJi write requests
 * from non-GM clients. Only the GM actually performs the writes; other
 * clients ignore the message.
 *
 * Must be called after the `ready` hook (when game.socket is available).
 */
export function registerSingletonSocket() {
  game.socket.on(SOCKET_CHANNEL, async (payload) => {
    if (!game.user.isGM) return
    switch (payload.action) {
      case "setLoksyuData":
        await writeLoksyuData(payload.data)
        break
      case "setTinjiValue":
        await writeTinjiValue(payload.value)
        break
      case "updateLoksyuFromRoll":
        await updateLoksyuFromRoll(payload.activeAspect, payload.faces)
        break
      case "updateTinjiFromRoll":
        await updateTinjiFromRoll(payload.delta)
        break
      case "loksyuDraw": {
        const data = getLoksyuData()
        const entry = data[payload.aspect] ?? { yin: 0, yang: 0 }
        const order = payload.order ?? "yang-first"
        if (order === "yin-first") {
          if (entry.yin > 0) entry.yin--
          else entry.yang--
        } else if (order === "balanced") {
          if (entry.yin > entry.yang) entry.yin--
          else if (entry.yang > entry.yin) entry.yang--
          else if (entry.yang > 0) entry.yang--
          else entry.yin--
        } else {
          if (entry.yang > 0) entry.yang--
          else entry.yin--
        }
        data[payload.aspect] = entry
        await writeLoksyuData(data)
        break
      }
      case "tinjiSpend": {
        const cur = getTinjiValue()
        if (cur > 0) await writeTinjiValue(cur - 1)
        break
      }
    }
  })
}

/**
 * After a WuXing roll, add the loksyu faces (yin + yang) of the relevant
 * aspect to the loksyu settings data.
 *
 * Non-GM: emits raw activeAspect+faces via socket so the GM recomputes
 * from current state — avoids stale-read races when two players roll
 * simultaneously.
 *
 * @param {string} activeAspect  - e.g. "fire"
 * @param {Object} faces         - Die face counts { 0: n, 1: n, …, 9: n }
 */
export async function updateLoksyuFromRoll(activeAspect, faces) {
  const cycle = WU_XING_CYCLE[activeAspect]
  if (!cycle) return

  const lokAspect = cycle[3]
  const [yinFace, yangFace] = ASPECT_FACES[lokAspect] ?? []
  if (yinFace === undefined) return

  const yinCount  = faces[yinFace]  ?? 0
  const yangCount = faces[yangFace] ?? 0
  if (yinCount === 0 && yangCount === 0) return

  if (game.user.isGM) {
    const data = getLoksyuData()
    const current = data[lokAspect] ?? { yin: 0, yang: 0 }
    data[lokAspect] = { yin: (current.yin ?? 0) + yinCount, yang: (current.yang ?? 0) + yangCount }
    await writeLoksyuData(data)
  } else {
    game.socket.emit(SOCKET_CHANNEL, { action: "updateLoksyuFromRoll", activeAspect, faces })
  }
}

/**
 * After a WuXing roll, add tinji faces to the TinJi settings.
 *
 * Non-GM: emits delta via socket so the GM adds to the current value
 * atomically.
 *
 * @param {number} count - Number of tinji faces rolled
 */
export async function updateTinjiFromRoll(count) {
  if (!count || count <= 0) return
  if (game.user.isGM) {
    const current = getTinjiValue()
    await writeTinjiValue(current + count)
  } else {
    game.socket.emit(SOCKET_CHANNEL, { action: "updateTinjiFromRoll", delta: count })
  }
}
