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

interface AccoupState {
  parent1: string | null;
  parent2: string | null;
  filterGen: number | null;
  search: string;
  couples: string;
  babies: string;
  selectingSlot: 1 | 2;
}

export class AccouplementView {
  private el: HTMLElement | null = null;
  private accoupState: AccoupState = {
    parent1: null, parent2: null,
    filterGen: null, search: '',
    couples: '', babies: '',
    selectingSlot: 1,
  };
  private dirty = true;
  private dragScrollRAF: number | null = null;
  private dragMouseY = 0;

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

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

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

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

  /* ── All races list ── */
  private getAllRaces(): { name: string; gen: number }[] {
    const all: { name: string; gen: number }[] = [];
    for (const base of ['Rousse', 'Amande', 'Dorée'])
      all.push({ name: base, gen: 1 });
    for (const [g, rs] of Object.entries(RACES_DATA))
      for (const r of rs)
        all.push({ name: r.name, gen: parseInt(g) });
    return all;
  }

  /* ── Filtered races (gen filter, search, partner compat) ── */
  private getFilteredRaces(): { name: string; gen: number }[] {
    const { filterGen, search, parent1, selectingSlot } = this.accoupState;
    let races = this.getAllRaces();

    // If selecting parent 2, only show compatible partners
    if (selectingSlot === 2 && parent1) {
      const partners = COMPATIBLE_PARTNERS[parent1] ?? [];
      const partnerNames = new Set(partners.map(p => p.partner));
      races = races.filter(r => partnerNames.has(r.name));
    }

    const q = search.trim().toLowerCase();
    return races.filter(r =>
      (filterGen ? r.gen === filterGen : true) &&
      (q ? r.name.toLowerCase().includes(q) : true)
    );
  }

  /* ── Baby name from parents ── */
  private getBabyName(): string | null {
    const { parent1, parent2 } = this.accoupState;
    if (!parent1 || !parent2) return null;
    return BREEDING_BY_PARENTS[parent1 + '|' + parent2] ?? null;
  }

  /* ── Auto-scroll during drag ── */
  private getScrollContainer(): HTMLElement | null {
    return this.el?.closest('.main-content') as HTMLElement | null;
  }

  private startDragAutoScroll(): void {
    const container = this.getScrollContainer();
    if (!container) return;

    const onDragOver = (e: DragEvent) => { this.dragMouseY = e.clientY; };
    container.addEventListener('dragover', onDragOver);

    const EDGE = 80;    // zone de déclenchement (px depuis le bord)
    const SPEED = 28;   // vitesse de scroll (px par frame)

    const tick = () => {
      const rect = container.getBoundingClientRect();
      const y = this.dragMouseY;

      if (y > 0 && y < rect.top + EDGE) {
        // Proche du haut → scroll vers le haut
        container.scrollTop -= SPEED;
      } else if (y > rect.bottom - EDGE && y < rect.bottom) {
        // Proche du bas → scroll vers le bas
        container.scrollTop += SPEED;
      }

      this.dragScrollRAF = requestAnimationFrame(tick);
    };

    this.dragScrollRAF = requestAnimationFrame(tick);
    // Store cleanup ref
    (this as any)._dragOverCleanup = () => container.removeEventListener('dragover', onDragOver);
  }

  private stopDragAutoScroll(): void {
    if (this.dragScrollRAF !== null) {
      cancelAnimationFrame(this.dragScrollRAF);
      this.dragScrollRAF = null;
    }
    if ((this as any)._dragOverCleanup) {
      (this as any)._dragOverCleanup();
      delete (this as any)._dragOverCleanup;
    }
  }

  /* ── Assign a race to a parent slot ── */
  private assignRaceToSlot(race: string, forceSlot?: 1 | 2): void {
    const slot = forceSlot ?? (this.accoupState.parent1 ? 2 : 1);

    if (slot === 1) {
      this.accoupState.parent1 = race;
      this.accoupState.parent2 = null; // Reset P2 since partners depend on P1
      this.accoupState.selectingSlot = 2;
      this.accoupState.filterGen = null;
      this.accoupState.search = '';
    } else {
      // Check partner compatibility if P1 is set
      if (this.accoupState.parent1) {
        const partners = COMPATIBLE_PARTNERS[this.accoupState.parent1] ?? [];
        const isCompat = partners.some(p => p.partner === race);
        if (!isCompat) return; // Ignore incompatible drop
      }
      this.accoupState.parent2 = race;
    }

    this.dirty = true;
    this.update();
  }

  /* ── Single page render ── */
  private renderSinglePage(): void {
    if (!this.el) return;
    const { parent1, parent2, filterGen, search, couples, babies } = this.accoupState;
    const baby = this.getBabyName();
    const hasBoth = !!(parent1 && parent2 && baby);

    let html = '';

    /* ── Parent panels row ── */
    html += `<div class="accoup-parents">`;

    // Parent 1 section
    html += `<section class="accoup-parent-section">`;
    html += `<div class="accoup-parent-header">
      <span class="accoup-parent-title">Parent 1</span>
    </div>`;
    if (parent1) {
      const gen1 = RACE_GEN[parent1] ?? 1;
      html += `<div class="accoup-selected-parent accoup-drop-zone" data-drop-slot="1">
        ${getDDImage(parent1)}
        <span class="accoup-selected-parent-name">${esc(parent1)}</span>
        <span class="accoup-selected-parent-badge" style="background:${GEN_COLORS[gen1] ?? '#888'}">Gen ${gen1}</span>
        <button class="accoup-selected-parent-clear" data-clear="1" title="Retirer">
          <span class="material-symbols-outlined" style="font-size:14px">close</span>
        </button>
      </div>`;
    } else {
      html += `<div class="accoup-placeholder accoup-drop-zone" data-select-slot="1" data-drop-slot="1">
        <div class="accoup-placeholder-inner">
          <div class="accoup-placeholder-icon">
            <span class="material-symbols-outlined" style="color:var(--md-primary)">add</span>
          </div>
          <p class="accoup-placeholder-text">Cliquer ou glisser un mâle ici</p>
        </div>
      </div>`;
    }
    html += `</section>`;

    // Center column
    html += `<div class="accoup-center">`;
    html += `<div class="accoup-heart">
      <span class="material-symbols-outlined mso-fill">favorite</span>
    </div>`;
    html += `<div class="accoup-center-inputs">
      <div class="accoup-center-field">
        <label class="accoup-center-label">Nombre de couples</label>
        <input class="accoup-center-input" id="accoup-couples" type="number" min="1" value="${esc(couples)}" placeholder="1">
      </div>
      <div class="accoup-center-field">
        <label class="accoup-center-label">Bébés obtenus</label>
        <input class="accoup-center-input secondary" id="accoup-babies" type="number" min="0" value="${esc(babies)}" placeholder="0">
      </div>
    </div>`;

    // Baby preview (if both parents selected)
    if (hasBoth && baby) {
      const babyGen = RACE_GEN[baby] ?? 0;
      html += `<div style="text-align:center;margin-top:4px">
        <div style="font-size:10px;color:var(--md-on-surface-variant);text-transform:uppercase;letter-spacing:0.08em;margin-bottom:6px">Résultat</div>
        ${getDDImage(baby)}
        <div style="font-size:12px;font-weight:700;color:var(--md-on-surface);margin-top:4px">${esc(baby)}</div>
        <span class="accoup-selected-parent-badge" style="background:${GEN_COLORS[babyGen] ?? '#888'};font-size:8px">Gen ${babyGen}</span>
      </div>`;
    }

    html += `<button class="accoup-register-btn" id="accoup-register" ${hasBoth ? '' : 'disabled'}>ENREGISTRER</button>`;
    html += `</div>`;

    // Parent 2 section
    html += `<section class="accoup-parent-section">`;
    html += `<div class="accoup-parent-header">
      <span class="accoup-parent-title">Parent 2</span>
    </div>`;
    if (parent2) {
      const gen2 = RACE_GEN[parent2] ?? 1;
      html += `<div class="accoup-selected-parent accoup-drop-zone" data-drop-slot="2">
        ${getDDImage(parent2)}
        <span class="accoup-selected-parent-name">${esc(parent2)}</span>
        <span class="accoup-selected-parent-badge" style="background:${GEN_COLORS[gen2] ?? '#888'}">Gen ${gen2}</span>
        <button class="accoup-selected-parent-clear" data-clear="2" title="Retirer">
          <span class="material-symbols-outlined" style="font-size:14px">close</span>
        </button>
      </div>`;
    } else {
      html += `<div class="accoup-placeholder accoup-drop-zone" data-select-slot="2" data-drop-slot="2">
        <div class="accoup-placeholder-inner">
          <div class="accoup-placeholder-icon">
            <span class="material-symbols-outlined" style="color:var(--md-secondary)">add</span>
          </div>
          <p class="accoup-placeholder-text">Cliquer ou glisser une femelle ici</p>
        </div>
      </div>`;
    }
    html += `</section>`;
    html += `</div>`; // .accoup-parents

    /* ── Grid panel ── */
    html += `<div class="accoup-grid-panel">`;

    // Gen chips
    html += `<div class="accoup-gen-chips">`;
    html += `<span class="accoup-gen-chips-label">Générations</span>`;
    html += `<button class="accoup-gen-chip${filterGen === null ? ' active' : ''}" data-gen="all">Toutes</button>`;
    for (let g = 1; g <= 10; g++) {
      html += `<button class="accoup-gen-chip${filterGen === g ? ' active' : ''}" data-gen="${g}">Gen ${g}</button>`;
    }
    html += `</div>`;

    // Search
    html += `<div class="accoup-search-wrap">
      <input class="accoup-search" id="accoup-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
    const filtered = this.getFilteredRaces();
    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)}" draggable="true">
          <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>`;
    }

    html += `</div>`; // .accoup-grid-panel

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

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

  /* ── Event binding ── */
  private bindEvents(): void {
    if (!this.el) return;

    // Gen chips
    this.el.querySelectorAll('.accoup-gen-chip').forEach(btn => {
      btn.addEventListener('click', () => {
        const val = (btn as HTMLElement).dataset.gen;
        this.accoupState.filterGen = val === 'all' ? null : parseInt(val!);
        this.dirty = true; this.update();
      });
    });

    // Search
    const searchInput = this.el.querySelector<HTMLInputElement>('#accoup-search-input');
    if (searchInput) {
      searchInput.addEventListener('input', () => {
        this.accoupState.search = searchInput.value;
        this.dirty = true; this.update();
      });
    }
    const clearBtn = this.el.querySelector('.accoup-search-clear');
    if (clearBtn) {
      clearBtn.addEventListener('click', () => {
        this.accoupState.search = '';
        this.dirty = true; this.update();
      });
    }

    // Placeholder clicks (select slot)
    this.el.querySelectorAll<HTMLElement>('[data-select-slot]').forEach(ph => {
      ph.addEventListener('click', () => {
        this.accoupState.selectingSlot = parseInt(ph.dataset.selectSlot!) as 1 | 2;
      });
    });

    // Clear parent buttons
    this.el.querySelectorAll<HTMLElement>('.accoup-selected-parent-clear').forEach(btn => {
      btn.addEventListener('click', (e) => {
        e.stopPropagation();
        const slot = parseInt(btn.dataset.clear!);
        if (slot === 1) {
          this.accoupState.parent1 = null;
          this.accoupState.parent2 = null;
          this.accoupState.selectingSlot = 1;
        } else {
          this.accoupState.parent2 = null;
          this.accoupState.selectingSlot = 2;
        }
        this.dirty = true; this.update();
      });
    });

    // Race card clicks
    this.el.querySelectorAll<HTMLElement>('.accoup-race-card').forEach(card => {
      card.addEventListener('click', () => {
        this.assignRaceToSlot(card.dataset.race!);
      });

      // Drag start
      card.addEventListener('dragstart', (e) => {
        e.dataTransfer!.setData('text/plain', card.dataset.race!);
        e.dataTransfer!.effectAllowed = 'copy';
        card.classList.add('dragging');
        this.startDragAutoScroll();
      });
      card.addEventListener('dragend', () => {
        card.classList.remove('dragging');
        this.stopDragAutoScroll();
      });
    });

    // Drop zones
    this.el.querySelectorAll<HTMLElement>('.accoup-drop-zone').forEach(zone => {
      zone.addEventListener('dragover', (e) => {
        e.preventDefault();
        e.dataTransfer!.dropEffect = 'copy';
      });
      zone.addEventListener('dragenter', (e) => {
        e.preventDefault();
        zone.classList.add('drag-over');
      });
      zone.addEventListener('dragleave', () => {
        zone.classList.remove('drag-over');
      });
      zone.addEventListener('drop', (e) => {
        e.preventDefault();
        zone.classList.remove('drag-over');
        const race = e.dataTransfer!.getData('text/plain');
        if (!race) return;
        const slot = parseInt(zone.dataset.dropSlot!) as 1 | 2;
        this.assignRaceToSlot(race, slot);
      });
    });

    // Couples / babies inputs
    const couplesInput = this.el.querySelector<HTMLInputElement>('#accoup-couples');
    const babiesInput = this.el.querySelector<HTMLInputElement>('#accoup-babies');
    if (couplesInput) {
      let prev = couplesInput.value;
      couplesInput.addEventListener('focus', () => { prev = couplesInput.value; couplesInput.value = ''; });
      couplesInput.addEventListener('blur', () => {
        if (couplesInput.value === '') couplesInput.value = prev;
        this.accoupState.couples = couplesInput.value;
      });
    }
    if (babiesInput) {
      let prev = babiesInput.value;
      babiesInput.addEventListener('focus', () => { prev = babiesInput.value; babiesInput.value = ''; });
      babiesInput.addEventListener('blur', () => {
        if (babiesInput.value === '') babiesInput.value = prev;
        this.accoupState.babies = babiesInput.value;
      });
    }

    // Register button
    const registerBtn = this.el.querySelector('#accoup-register');
    if (registerBtn) {
      registerBtn.addEventListener('click', () => {
        const { parent1, parent2 } = this.accoupState;
        if (!parent1 || !parent2) return;
        const baby = BREEDING_BY_PARENTS[parent1 + '|' + parent2] ?? '';
        if (!baby) return;
        const c = parseInt(this.accoupState.couples) || 0;
        const b = parseInt(this.accoupState.babies) || 0;
        if (c <= 0) return;

        this.commandBus.execute({
          type: 'register-accouplement',
          parent1,
          parent2,
          baby,
          gen: RACE_GEN[baby] ?? 0,
          couples: c,
          babiesObtained: b,
        });

        Toast.show('success', 'Accouplement enregistré.');

        // Reset
        this.accoupState = {
          parent1: null, parent2: null,
          filterGen: null, search: '',
          couples: '', babies: '',
          selectingSlot: 1,
        };
        this.dirty = true; this.update();
      });
    }
  }
}
