import { describe, it, expect } from 'vitest'
import { parse } from './parser'
import type { ParserContext } from './parser'
import { dispatch, initialStateFor } from './dispatcher'
import { world } from '../world'
import type { GameState } from './types'

function ctxFor(state: GameState): ParserContext {
  const room = world.rooms[state.location]
  const visibleNouns: { id: string; aliases: string[] }[] = []
  for (const itemId of room?.items ?? []) {
    const item = world.items[itemId]
    if (item) visibleNouns.push({ id: itemId, aliases: item.names })
  }
  for (const inst of state.inventory) {
    const item = world.items[inst.id]
    if (item) visibleNouns.push({ id: inst.id, aliases: item.names })
  }
  if (room?.encounter) {
    const encounter = world.encounters[room.encounter]
    visibleNouns.push({
      id: room.encounter,
      aliases: [room.encounter, room.encounter.replace(/-/g, ' '), ...(encounter?.aliases ?? [])],
    })
  }
  return {
    knownItems: Object.keys(world.items),
    knownEncounters: Object.keys(world.encounters),
    visibleNouns,
    inventoryItemIds: state.inventory.map((i) => i.id),
    lastNoun: state.lastNoun,
    awaitingDisambiguation: state.pendingDisambiguation,
  }
}

function play(commands: string[]): GameState {
  let state = initialStateFor(world)
  for (const cmd of commands) {
    const parsed = parse(cmd, ctxFor(state))
    state = dispatch(state, parsed, world).state
  }
  return state
}

describe('playthrough — sample world', () => {
  it('reaches the rat-gone flag via the canonical command sequence', () => {
    const state = play([
      'read letter',  // verb is recognized but encounter takes priority elsewhere; here it's a no-op
      'n',            // gate → foyer
      'n',            // foyer → hallway
      'take lamp',
      'e',            // hallway → cellar-stair (triggers rat encounter)
      'attack rat',
    ])
    expect(state.flags['ratGone']).toBe(true)
    expect(state.location).toBe('cellar-stair')
    expect(state.encounterState['rat']).toBeUndefined()
  })

  it('handles invalid moves gracefully', () => {
    const state = play([
      'go up',        // gate has no up exit
      'n',
      's',
      'flibbertigibbet',  // unknown verb
    ])
    expect(state.location).toBe('outside-gate')
  })

  it('plays through the main-floor slice encounters', () => {
    const state = play([
      'n',                 // gate → foyer
      'n',                 // foyer → hallway
      'n',                 // hallway → dining-room
      'close curtains',
      'take candlestick',
      'n',                 // dining-room → conservatory
      'take shears',
      'cut vines with shears',
      's',
      'w',                 // dining-room → hallway
      'w',                 // hallway → smoking-room
      'take lighter',
      'uncover cage',
      'e',
      'd',                 // hallway → music-room
      'play note',
      'take tiny key',
      'n',                 // music-room → servants-passage
      'wait',
      'e',                 // servants-passage → laundry
      'wait',
      'take damp sheet',
    ])

    expect(state.flags['window-guest.resolved']).toBe(true)
    expect(state.flags['ivy-figure.resolved']).toBe(true)
    expect(state.flags['covered-cage.resolved']).toBe(true)
    expect(state.flags['piano-echo.resolved']).toBe(true)
    expect(state.flags['breathing-wall.resolved']).toBe(true)
    expect(state.flags['linen-shape.resolved']).toBe(true)
    expect(state.inventory.map((i) => i.id)).toEqual(expect.arrayContaining([
      'candlestick',
      'pruning-shears',
      'silver-lighter',
      'music-box-key',
      'damp-sheet',
    ]))
  })
})
