// @vitest-environment happy-dom
import { describe, expect, it } from 'vitest';
import type { GameData, SimEvent, SimSnapshot, UIBus } from '../contracts';
import { createInspector } from './inspector';
import { eventToast, wildlifeToast } from './voice';

const named = (id: number) => `UNIT ${id}`;

describe('wildlife voice', () => {
  it('warns that a dust pile is a nursery, not just a mess', () => {
    expect(wildlifeToast('dust-pile', 'spawned')).toContain('HATCH');
  });

  it('says nothing when a pile is cleared — housekeeping is not news', () => {
    expect(wildlifeToast('dust-pile', 'cleared')).toBeNull();
  });

  it('announces a swarm and its dispersal differently', () => {
    const on = wildlifeToast('mosquito-swarm', 'spawned');
    const off = wildlifeToast('mosquito-swarm', 'cleared');
    expect(on).toContain('MOSQUITO SWARM');
    expect(off).not.toBe(on);
    expect(off).toContain('DISPERSED');
  });

  it('routes wildlife events through the standard toast path', () => {
    const ev: SimEvent = {
      kind: 'wildlife', on: 'spawned', wildlife: 'mosquito-swarm',
      id: 1, pos: { x: 0, y: 0 }, tick: 5,
    };
    expect(eventToast(ev, named)).toContain('MOSQUITO SWARM');
  });

  it('announces the fossil in the house voice', () => {
    const ev: SimEvent = { kind: 'relicFound', relic: 'optical-fossil', id: 1, tick: 9 };
    expect(eventToast(ev, named)).toBe('AUDIO BUS RESTORED. THE BITSTREAM HAS A VOICE.');
  });
});

// ------------------------------------------------------------------ inspector exposure

const DATA: GameData = {
  items: [],
  machines: [{
    id: 'quantizer', name: 'QUANTIZER', codex: '', kind: 'crafter',
    footprint: { x: 2, y: 2 }, recipes: [], powerDraw: 0, asset: 'q',
  }],
  recipes: [],
  tech: [],
  commissions: [],
};

const bus: UIBus = {
  dispatch: () => {},
  selectedBuild: () => null,
  pickTile: () => null,
};

function snapWith(wildlife?: SimSnapshot['wildlife']): SimSnapshot {
  return {
    tick: 1, paused: false,
    entities: [{
      id: 7, def: 'quantizer', pos: { x: 0, y: 0 }, dir: 0, recipe: null, progress: 0,
      inputBuf: {}, outputBuf: {}, jammed: null, heat: 0,
    }],
    beltItems: [],
    bandwidth: { gen: 10, draw: 1, stored: 0, brownout: false },
    shippedTotal: {}, activeCommission: null, commissionProgress: {},
    wildlife,
  };
}

describe('MOSQUITO EXPOSURE in the inspector', () => {
  function open() {
    document.body.innerHTML = '';
    const ins = createInspector(DATA, bus);
    document.body.append(ins.el);
    ins.open(7);
    return ins;
  }
  const exposure = () => document.querySelector('.fk-ins-exposure') as HTMLElement;

  it('shows nothing when no swarm is on this machine', () => {
    const ins = open();
    ins.update(snapWith([
      { id: 1, kind: 'mosquito-swarm', pos: { x: 9, y: 9 }, size: 0.8, target: 99 },
    ]));
    expect(exposure().classList.contains('is-on')).toBe(false);
  });

  it('surfaces the exposure and its severity when a swarm targets this machine', () => {
    const ins = open();
    ins.update(snapWith([
      { id: 1, kind: 'mosquito-swarm', pos: { x: 0, y: 0 }, size: 0.42, target: 7 },
    ]));
    expect(exposure().classList.contains('is-on')).toBe(true);
    expect(document.querySelector('.fk-ins-exposure-pct')!.textContent)
      .toBe('PRECISION DOWN 42%. SWARM ON THE EDGES.');
  });

  it('ignores dust piles — a pile is not biting anything yet', () => {
    const ins = open();
    ins.update(snapWith([
      { id: 2, kind: 'dust-pile', pos: { x: 0, y: 0 }, size: 1, target: 7 },
    ]));
    expect(exposure().classList.contains('is-on')).toBe(false);
  });

  it('clears the line when the swarm is dispersed', () => {
    const ins = open();
    ins.update(snapWith([
      { id: 1, kind: 'mosquito-swarm', pos: { x: 0, y: 0 }, size: 0.5, target: 7 },
    ]));
    expect(exposure().classList.contains('is-on')).toBe(true);
    ins.update(snapWith([]));
    expect(exposure().classList.contains('is-on')).toBe(false);
  });

  it('survives a sim that reports no wildlife at all', () => {
    const ins = open();
    ins.update(snapWith(undefined));
    expect(exposure().classList.contains('is-on')).toBe(false);
  });
});
