import { describe, expect, it } from 'vitest';
import { en } from './en';
import { de } from './de';
import { fmt, fmtSplit, plural } from './index';

describe('fmt', () => {
  it('interpolates named vars', () => {
    expect(fmt('Ratings as of {date}', { date: '2026-06-10' })).toBe('Ratings as of 2026-06-10');
  });
  it('leaves unknown placeholders intact', () => {
    expect(fmt('{a} {b}', { a: 1 })).toBe('1 {b}');
  });
});

describe('plural', () => {
  const p = { one: '{n} match scored', other: '{n} matches scored' };
  it('uses one at exactly 1', () => expect(plural(p, 1)).toBe('1 match scored'));
  it('uses other elsewhere', () => {
    expect(plural(p, 0)).toBe('0 matches scored');
    expect(plural(p, 7)).toBe('7 matches scored');
  });
});

describe('fmtSplit', () => {
  it('splits around placeholders preserving order', () => {
    expect(fmtSplit('scored with the {rps}. Done.')).toEqual([
      'scored with the ', { key: 'rps' }, '. Done.',
    ]);
  });
  it('handles adjacent and leading placeholders', () => {
    expect(fmtSplit('{a}{b} tail')).toEqual([{ key: 'a' }, { key: 'b' }, ' tail']);
  });
});

describe('en/de dictionary parity', () => {
  type Node = string | { [k: string]: Node };
  const walk = (a: Node, b: Node, path: string, problems: string[]): void => {
    if (typeof a === 'string' || typeof b === 'string') {
      if (typeof a !== typeof b) problems.push(`type mismatch at ${path}`);
      if (typeof b === 'string' && b.trim() === '') problems.push(`empty translation at ${path}`);
      return;
    }
    for (const k of Object.keys(a)) {
      if (!(k in b)) { problems.push(`missing in de: ${path}.${k}`); continue; }
      walk(a[k]!, b[k]!, `${path}.${k}`, problems);
    }
    for (const k of Object.keys(b)) if (!(k in a)) problems.push(`extra in de: ${path}.${k}`);
  };

  it('de mirrors en exactly, with no empty leaves', () => {
    const problems: string[] = [];
    walk(en as unknown as Node, de as unknown as Node, '$', problems);
    expect(problems).toEqual([]);
  });

  it('placeholders agree between en and de', () => {
    const collect = (n: Node, path: string, into: Map<string, string>): void => {
      if (typeof n === 'string') { into.set(path, [...n.matchAll(/\{(\w+)\}/g)].map((m) => m[1]).sort().join(',')); return; }
      for (const k of Object.keys(n)) collect(n[k]!, `${path}.${k}`, into);
    };
    const a = new Map<string, string>(); const b = new Map<string, string>();
    collect(en as unknown as Node, '$', a);
    collect(de as unknown as Node, '$', b);
    const diff = [...a].filter(([k, v]) => b.get(k) !== v).map(([k]) => k);
    expect(diff).toEqual([]);
  });
});
