import type { CommandBus } from '@application/handlers/CommandBus';
import type { QueryBus } from '@application/handlers/QueryBus';
import type { UIState } from '@presentation/state/UIState';
import type { Enclos } from '@domain/entities/Enclos';
import type { GaugeType } from '@domain/value-objects/GaugeType';
import { GAUGE_DEFS } from '@domain/value-objects/GaugeType';
import { tierNum, tierRate } from '@domain/value-objects/Tier';
import { elapsed, timeToGain } from '@domain/services/GaugeCalculator';
import { enclosGlobalState, enclosGaugeCurGl, computeGaugeLive, calcSerenEtaLive, calcLevelEtaLive } from '@presentation/helpers/gauge-live';
import { esc, fmt, fmtClock } from '@presentation/helpers/format';
import { DragodindeCard } from './DragodindeCard';
import { Toast } from './Toast';
import { ConfirmModal } from './ConfirmModal';
import { UndoManager } from '@presentation/services/UndoManager';

const ALL_GAUGES: GaugeType[] = ['baffeur', 'caresseur', 'foudroyeur', 'abreuvoir', 'dragofesse', 'mangeoire'];

export class EnclosView {
  private el: HTMLElement | null = null;
  private enclosId = 0;
  private ddCards: Map<number, DragodindeCard> = new Map();

  constructor(
    private commandBus: CommandBus,
    private queryBus: QueryBus,
    private uiState: UIState,
  ) {}

  render(container: HTMLElement, enclosId: number): void {
    this.enclosId = enclosId;
    this.el = document.createElement('div');
    this.el.className = 'enclos-view';
    container.appendChild(this.el);
    this.renderInner();
  }

  private getEnc(): Enclos {
    return this.queryBus.execute<Enclos>({ type: 'get-enclos-detail', enclosId: this.enclosId });
  }

  private renderInner(): void {
    if (!this.el) return;
    const enc = this.getEnc();
    const eId = this.enclosId;
    const started = !!enc.timer.startTime;
    const running = enc.timer.running;

    /* Gauge toggle buttons */
    const SEREN_PAIR: Record<string, GaugeType> = { baffeur: 'caresseur', caresseur: 'baffeur' };
    const gaugeBtnsHtml = ALL_GAUGES.map(gid => {
      const def = GAUGE_DEFS[gid];
      const active = enc.activeGauges.includes(gid);
      const locked = started && !enc.alerted['__done__'];
      // Exclusion mutuelle : si la jauge opposée (baffeur↔caresseur) est active, on bloque
      const opposite = SEREN_PAIR[gid];
      const blocked = !active && !!opposite && enc.activeGauges.includes(opposite);
      const cls = `gauge-btn enc-gauge-toggle${active ? ' on' : ''}${locked || blocked ? ' locked' : ''}`;
      return `<button class="${cls}" data-gid="${gid}" style="${active ? `border-color:var(${def.cssVar})` : ''}"
        ${locked || blocked ? 'disabled' : ''}>${def.icon} ${def.label}</button>`;
    }).join('');

    /* Active gauge config blocks */
    const gaugeConfigsHtml = enc.activeGauges.map(gid => {
      const def = GAUGE_DEFS[gid];
      const lvl = enc.gaugeLevels[gid] || 0;
      const tn = tierNum(lvl);
      const tr = tierRate(lvl);
      const pct = Math.min(100, (lvl / 100000) * 100);
      const emptyTime = timeToGain(lvl, lvl);
      const emptyStr = emptyTime === Infinity ? '∞' : fmt(emptyTime);
      return `<div class="enc-gauge-card" data-gid="${gid}" id="gcfg-${eId}-${gid}">
        <div class="enc-gauge-card-head">
          <div class="enc-gauge-card-name" style="color:var(${def.cssVar})">${def.icon} ${def.label.toUpperCase()}</div>
          <span class="enc-tier-badge" id="gtier-${eId}-${gid}"
            style="color:var(${def.cssVar});border-color:color-mix(in srgb,var(${def.cssVar}) 40%,transparent);background:color-mix(in srgb,var(${def.cssVar}) 15%,transparent)">Tier ${tn} · ±${tr}/tick</span>
        </div>
        <div class="enc-gauge-bar-bg">
          <div class="enc-gauge-bar-inner">
            <div class="enc-gauge-bar-fill" id="gbar-${eId}-${gid}" style="width:${pct.toFixed(1)}%;background:linear-gradient(to right,color-mix(in srgb,var(${def.cssVar}) 70%,#000),var(${def.cssVar}))"></div>
          </div>
        </div>
        <div class="enc-gauge-bottom">
          <div class="enc-gauge-val-group">
            <input type="number" class="gauge-inp enc-gauge-inp${started ? ' gauge-inp-recharge recharge' : ''}" id="glvl-${eId}-${gid}"
              data-gid="${gid}" data-prev="${lvl}" data-running="${started ? '1' : '0'}"
              min="0" max="100000" step="1000" value="${lvl}"
              title="${started ? 'Recharger la jauge en cours de session' : ''}">
            <span class="enc-gauge-inp-max">/ 100 000</span>
          </div>
          <div class="enc-gauge-info" id="gempty-${eId}-${gid}">Vide en ${emptyStr}</div>
        </div>
      </div>`;
    }).join('');

    /* Timer button */
    const timerIcon = running ? 'pause' : 'play_arrow';
    const timerBtnClass = running ? 'enc-start-btn enc-btn-pause' : 'enc-start-btn';
    const timerBtnText  = running ? 'PAUSE' : (enc.timer.pausedAt && !enc.alerted['__done__'] ? 'REPRENDRE' : 'DÉMARRER');

    /* DD count */
    const ddCount = enc.dragodindes.length;
    const ddMax = 10;

    this.el.innerHTML = `
      <div class="enc-view-inner">

        <!-- Panel glassmorphism principal -->
        <div class="enc-panel">

          <!-- Ligne principale : [nom + vider] ←→ [elapsed | alarme | bouton] -->
          <div class="enc-header-row">
            <!-- Gauche : nom + vider -->
            <div class="enc-header-left">
              <input type="text" class="enc-name-input" value="${esc(enc.name)}"
                size="${Math.max(4, enc.name.length + 1)}"
                data-prev="${esc(enc.name)}" id="ename-${eId}">
              <button class="enc-clear-btn" id="eclear-${eId}">
                <span class="material-symbols-outlined" style="font-size:13px">delete</span>
                Vider l'enclos
              </button>
            </div>
            <!-- Droite : temps écoulé + alarme + démarrer -->
            <div class="enc-header-right">
              <div class="enc-time-block">
                <div class="enc-time-lbl">Temps Écoulé</div>
                <div class="enc-elapsed" id="elapsed-${eId}">00:00:00</div>
              </div>
              <div class="enc-time-block">
                <div class="enc-time-lbl enc-alarm-lbl">
                  <span class="material-symbols-outlined mso-fill" style="font-size:12px;color:#00ff00">notifications</span>
                  Alarme dans
                </div>
                <div class="enc-alarm" id="gcd-${eId}">--:--:--</div>
              </div>
              <button class="${timerBtnClass}" id="tbtn-${eId}">
                <span class="material-symbols-outlined">${timerIcon}</span>${timerBtnText}
              </button>
              <button class="enc-reset-btn" id="treset-${eId}" ${!started ? 'style="display:none"' : ''} title="Réinitialiser le timer">
                <span class="material-symbols-outlined" style="font-size:18px">restart_alt</span>
              </button>
            </div>
          </div>

          <!-- Jauges actives -->
          <div class="enc-gauge-label">Jauges Actives :</div>
          <div class="enc-gauge-toggles" id="gtoggle-${eId}">${gaugeBtnsHtml}</div>
          <div class="enc-gauges-grid" id="gconfigs-${eId}">${gaugeConfigsHtml}</div>

        </div>

        <!-- Bandeau session terminée -->
        <div class="enc-done-banner" id="done-banner-${eId}" style="display:none">
          <span class="material-symbols-outlined mso-fill" style="color:#22c55e;font-size:22px">check_circle</span>
          <div class="enc-done-texts">
            <div class="enc-done-title">Session terminée !</div>
            <div class="enc-done-sub">Toutes les cibles ont été atteintes</div>
          </div>
          <button class="enc-done-reset-btn" id="done-reset-${eId}" style="display:none">
            <span class="material-symbols-outlined" style="font-size:16px">refresh</span>
            Nouvelle fournée
          </button>
        </div>

        <!-- Section Dragodindes -->
        <div class="enc-dd-section">
          <div class="enc-dd-head">
            <h3 class="enc-dd-title">
              Dragodindes <span class="enc-dd-count" id="ddcount-${eId}">${ddCount}/${ddMax}</span>
            </h3>
            <button class="enc-add-dd-btn" id="adddd-${eId}" ${ddCount >= ddMax ? 'disabled' : ''}>
              <span class="material-symbols-outlined" style="font-size:16px">add</span>
              Ajouter une Dragodinde
            </button>
          </div>
          <div class="dd-grid" id="dd-grid-${eId}"></div>
        </div>

      </div>
    `;

    this.bindEvents(enc);
    this.renderDdCards(enc);
  }

  private bindEvents(enc: Enclos): void {
    if (!this.el) return;
    const eId = this.enclosId;

    /* Enclos name input */
    const nameInput = this.el.querySelector<HTMLInputElement>(`#ename-${eId}`);
    if (nameInput) {
      nameInput.addEventListener('focus', () => {
        nameInput.dataset.prev = nameInput.value;
        nameInput.value = '';
      });
      nameInput.addEventListener('blur', () => {
        const v = nameInput.value.trim();
        if (!v) nameInput.value = nameInput.dataset.prev || enc.name;
        else this.commandBus.execute({ type: 'rename-enclos', enclosId: eId, name: v });
      });
      nameInput.addEventListener('keydown', (e) => {
        if (e.key === 'Escape') { nameInput.value = nameInput.dataset.prev || enc.name; nameInput.blur(); }
        else if (e.key === 'Enter') nameInput.blur();
      });
    }

    /* Clear enclos */
    this.el.querySelector(`#eclear-${eId}`)?.addEventListener('click', async () => {
      const ok = await ConfirmModal.show('Vider l\'enclos', 'Toutes les dragodindes seront supprimées. Continuer ?');
      if (!ok) return;
      const hasSnap = await UndoManager.snapshotCurrent('Enclos vidé');
      this.commandBus.execute({ type: 'clear-enclos', enclosId: eId });
      Toast.show('success', 'Enclos vidé.', hasSnap ? { label: 'Annuler', callback: () => UndoManager.undo() } : undefined);
      this.renderInner();
    });

    /* Gauge toggles */
    this.el.querySelectorAll<HTMLButtonElement>('.gauge-btn').forEach(btn => {
      btn.addEventListener('click', () => {
        const gid = btn.dataset.gid as GaugeType;
        this.commandBus.execute({ type: 'toggle-gauge', enclosId: eId, gaugeId: gid });
        this.renderInner();
      });
    });

    /* Gauge level inputs */
    this.el.querySelectorAll<HTMLInputElement>('.gauge-inp').forEach(inp => {
      inp.addEventListener('focus', () => {
        inp.dataset.prev = inp.value;
        inp.value = '';
      });
      inp.addEventListener('blur', () => {
        if (inp.value === '') { inp.value = inp.dataset.prev || '0'; return; }
        const v = Math.min(100000, Math.max(0, Number(inp.value)));
        if (isNaN(v)) { inp.value = inp.dataset.prev || '0'; return; }
        const gid = inp.dataset.gid as GaugeType;
        if (inp.dataset.running === '1') {
          this.commandBus.execute({ type: 'recharge-gauge', enclosId: eId, gaugeId: gid, level: v });
        } else {
          this.commandBus.execute({ type: 'update-gauge-level', enclosId: eId, gaugeId: gid, level: v });
        }
        inp.value = String(v);
      });
      inp.addEventListener('input', () => {
        if (!inp.value) return;
        const v = Math.min(100000, Math.max(0, Number(inp.value)));
        if (isNaN(v)) return;
        const gid = inp.dataset.gid as GaugeType;
        if (inp.dataset.running === '1') {
          // Recharge en temps réel pendant la session (consolidé côté command)
          this.commandBus.execute({ type: 'recharge-gauge', enclosId: eId, gaugeId: gid, level: v });
        } else {
          this.commandBus.execute({ type: 'update-gauge-level', enclosId: eId, gaugeId: gid, level: v });
        }
      });
      inp.addEventListener('keydown', (e) => {
        if (e.key === 'Escape') { inp.value = inp.dataset.prev || '0'; inp.blur(); }
        else if (e.key === 'Enter') inp.blur();
      });
    });

    /* Timer start/pause */
    this.el.querySelector(`#tbtn-${eId}`)?.addEventListener('click', () => {
      const freshEnc = this.getEnc();
      if (freshEnc.timer.running) {
        this.commandBus.execute({ type: 'stop-timer', enclosId: eId });
      } else {
        this.commandBus.execute({ type: 'start-timer', enclosId: eId });
      }
      this.renderInner();
    });

    /* Reset timer */
    this.el.querySelector(`#treset-${eId}`)?.addEventListener('click', () => {
      this.commandBus.execute({ type: 'reset-timer', enclosId: eId });
      this.renderInner();
    });

    /* Done banner — Nouvelle fournée */
    this.el.querySelector(`#done-reset-${eId}`)?.addEventListener('click', () => {
      this.commandBus.execute({ type: 'nouvelle-fournee', enclosId: eId });
      Toast.show('success', 'Nouvelle fournée lancée.');
      this.renderInner();
    });

    /* Add DD */
    this.el.querySelector(`#adddd-${eId}`)?.addEventListener('click', () => {
      this.commandBus.execute({ type: 'add-dragodinde', enclosId: eId });
      this.renderInner();
    });
  }

  private renderDdCards(enc: Enclos): void {
    const grid = this.el?.querySelector(`#dd-grid-${this.enclosId}`);
    if (!grid) return;

    /* Destroy old cards */
    this.ddCards.forEach(card => card.destroy());
    this.ddCards.clear();

    /* Create new cards */
    enc.dragodindes.forEach(dd => {
      const card = new DragodindeCard(this.commandBus, this.queryBus, () => this.renderInner());
      card.render(grid as HTMLElement, this.enclosId, dd.id);
      this.ddCards.set(dd.id, card);
    });
  }

  update(): void {
    if (!this.el) return;
    const enc = this.getEnc();
    const eId = this.enclosId;
    const { globalMax, allDone, started, el: elSec, ddDone } = enclosGlobalState(enc);
    const running = enc.timer.running;

    /* Complétion automatique : toutes les cibles atteintes → une seule alarme */
    if (allDone && running && enc.dragodindes.length > 0 && enc.activeGauges.length > 0) {
      this.commandBus.execute({ type: 'complete-timer', enclosId: eId });
      this.renderInner();
      return;
    }

    /* Elapsed clock */
    const elapsedEl = this.el.querySelector(`#elapsed-${eId}`);
    if (elapsedEl) elapsedEl.textContent = fmtClock(elSec);

    /* Global countdown */
    const gcdEl = this.el.querySelector(`#gcd-${eId}`);
    if (gcdEl) {
      if (enc.activeGauges.length > 0 && enc.dragodindes.length > 0) {
        if (allDone) {
          gcdEl.textContent = '✅';
        } else if (!isFinite(globalMax)) {
          gcdEl.textContent = '∞';
        } else {
          gcdEl.textContent = fmtClock(globalMax);
        }
      } else {
        gcdEl.textContent = '--:--:--';
      }
    }

    /* Timer button state */
    const tbtn = this.el.querySelector<HTMLButtonElement>(`#tbtn-${eId}`);
    if (tbtn) {
      const timerIcon = running ? 'pause' : 'play_arrow';
      const timerText = running ? 'PAUSE' : (enc.timer.pausedAt && !enc.alerted['__done__'] ? 'REPRENDRE' : 'DÉMARRER');
      tbtn.className = running ? 'enc-start-btn enc-btn-pause' : 'enc-start-btn';
      tbtn.innerHTML = `<span class="material-symbols-outlined">${timerIcon}</span>${timerText}`;
    }

    /* Reset button visibility */
    const resetBtn = this.el.querySelector<HTMLElement>(`#treset-${eId}`);
    if (resetBtn) resetBtn.style.display = started ? '' : 'none';

    /* Gauge config live updates (tier badges and bars decay over time) */
    enc.activeGauges.forEach(gid => {
      const startGl = started ? (enc.timer.snapGauges[gid] ?? enc.gaugeLevels[gid]) : enc.gaugeLevels[gid];
      const curGl = started ? enclosGaugeCurGl(enc, gid) : startGl;
      const tn = tierNum(curGl);
      const tr = curGl > 0 ? tierRate(curGl) : 0;
      const pct = Math.min(100, (curGl / 100000) * 100);
      const emptyTime = curGl > 0 ? timeToGain(curGl, curGl) : 0;
      const emptyStr = emptyTime === Infinity ? '∞' : fmt(emptyTime);

      const tierEl = this.el!.querySelector(`#gtier-${eId}-${gid}`);
      if (tierEl) {
        tierEl.textContent = curGl > 0 ? `Tier ${tn} · ±${tr}/tick` : 'Jauge vide';
      }

      const barEl = this.el!.querySelector<HTMLElement>(`#gbar-${eId}-${gid}`);
      if (barEl) barEl.style.width = `${pct.toFixed(1)}%`;

      const emptyEl = this.el!.querySelector(`#gempty-${eId}-${gid}`);
      if (emptyEl) {
        if (curGl > 0) {
          emptyEl.textContent = `Vide en ${emptyStr}`;
          emptyEl.classList.remove('enc-gauge-alert');
        } else if (started && running) {
          emptyEl.textContent = '⚠ Rechargez la jauge';
          emptyEl.classList.add('enc-gauge-alert');
        } else {
          emptyEl.textContent = 'Vide';
          emptyEl.classList.remove('enc-gauge-alert');
        }
      }

      /* Mettre à jour la valeur affichée de l'input et son état en temps réel */
      const inp = this.el!.querySelector<HTMLInputElement>(`#glvl-${eId}-${gid}`);
      if (inp) {
        // Synchroniser data-running avec l'état réel du timer
        inp.dataset.running = started ? '1' : '0';
        // Mettre à jour la valeur affichée (sauf si l'utilisateur est en train de taper)
        if (document.activeElement !== inp) {
          inp.value = String(Math.round(started ? curGl : (enc.gaugeLevels[gid] ?? 0)));
        }
      }
    });

    /* Done banner */
    const doneBanner = this.el.querySelector<HTMLElement>(`#done-banner-${eId}`);
    if (doneBanner) {
      doneBanner.style.display = (allDone && started && enc.dragodindes.length > 0 && enc.activeGauges.length > 0) ? '' : 'none';
    }

    /* Bouton "Nouvelle fournée" — visible uniquement si toutes les DD ont 20000 en maturité, endurance et amour */
    const doneResetBtn = this.el.querySelector<HTMLElement>(`#done-reset-${eId}`);
    if (doneResetBtn) {
      const allMaxed = enc.dragodindes.length > 0 && enc.dragodindes.every(dd =>
        dd.stats.maturite >= 20000 && dd.stats.endurance >= 20000 && dd.stats.amour >= 20000
      );
      doneResetBtn.style.display = allMaxed ? '' : 'none';
    }

    /* DD count */
    const ddCountEl = this.el.querySelector(`#ddcount-${eId}`);
    if (ddCountEl) ddCountEl.textContent = `${enc.dragodindes.length}/10`;

    /* Add button state */
    const addBtn = this.el.querySelector<HTMLButtonElement>(`#adddd-${eId}`);
    if (addBtn) addBtn.disabled = enc.dragodindes.length >= 10;

    /* Update each DD card */
    enc.dragodindes.forEach(dd => {
      const card = this.ddCards.get(dd.id);
      if (card) card.update(enc, dd, elSec, started);
    });

    /* Clean up cards for removed DDs */
    const currentDdIds = new Set(enc.dragodindes.map(d => d.id));
    this.ddCards.forEach((card, id) => {
      if (!currentDdIds.has(id)) {
        card.destroy();
        this.ddCards.delete(id);
      }
    });
  }

  destroy(): void {
    this.ddCards.forEach(card => card.destroy());
    this.ddCards.clear();
    this.el?.remove();
    this.el = null;
  }
}
