import type { EventBus } from './EventBus';
import type { HeatStrike, NightState } from '../data/types';

export function freshNightState(venueId: string, nightIndex: number, licensed: number): NightState {
  return {
    venueId,
    nightIndex,
    vibe: 50,
    aggro: 0,
    heatStrikes: [],
    hype: 1,
    capacity: { inside: 0, licensed, clickerShown: 0 },
    clockMin: 0,
    location: 'door',
    incidents: [],
  };
}

const clamp = (v: number, lo: number, hi: number) => Math.max(lo, Math.min(hi, v));

/**
 * Vibe per clock-minute the room loses on its own (emit from the night's
 * minute tick). Nobody stays impressed: above the baseline the room cools, so
 * a lit room is a job you keep doing, not a ratchet you finish. Never positive
 * — a dead floor does not heal itself (tuning pass 2026-07-19; before this,
 * any net-positive night pinned vibe at 100 by 10 PM and the meter went dead).
 */
export function vibeCoolingPerMin(vibe: number): number {
  if (vibe > 70) return -0.8;
  if (vibe > 50) return -0.4;
  return 0;
}

// Single writer for the night's numbers. Scenes emit meters:delta / heat:strike;
// nothing else may mutate NightState meters directly.
export class Meters {
  private readonly offs: Array<() => void> = [];

  constructor(
    private readonly bus: EventBus,
    public readonly state: NightState,
  ) {
    this.offs.push(
      bus.on('meters:delta', (d) => this.applyDelta(d)),
      bus.on('heat:strike', (s) => this.applyStrike(s)),
      bus.on('clock:tick', ({ clockMin }) => {
        this.state.clockMin = clockMin;
      }),
      bus.on('night:phaseChange', ({ location }) => {
        this.state.location = location;
      }),
      bus.on('incident:log', (incident) => {
        this.state.incidents.push(incident);
      }),
      bus.on('door:clicker', ({ direction }) => {
        this.state.capacity.clickerShown = Math.max(
          0,
          this.state.capacity.clickerShown + (direction === 'in' ? 1 : -1),
        );
      }),
    );
  }

  private applyDelta(d: { vibe?: number; aggro?: number; hype?: number }): void {
    const s = this.state;
    if (d.vibe !== undefined) {
      // Hype multiplies the good news only — queue theatre never softens a hit.
      const scaled = d.vibe > 0 ? d.vibe * s.hype : d.vibe;
      s.vibe = clamp(s.vibe + scaled, 0, 100);
    }
    if (d.aggro !== undefined) s.aggro = clamp(s.aggro + d.aggro, 0, 100);
    if (d.hype !== undefined) s.hype = clamp(s.hype + d.hype, 1, 3);
    this.bus.emit('meters:changed', { vibe: s.vibe, aggro: s.aggro, hype: s.hype });
  }

  private applyStrike(strike: HeatStrike): void {
    this.state.heatStrikes.push(strike);
  }

  destroy(): void {
    for (const off of this.offs) off();
  }
}
