import { clamp } from './util';

/** Keyboard (WASD/arrows + J/Space) and touch (virtual stick + attack button). */
export class Input {
  /** World-space move vector, -1..1 (screen up = -Z). */
  mx = 0;
  mz = 0;
  attackHeld = false;
  /** Fired on any input — used to start the game from the title screen. */
  onAny: (() => void) | null = null;

  private keys = new Set<string>();
  private presses = 0;
  private keyAttackHeld = false;
  private touchAttackHeld = false;

  private stickId = -1;
  private stickOx = 0;
  private stickOy = 0;
  private sx = 0;
  private sy = 0;

  private stickBase: HTMLElement;
  private stickNub: HTMLElement;
  private attackBtn: HTMLElement;

  constructor() {
    window.addEventListener('keydown', (e) => {
      if (e.code === 'Space' || e.code.startsWith('Arrow')) e.preventDefault();
      if (e.repeat) return;
      this.keys.add(e.code);
      if (this.isAttackCode(e.code)) {
        this.presses++;
        this.keyAttackHeld = true;
      }
      this.onAny?.();
    });
    window.addEventListener('keyup', (e) => {
      this.keys.delete(e.code);
      if (this.isAttackCode(e.code)) this.keyAttackHeld = false;
    });
    window.addEventListener('blur', () => {
      this.keys.clear();
      this.keyAttackHeld = false;
    });

    // --- touch UI ---
    const zone = document.createElement('div');
    zone.id = 'stick-zone';
    this.stickBase = document.createElement('div');
    this.stickBase.id = 'stick-base';
    this.stickNub = document.createElement('div');
    this.stickNub.id = 'stick-nub';
    this.attackBtn = document.createElement('div');
    this.attackBtn.id = 'attack-btn';
    this.attackBtn.innerHTML =
      '<svg viewBox="0 0 24 24" fill="none" stroke="#fff8e1" stroke-width="2" stroke-linecap="round">' +
      '<path d="M5 19 L14 10 M14 10 L19 5 M19 5 l0.4 -0.4 M13 9 l5 0 M14 10 l0 -5"/>' +
      '<path d="M5 19 l-1.6 1.6 M7.5 16.5 l-3.4 -3.4"/></svg>';
    document.body.append(zone, this.stickBase, this.stickNub, this.attackBtn);

    zone.addEventListener('pointerdown', (e) => {
      if (this.stickId !== -1) return;
      document.body.classList.add('touched');
      this.stickId = e.pointerId;
      this.stickOx = e.clientX;
      this.stickOy = e.clientY;
      zone.setPointerCapture(e.pointerId);
      this.showStick(true);
      this.moveStick(e.clientX, e.clientY);
      this.onAny?.();
    });
    zone.addEventListener('pointermove', (e) => {
      if (e.pointerId === this.stickId) this.moveStick(e.clientX, e.clientY);
    });
    const endStick = (e: PointerEvent): void => {
      if (e.pointerId !== this.stickId) return;
      this.stickId = -1;
      this.sx = 0;
      this.sy = 0;
      this.showStick(false);
    };
    zone.addEventListener('pointerup', endStick);
    zone.addEventListener('pointercancel', endStick);

    this.attackBtn.addEventListener('pointerdown', (e) => {
      e.preventDefault();
      document.body.classList.add('touched');
      this.attackBtn.setPointerCapture(e.pointerId);
      this.attackBtn.classList.add('pressed');
      this.presses++;
      this.touchAttackHeld = true;
      this.onAny?.();
    });
    const endAttack = (): void => {
      this.attackBtn.classList.remove('pressed');
      this.touchAttackHeld = false;
    };
    this.attackBtn.addEventListener('pointerup', endAttack);
    this.attackBtn.addEventListener('pointercancel', endAttack);
  }

  private isAttackCode(code: string): boolean {
    return code === 'Space' || code === 'KeyJ' || code === 'Enter';
  }

  private showStick(on: boolean): void {
    const d = on ? 'block' : 'none';
    this.stickBase.style.display = d;
    this.stickNub.style.display = d;
    if (on) {
      this.stickBase.style.left = `${this.stickOx}px`;
      this.stickBase.style.top = `${this.stickOy}px`;
    }
  }

  private moveStick(x: number, y: number): void {
    const dx = x - this.stickOx;
    const dy = y - this.stickOy;
    const len = Math.hypot(dx, dy);
    const max = 46;
    const k = len > max ? max / len : 1;
    this.stickNub.style.left = `${this.stickOx + dx * k}px`;
    this.stickNub.style.top = `${this.stickOy + dy * k}px`;
    const dead = 7;
    if (len < dead) {
      this.sx = 0;
      this.sy = 0;
    } else {
      this.sx = clamp(dx / max, -1, 1);
      this.sy = clamp(dy / max, -1, 1);
    }
  }

  /** Recompute the move vector; call once per frame. */
  poll(): void {
    const k = this.keys;
    let x = (k.has('KeyD') || k.has('ArrowRight') ? 1 : 0) - (k.has('KeyA') || k.has('ArrowLeft') ? 1 : 0);
    let z = (k.has('KeyS') || k.has('ArrowDown') ? 1 : 0) - (k.has('KeyW') || k.has('ArrowUp') ? 1 : 0);
    if (x === 0 && z === 0) {
      x = this.sx;
      z = this.sy;
    }
    const len = Math.hypot(x, z);
    if (len > 1) {
      x /= len;
      z /= len;
    }
    this.mx = x;
    this.mz = z;
    this.attackHeld = this.keyAttackHeld || this.touchAttackHeld;
  }

  /** Returns true once per queued attack press. */
  consumePress(): boolean {
    if (this.presses > 0) {
      this.presses--;
      return true;
    }
    return false;
  }
}
