// @vitest-environment happy-dom
import { beforeEach, describe, expect, it } from 'vitest';
import type { ItemDef } from '../contracts';
import { createChipRow } from './chips';

const ITEMS = new Map<string, ItemDef>([
  ['melt', { id: 'melt', name: 'MELT', codex: '', tier: 2, color: '#ff7a3f' }],
  ['mdat-ore', { id: 'mdat-ore', name: 'MDAT ORE', codex: '', tier: 0, color: '#1a1a22' }],
  ['hf-dust', { id: 'hf-dust', name: 'HF DUST', codex: '', tier: 1, color: '#e8e0ff' }],
]);

// Array.from, not spread: the root tsconfig's lib list has DOM but not DOM.Iterable,
// so a NodeList isn't iterable in types. Root config isn't this lane's to edit.
const texts = (row: HTMLElement) =>
  Array.from(row.querySelectorAll('.fk-chip')).map((c) => c.textContent);
const ids = (row: HTMLElement) =>
  Array.from(row.querySelectorAll('.fk-chip')).map((c) => c.getAttribute('title'));

describe('createChipRow', () => {
  let row: ReturnType<typeof createChipRow>;
  beforeEach(() => {
    row = createChipRow(ITEMS);
  });

  it('renders a chip per spec with the item display name and count', () => {
    row.update([{ item: 'melt', count: '3/40' }]);
    expect(texts(row.el)).toEqual(['MELT3/40']);
  });

  it('paints the swatch with the item colour from data', () => {
    row.update([{ item: 'melt', count: '1' }]);
    const sw = row.el.querySelector('.fk-chip-sw') as HTMLElement;
    expect(sw.style.background).toBe('#ff7a3f');
  });

  it('reuses the same node across updates instead of rebuilding the row', () => {
    row.update([{ item: 'melt', count: '1' }]);
    const first = row.el.querySelector('.fk-chip');
    row.update([{ item: 'melt', count: '2' }]);
    expect(row.el.querySelector('.fk-chip')).toBe(first); // same node, new count
    expect(texts(row.el)).toEqual(['MELT2']);
  });

  it('applies state classes and clears them again', () => {
    row.update([{ item: 'melt', count: '0/1', state: 'short' }]);
    expect(row.el.querySelector('.fk-chip')!.className).toBe('fk-chip is-short');
    row.update([{ item: 'melt', count: '1/1', state: 'met' }]);
    expect(row.el.querySelector('.fk-chip')!.className).toBe('fk-chip is-met');
    row.update([{ item: 'melt', count: '9' }]);
    expect(row.el.querySelector('.fk-chip')!.className).toBe('fk-chip');
  });

  it('removes chips that are no longer in the spec', () => {
    row.update([{ item: 'melt', count: '1' }, { item: 'hf-dust', count: '2' }]);
    expect(ids(row.el)).toEqual(['melt', 'hf-dust']);
    row.update([{ item: 'hf-dust', count: '3' }]);
    expect(ids(row.el)).toEqual(['hf-dust']);
  });

  it('keeps DOM order matching spec order', () => {
    row.update([{ item: 'melt', count: '1' }, { item: 'hf-dust', count: '1' }]);
    row.update([{ item: 'hf-dust', count: '1' }, { item: 'melt', count: '1' }]);
    expect(ids(row.el)).toEqual(['hf-dust', 'melt']);
  });

  it('renders an unknown item as a drab chip rather than crashing', () => {
    // A dangling id from DATA must never take the panel down.
    row.update([{ item: 'not-an-item', count: '1' }]);
    expect(texts(row.el)).toEqual(['???1']);
  });

  it('renders nothing for an empty spec', () => {
    row.update([{ item: 'melt', count: '1' }]);
    row.update([]);
    expect(row.el.querySelectorAll('.fk-chip')).toHaveLength(0);
  });
});
