import { describe, expect, it } from 'vitest';
import { applyArticlePatches, applyAndValidate } from '../articlePatches';

const article = () => ({
  title: 'Onboarding',
  intro: 'Old intro.',
  sections: [
    { heading: 'Day one', body: 'First day body, three sentences long. Welcome. Read the handbook.' },
    { heading: 'Day two', body: 'Second day body. Three sentences. Meet your team.' },
  ],
  keyTakeaways: ['Show up', 'Ask questions'],
});

describe('applyArticlePatches', () => {
  it('does not mutate the input article', () => {
    const original = article();
    const snapshot = JSON.parse(JSON.stringify(original));
    applyArticlePatches(original, [
      { name: 'set_intro', input: { intro: 'New intro.' } },
    ]);
    expect(original).toEqual(snapshot);
  });

  it('set_intro replaces the intro', () => {
    const result = applyArticlePatches(article(), [
      { name: 'set_intro', input: { intro: 'Punchier intro.' } },
    ]);
    expect(result.intro).toBe('Punchier intro.');
  });

  it('set_section replaces the matching section body (case-insensitive)', () => {
    const result = applyArticlePatches(article(), [
      { name: 'set_section', input: { heading: 'DAY ONE', body: 'Rewritten body. With several sentences. Indeed.' } },
    ]);
    expect(result.sections[0].body).toMatch(/Rewritten body/);
    expect(result.sections[1].body).toMatch(/Second day body/);
  });

  it('add_section position=start prepends a new section', () => {
    const result = applyArticlePatches(article(), [
      { name: 'add_section', input: { heading: 'Before', body: 'New intro section. Three sentences. Indeed.', position: 'start' } },
    ]);
    expect(result.sections[0].heading).toBe('Before');
    expect(result.sections).toHaveLength(3);
  });

  it('add_section position=end appends a new section', () => {
    const result = applyArticlePatches(article(), [
      { name: 'add_section', input: { heading: 'After', body: 'Closing section. Three sentences. Indeed.', position: 'end' } },
    ]);
    expect(result.sections[2].heading).toBe('After');
  });

  it('remove_section drops the matching section', () => {
    const result = applyArticlePatches(article(), [
      { name: 'remove_section', input: { heading: 'Day one' } },
    ]);
    expect(result.sections).toHaveLength(1);
    expect(result.sections[0].heading).toBe('Day two');
  });

  it('replace_takeaways swaps the key takeaways', () => {
    const result = applyArticlePatches(article(), [
      { name: 'replace_takeaways', input: { items: ['First', 'Second', 'Third'] } },
    ]);
    expect(result.keyTakeaways).toEqual(['First', 'Second', 'Third']);
  });

  it('applies multiple patches in order', () => {
    const result = applyArticlePatches(article(), [
      { name: 'set_intro', input: { intro: 'Brand new intro.' } },
      { name: 'remove_section', input: { heading: 'Day one' } },
      { name: 'add_section', input: { heading: 'New', body: 'Body of the new section. Three sentences. Yes.', position: 'end' } },
    ]);
    expect(result.intro).toBe('Brand new intro.');
    expect(result.sections.map(s => s.heading)).toEqual(['Day two', 'New']);
  });

  it('falls back to appending when set_section cannot find a matching heading', () => {
    const result = applyArticlePatches(article(), [
      { name: 'set_section', input: { heading: 'Nonexistent', body: 'New body, with three sentences. Yes indeed. Foo.' } },
    ]);
    expect(result.sections).toHaveLength(3);
    expect(result.sections[2].heading).toBe('Nonexistent');
  });
});

describe('applyAndValidate', () => {
  it('returns the patched article when valid', () => {
    const patched = applyAndValidate(article(), [
      { name: 'set_intro', input: { intro: 'Tighter intro.' } },
    ]);
    expect(patched.intro).toBe('Tighter intro.');
  });

  it('throws when patches strip the article to invalid', () => {
    expect(() =>
      applyAndValidate(article(), [
        { name: 'remove_section', input: { heading: 'Day one' } },
        { name: 'remove_section', input: { heading: 'Day two' } },
      ]),
    ).toThrow(/invalid article/i);
  });
});
