import { describe, expect, it } from 'vitest';
import type { CommissionDef, EntityState, RecipeDef } from '../contracts';
import { commissionChips, commissionMet, intakeChips, outputChips } from './chipspec';

function ent(over: Partial<EntityState> = {}): EntityState {
  return {
    id: 1, def: 'demuxer', pos: { x: 0, y: 0 }, dir: 0, recipe: 'demux-ore',
    progress: 0, inputBuf: {}, outputBuf: {}, jammed: null, heat: 0, ...over,
  };
}

const DEMUX: RecipeDef = {
  id: 'demux-ore', machine: 'demuxer',
  inputs: { 'mdat-ore': 2 },
  outputs: { 'luma-bars': 1, 'chroma-slurry': 1 },
  ticks: 45, bandwidth: 3,
};

describe('intakeChips', () => {
  it('reads have/need and marks a short intake', () => {
    expect(intakeChips(DEMUX, ent({ inputBuf: { 'mdat-ore': 1 } }))).toEqual([
      { item: 'mdat-ore', count: '1/2', state: 'short' },
    ]);
  });

  it('marks intake met once the buffer covers the recipe', () => {
    expect(intakeChips(DEMUX, ent({ inputBuf: { 'mdat-ore': 2 } }))[0].state).toBe('met');
  });

  it('treats a missing buffer entry as zero rather than NaN', () => {
    expect(intakeChips(DEMUX, ent())[0].count).toBe('0/2');
  });
});

describe('outputChips', () => {
  it('lists recipe outputs even when the buffer is empty', () => {
    expect(outputChips(DEMUX, ent()).map((c) => c.item))
      .toEqual(['luma-bars', 'chroma-slurry']);
  });

  it('also surfaces buffer contents the recipe never mentions — that is the jam', () => {
    const chips = outputChips(DEMUX, ent({ outputBuf: { 'luma-bars': 3, melt: 1 } }));
    expect(chips).toContainEqual({ item: 'melt', count: '1' });
    expect(chips).toContainEqual({ item: 'luma-bars', count: '3' });
  });

  it('does not duplicate an item present in both recipe and buffer', () => {
    const ids = outputChips(DEMUX, ent({ outputBuf: { 'luma-bars': 2 } })).map((c) => c.item);
    expect(ids.filter((i) => i === 'luma-bars')).toHaveLength(1);
  });
});

describe('commissionChips', () => {
  const c: CommissionDef = {
    id: 'first-taste', flavor: '', wants: { melt: 40 }, rewardBandwidth: 50,
  };

  it('shows delivered against wanted', () => {
    expect(commissionChips(c, { melt: 3 })).toEqual([
      { item: 'melt', count: '3/40', state: 'plain' },
    ]);
  });

  it('never reports more delivered than wanted', () => {
    expect(commissionChips(c, { melt: 99 })[0].count).toBe('40/40');
  });

  it('marks met at exactly the wanted count', () => {
    expect(commissionChips(c, { melt: 40 })[0].state).toBe('met');
  });
});

describe('commissionMet', () => {
  const two: CommissionDef = {
    id: 'x', flavor: '', wants: { melt: 2, 'hf-dust': 1 }, rewardBandwidth: 0,
  };

  it('is false while any want is short', () => {
    expect(commissionMet(two, { melt: 2 })).toBe(false);
  });

  it('is true only once every want is covered', () => {
    expect(commissionMet(two, { melt: 2, 'hf-dust': 1 })).toBe(true);
  });
});
