import type { CommandBus } from '@application/handlers/CommandBus';
import type { QueryBus } from '@application/handlers/QueryBus';
import { RACES_DATA, GEN_COLORS, RACE_GEN, BREEDING_RECIPES } from '@domain/value-objects/Race';
import { getDDImage } from '@presentation/helpers/dd-image';
import { esc } from '@presentation/helpers/format';

interface ApproNeeds {
  total: number;
  m: number;
  f: number;
}

interface ApproStep {
  baby: string;
  parentA: string;
  parentB: string;
  couples: number;
  gen: number;
}

interface ApproState {
  target: string;
  qty: number;
  repro: Record<string, number>;
  inverted: Record<string, boolean>;
  genFilter: number;
  search: string;
}

export class ReapproView {
  private el: HTMLElement | null = null;
  private approState: ApproState = { target: '', qty: 1, repro: {}, inverted: {}, genFilter: 0, search: '' };
  private dirty = true;

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

  render(container: HTMLElement): void {
    this.el = document.createElement('div');
    this.el.className = 'reappro-view-new';
    container.appendChild(this.el);
    this.dirty = true; this.update();
  }

  update(): void {
    if (!this.el || !this.dirty) return;
    this.dirty = false;
    const { target } = this.approState;

    if (target) {
      this.renderResults();
    } else {
      this.renderTargetSelection();
    }
  }

  destroy(): void {
    this.el?.remove();
    this.el = null;
  }

  /* ── Target selection ── */
  private renderTargetSelection(): void {
    if (!this.el) return;
    const { genFilter, search } = this.approState;

    const allRaces: { name: string; gen: number }[] = [];
    for (const [g, rs] of Object.entries(RACES_DATA)) {
      const gen = parseInt(g);
      if (gen < 2) continue;
      for (const r of rs) allRaces.push({ name: r.name, gen });
    }

    const q = search.trim().toLowerCase();
    const filtered = allRaces.filter(r =>
      (genFilter > 0 ? r.gen === genFilter : true) &&
      (q ? r.name.toLowerCase().includes(q) : true)
    );

    let html = '';

    // Header
    html += `<div class="reappro-section-header">
      <div>
        <h2 class="reappro-section-title">Sélectionne ta cible</h2>
        <div class="reappro-title-bar"></div>
      </div>
      <div class="accoup-gen-chips" style="margin-bottom:0;border-bottom:none;padding-bottom:0">
        <button class="accoup-gen-chip${genFilter === 0 ? ' active' : ''}" data-gen="0">Toutes</button>`;
    for (let g = 2; g <= 10; g++) {
      html += `<button class="accoup-gen-chip${genFilter === g ? ' active' : ''}" data-gen="${g}">Gen ${g}</button>`;
    }
    html += `</div></div>`;

    // Search
    html += `<div class="accoup-search-wrap">
      <input class="accoup-search" id="appro-search-input" type="text"
        placeholder="Rechercher une race…" value="${esc(search)}" autocomplete="off">
      ${search ? `<button class="accoup-search-clear" title="Effacer"><span class="material-symbols-outlined" style="font-size:16px">close</span></button>` : ''}
    </div>`;

    // Race grid
    if (filtered.length === 0) {
      html += `<div class="accoup-empty">Aucune race trouvée</div>`;
    } else {
      html += `<div class="accoup-race-grid">`;
      for (const race of filtered) {
        const genCol = GEN_COLORS[race.gen] ?? '#888';
        html += `<div class="accoup-race-card" data-race="${esc(race.name)}">
          <div class="accoup-race-card-img">
            ${getDDImage(race.name)}
            <div class="accoup-race-card-gen" style="color:${genCol}">GEN ${race.gen}</div>
          </div>
          <div class="accoup-race-card-name">${esc(race.name)}</div>
        </div>`;
      }
      html += `</div>`;
    }

    this.el.innerHTML = html;
    this.bindTargetSelectionEvents();

    if (search) {
      const inp = this.el.querySelector<HTMLInputElement>('#appro-search-input');
      if (inp) { inp.focus(); inp.setSelectionRange(inp.value.length, inp.value.length); }
    }
  }

  private bindTargetSelectionEvents(): void {
    if (!this.el) return;

    const searchInput = this.el.querySelector<HTMLInputElement>('#appro-search-input');
    if (searchInput) {
      searchInput.addEventListener('input', () => {
        this.approState.search = searchInput.value;
        this.dirty = true; this.update();
      });
    }

    const clearBtn = this.el.querySelector('.accoup-search-clear');
    if (clearBtn) {
      clearBtn.addEventListener('click', () => {
        this.approState.search = '';
        this.dirty = true; this.update();
      });
    }

    this.el.querySelectorAll('.accoup-gen-chip').forEach(btn => {
      btn.addEventListener('click', () => {
        this.approState.genFilter = parseInt((btn as HTMLElement).dataset.gen ?? '0');
        this.dirty = true; this.update();
      });
    });

    this.el.querySelectorAll('.accoup-race-card').forEach(card => {
      card.addEventListener('click', () => {
        this.approState.target = (card as HTMLElement).dataset.race!;
        this.approState.qty = 1;
        this.approState.repro = {};
        this.approState.inverted = {};
        this.approState.search = '';
        this.dirty = true; this.update();
      });
    });
  }

  /* ── Results view ── */
  private renderResults(): void {
    if (!this.el) return;
    const { target, qty } = this.approState;
    const targetGen = RACE_GEN[target] ?? 0;
    const { materials, steps } = this.calcAppro(target, qty);

    // Group steps by generation
    const stepsByGen = new Map<number, ApproStep[]>();
    for (const step of steps) {
      const list = stepsByGen.get(step.gen) ?? [];
      list.push(step);
      stepsByGen.set(step.gen, list);
    }
    const sortedGens = [...stepsByGen.keys()].sort((a, b) => a - b);

    let html = '';

    // Top bar: back + quantity (top-left)
    html += `<div class="reappro-top-bar">
      <button class="reappro-back-btn" id="appro-back">
        <span class="material-symbols-outlined" style="font-size:18px">arrow_back</span>
        Retour
      </button>
      <div class="reappro-qty-wrap">
        <label class="accoup-center-label">Quantité</label>
        <input class="accoup-center-input" id="appro-qty" type="number" min="1" value="${qty}" style="width:70px">
      </div>
    </div>`;

    // Materials (Gen 1)
    if (materials.length > 0) {
      const totalMat = materials.reduce((s, m) => s + m.m + m.f, 0);
      html += `<div class="reappro-step-panel reappro-step-primary">`;
      html += `<div class="reappro-step-header reappro-step-header-primary">
        <h3 class="reappro-step-title">
          <span class="reappro-step-badge reappro-badge-primary">1</span>
          MATIÈRES PREMIÈRES — GÉNÉRATION 1
        </h3>
        <span class="reappro-step-count">Total : ${totalMat} dragodindes requises</span>
      </div>`;
      html += `<div class="reappro-materials-grid">`;
      for (const mat of materials) {
        html += this.renderMaterialCard(mat.race, mat.m, mat.f);
      }
      html += `</div></div>`;

      // Arrow separator
      html += `<div class="reappro-arrow-sep">
        <span class="material-symbols-outlined reappro-arrow-icon">expand_more</span>
      </div>`;
    }

    // Steps grouped by generation
    let stepNum = 2;
    for (let gi = 0; gi < sortedGens.length; gi++) {
      const gen = sortedGens[gi];
      const genSteps = stepsByGen.get(gen)!;
      const isFinalGen = gi === sortedGens.length - 1;
      const genCol = GEN_COLORS[gen] ?? '#888';

      const panelClass = isFinalGen ? 'reappro-step-panel reappro-step-final' : 'reappro-step-panel';
      const headerClass = isFinalGen ? 'reappro-step-header reappro-step-header-final' : 'reappro-step-header';
      const badgeClass = isFinalGen ? 'reappro-step-badge reappro-badge-primary' : 'reappro-step-badge';

      html += `<div class="${panelClass}">`;
      html += `<div class="${headerClass}">
        <h3 class="reappro-step-title">
          <span class="${badgeClass}">${stepNum}</span>
          ${isFinalGen ? 'ÉTAPE FINALE' : 'CROISEMENTS'} — GÉNÉRATION ${gen}
        </h3>
        <span class="reappro-step-count">${genSteps.length} croisement${genSteps.length > 1 ? 's' : ''}</span>
      </div>`;

      html += `<div class="reappro-crossings-grid">`;
      for (const step of genSteps) {
        const invKey = step.baby;
        const isInverted = this.approState.inverted[invKey] ?? false;
        const reproCount = this.approState.repro[invKey] ?? 0;
        const isLast = isFinalGen && genSteps.length === 1;

        html += `<div class="reappro-crossing-card${isLast ? ' reappro-crossing-final' : ''}">`;
        html += `<div class="reappro-crossing-row">`;

        // Parents
        const pA = isInverted ? step.parentB : step.parentA;
        const pB = isInverted ? step.parentA : step.parentB;
        html += this.renderCrossingParent(pA, '♂', step.couples);
        html += `<span class="material-symbols-outlined reappro-crossing-op">add</span>`;
        html += this.renderCrossingParent(pB, '♀', step.couples);
        html += `<span class="material-symbols-outlined reappro-crossing-op" style="color:var(--md-primary)">arrow_forward</span>`;

        // Baby result
        const babyGen = RACE_GEN[step.baby] ?? 0;
        html += `<div class="reappro-crossing-baby${isLast ? ' reappro-crossing-baby-final' : ''}">
          <div class="reappro-baby-avatar">
            ${getDDImage(step.baby)}
            <span class="reappro-baby-gen-badge" style="background:${genCol}">G${babyGen}</span>
          </div>
          <span class="reappro-baby-name">${esc(step.baby)}</span>
          <span class="reappro-baby-qty">&times;${step.couples}</span>
        </div>`;

        html += `</div>`; // .reappro-crossing-row

        // Controls row
        html += `<div class="reappro-crossing-controls">
          <label class="reappro-repro-label">
            <span style="font-size:11px;color:var(--md-on-surface-variant)">Reproducteurs</span>
            <input type="number" class="reappro-repro-input" data-race="${esc(invKey)}" min="0" value="${reproCount}">
          </label>
          <button class="reappro-invert-btn" data-race="${esc(invKey)}" title="Inverser ♂/♀">
            <span class="material-symbols-outlined" style="font-size:16px">swap_horiz</span>
          </button>
          <span class="reappro-couples-badge">${step.couples} couple${step.couples > 1 ? 's' : ''}</span>
        </div>`;

        html += `</div>`; // .reappro-crossing-card
      }
      html += `</div>`; // .reappro-crossings-grid
      html += `</div>`; // .reappro-step-panel

      stepNum++;

      // Arrow between gen panels (not after last)
      if (!isFinalGen) {
        html += `<div class="reappro-arrow-sep">
          <span class="material-symbols-outlined reappro-arrow-icon">expand_more</span>
        </div>`;
      }
    }

    // Sticky target bar at bottom with save button
    html += `<div class="reappro-target-bar-sticky">
      <div class="reappro-target-info">
        <div class="reappro-target-icon">
          <span class="material-symbols-outlined mso-fill">auto_awesome</span>
        </div>
        <div>
          <div class="reappro-target-label">Cible de réapprovisionnement</div>
          <div class="reappro-target-name">${esc(target)} (Génération ${targetGen})</div>
        </div>
      </div>
      <button class="reappro-save-btn" id="appro-save">
        <span class="material-symbols-outlined" style="font-size:18px">save</span>
        Sauvegarder ce plan
      </button>
    </div>`;

    this.el.innerHTML = html;
    this.bindResultsEvents();
  }

  private renderMaterialCard(race: string, m: number, f: number): string {
    const gen = RACE_GEN[race] ?? 1;
    const genCol = GEN_COLORS[gen] ?? '#888';
    const parts: string[] = [];
    if (m > 0) parts.push(`<span class="reappro-mat-gender" style="color:#50a0ff">♂ ${m}</span>`);
    if (f > 0) parts.push(`<span class="reappro-mat-gender" style="color:#ff64a0">♀ ${f}</span>`);

    return `<div class="reappro-material-card">
      <div class="reappro-mat-avatar">
        ${getDDImage(race)}
      </div>
      <span class="reappro-mat-name">${esc(race)}</span>
      <div class="reappro-mat-qty">${parts.join(' ')}</div>
    </div>`;
  }

  private renderCrossingParent(race: string, gender: string, qty: number): string {
    const genderColor = gender === '♂' ? '#50a0ff' : '#ff64a0';
    return `<div class="reappro-crossing-parent">
      <div class="reappro-crossing-parent-avatar">
        ${getDDImage(race)}
      </div>
      <span class="reappro-crossing-parent-name">${esc(race)}</span>
      <span class="reappro-crossing-parent-gender" style="color:${genderColor}">${gender} ${qty}</span>
    </div>`;
  }

  private bindResultsEvents(): void {
    if (!this.el) return;

    // Back buttons
    this.el.querySelectorAll('#appro-back').forEach(btn => {
      btn.addEventListener('click', () => {
        this.approState.target = '';
        this.dirty = true; this.update();
      });
    });

    const qtyInput = this.el.querySelector('#appro-qty') as HTMLInputElement | null;
    if (qtyInput) {
      let prevQty = qtyInput.value;
      qtyInput.addEventListener('focus', () => { prevQty = qtyInput.value; qtyInput.value = ''; });
      qtyInput.addEventListener('blur', () => {
        if (qtyInput.value === '') qtyInput.value = prevQty;
        this.approState.qty = Math.max(1, parseInt(qtyInput.value) || 1);
        qtyInput.value = String(this.approState.qty);
        this.dirty = true; this.update();
      });
    }

    this.el.querySelectorAll('.reappro-repro-input').forEach(inp => {
      const inpEl = inp as HTMLInputElement;
      let prev = inpEl.value;
      inpEl.addEventListener('focus', () => { prev = inpEl.value; inpEl.value = ''; });
      inpEl.addEventListener('blur', () => {
        if (inpEl.value === '') inpEl.value = prev;
        const race = inpEl.dataset.race!;
        this.approState.repro[race] = Math.max(0, parseInt(inpEl.value) || 0);
        this.dirty = true; this.update();
      });
    });

    this.el.querySelectorAll('.reappro-invert-btn').forEach(btn => {
      btn.addEventListener('click', () => {
        const race = (btn as HTMLElement).dataset.race!;
        this.approState.inverted[race] = !this.approState.inverted[race];
        this.dirty = true; this.update();
      });
    });

    const saveBtn = this.el.querySelector('#appro-save');
    if (saveBtn) {
      saveBtn.addEventListener('click', () => {
        const { target, qty, repro } = this.approState;
        const { materials, steps } = this.calcAppro(target, qty);
        this.commandBus.execute({
          type: 'save-workflow',
          target,
          qty,
          materials,
          steps,
          repro: { ...repro },
        });
        (saveBtn as HTMLButtonElement).innerHTML = '<span class="material-symbols-outlined" style="font-size:16px;vertical-align:middle;margin-right:6px">check</span> Sauvegardé !';
        setTimeout(() => {
          (saveBtn as HTMLButtonElement).innerHTML = '<span class="material-symbols-outlined" style="font-size:16px;vertical-align:middle;margin-right:6px">save</span> Sauvegarder ce plan';
        }, 2000);
      });
    }
  }

  /* ── Breeding plan calculation ── */
  private calcAppro(target: string, qty: number): { materials: { race: string; m: number; f: number }[]; steps: ApproStep[] } {
    const targetGen = RACE_GEN[target] ?? 0;
    if (targetGen < 2 || !BREEDING_RECIPES[target]) {
      return { materials: [], steps: [] };
    }

    const needs: Record<string, ApproNeeds> = {};
    needs[target] = { total: qty, m: 0, f: 0 };

    const steps: ApproStep[] = [];

    for (let g = targetGen; g >= 2; g--) {
      const racesAtGen: string[] = [];
      for (const [rg, rs] of Object.entries(RACES_DATA)) {
        if (parseInt(rg) === g) {
          for (const r of rs) {
            if (needs[r.name]) racesAtGen.push(r.name);
          }
        }
      }

      for (const race of racesAtGen) {
        const recipe = BREEDING_RECIPES[race];
        if (!recipe) continue;
        const need = needs[race];
        if (!need || need.total <= 0) continue;

        const reproCount = this.approState.repro[race] ?? 0;
        const couples = Math.max(1, need.total - reproCount);
        const isInverted = this.approState.inverted[race] ?? false;

        const [recipeA, recipeB] = recipe;
        const parentA = isInverted ? recipeB : recipeA;
        const parentB = isInverted ? recipeA : recipeB;

        if (!needs[parentA]) needs[parentA] = { total: 0, m: 0, f: 0 };
        needs[parentA].total += couples;
        needs[parentA].m += couples;

        if (!needs[parentB]) needs[parentB] = { total: 0, m: 0, f: 0 };
        needs[parentB].total += couples;
        needs[parentB].f += couples;

        steps.push({ baby: race, parentA, parentB, couples, gen: g });
      }
    }

    const materials: { race: string; m: number; f: number }[] = [];
    for (const [race, need] of Object.entries(needs)) {
      if (!BREEDING_RECIPES[race] && need.total > 0) {
        materials.push({ race, m: need.m, f: need.f });
      }
    }

    steps.reverse();
    return { materials, steps };
  }
}
