import { describe, it, expect } from 'vitest';
import { RichTextBlock } from '@shared/kernel/rich-text';
import { PlaceholderDef, lintPlaceholders, severityOf } from './placeholders';

const valid: PlaceholderDef[] = [
  { key: 'naam', label: 'Naam', autoResolvable: true }, // clean when used
  { key: 'reden', label: 'Reden', autoResolvable: false }, // manual → unresolved-at-send
  { key: 'oud_veld', label: 'Oud veld', autoResolvable: true, deprecated: true },
  { key: 'niet_invulbaar', label: 'Niet invulbaar', autoResolvable: true, fillable: false },
];

const withPlaceholder = (key: string): RichTextBlock => ({ paragraphs: [{ nodes: [{ type: 'placeholder', key }] }] });
const withText = (text: string): RichTextBlock => ({ paragraphs: [{ nodes: [{ type: 'text', text }] }] });

describe('lintPlaceholders', () => {
  it('clean content (auto-resolvable, fillable, current key) yields no diagnostics', () => {
    expect(lintPlaceholders(withPlaceholder('naam'), valid, 'b1')).toEqual([]);
    expect(lintPlaceholders(withText('gewone tekst'), valid, 'b1')).toEqual([]);
  });

  it('flags an unknown key as an error', () => {
    const [d] = lintPlaceholders(withPlaceholder('onbekend'), valid, 'b1');
    expect(d.code).toBe('unknown-placeholder');
    expect(d.severity).toBe('error');
    expect(d.placeholderKey).toBe('onbekend');
    expect(d.location).toEqual({ blockId: 'b1', paragraphIndex: 0, nodeIndex: 0 });
  });

  it('flags a not-fillable key as an error', () => {
    const [d] = lintPlaceholders(withPlaceholder('niet_invulbaar'), valid, 'b1');
    expect(d.code).toBe('not-fillable');
    expect(d.severity).toBe('error');
  });

  it('flags a deprecated key as a warning', () => {
    const [d] = lintPlaceholders(withPlaceholder('oud_veld'), valid, 'b1');
    expect(d.code).toBe('deprecated');
    expect(d.severity).toBe('warning');
  });

  it('flags a manual placeholder as unresolved-at-send (warning)', () => {
    const [d] = lintPlaceholders(withPlaceholder('reden'), valid, 'b1');
    expect(d.code).toBe('unresolved-at-send');
    expect(d.severity).toBe('warning');
  });

  it('flags raw braces in text as malformed (paste safety net)', () => {
    const [d] = lintPlaceholders(withText('Beste {{naam'), valid, 'b1');
    expect(d.code).toBe('malformed');
    expect(d.severity).toBe('error');
    expect(d.placeholderKey).toBeUndefined();
  });

  it('returns diagnostics in document order across paragraphs/nodes', () => {
    const content: RichTextBlock = {
      paragraphs: [
        { nodes: [{ type: 'placeholder', key: 'onbekend' }] },
        { nodes: [{ type: 'text', text: 'ok' }, { type: 'placeholder', key: 'reden' }] },
      ],
    };
    const codes = lintPlaceholders(content, valid, 'b1').map((d) => `${d.code}@${d.location.paragraphIndex}.${d.location.nodeIndex}`);
    expect(codes).toEqual(['unknown-placeholder@0.0', 'unresolved-at-send@1.1']);
  });

  it('severityOf maps each code to its policy', () => {
    expect(severityOf('malformed')).toBe('error');
    expect(severityOf('unknown-placeholder')).toBe('error');
    expect(severityOf('not-fillable')).toBe('error');
    expect(severityOf('deprecated')).toBe('warning');
    expect(severityOf('unresolved-at-send')).toBe('warning');
  });
});
