import { describe, it, expect } from 'vitest';
import { escXml, markdownToText, buildAtomSummary } from './atom-utils';

describe('escXml', () => {
  it('escapes ampersands', () => {
    expect(escXml('a & b')).toBe('a &amp; b');
  });

  it('escapes angle brackets', () => {
    expect(escXml('<tag>')).toBe('&lt;tag&gt;');
  });

  it('escapes double quotes', () => {
    expect(escXml('"quoted"')).toBe('&quot;quoted&quot;');
  });
});

describe('markdownToText', () => {
  it('preserves paragraph breaks', () => {
    const result = markdownToText('First paragraph.\n\nSecond paragraph.');
    expect(result).toContain('\n\n');
  });

  it('preserves single line breaks', () => {
    const result = markdownToText('Line one.\nLine two.');
    expect(result).toContain('\n');
  });

  it('strips heading markers', () => {
    expect(markdownToText('## Heading\n\nBody.')).not.toContain('#');
  });

  it('strips list markers but keeps text and newlines', () => {
    const result = markdownToText('- Item one\n- Item two');
    expect(result).not.toMatch(/^-/m);
    expect(result).toContain('\n');
  });

  it('strips bold markers', () => {
    expect(markdownToText('**bold**')).toBe('bold');
  });

  it('strips fenced code blocks entirely', () => {
    const result = markdownToText('Intro.\n\n```\ncode here\n```\n\nOutro.');
    expect(result).not.toContain('code here');
    expect(result).toContain('Intro.');
    expect(result).toContain('Outro.');
  });
});

describe('buildAtomSummary', () => {
  it('uses type="html"', () => {
    expect(buildAtomSummary('hello')).toContain('type="html"');
  });

  it('wraps content in XML-escaped <p> tags', () => {
    expect(buildAtomSummary('hello')).toContain('&lt;p&gt;hello&lt;/p&gt;');
  });

  it('converts single newlines to XML-escaped <br/>', () => {
    expect(buildAtomSummary('line one\nline two')).toContain('line one&lt;br/&gt;line two');
  });

  it('converts double newlines to XML-escaped </p><p>', () => {
    expect(buildAtomSummary('para one\n\npara two')).toContain('para one&lt;/p&gt;&lt;p&gt;para two');
  });

  it('double-encodes special characters so they survive XML then HTML parsing', () => {
    expect(buildAtomSummary('a & <b>')).toContain('a &amp;amp; &amp;lt;b&amp;gt;');
  });

  it('includes a text-only preamble with a link when postUrl and postTitle are provided', () => {
    const result = buildAtomSummary('content', 'https://example.com/post', 'My Post');
    expect(result).toContain('text-only version');
    expect(result).toContain('&lt;a href=&quot;https://example.com/post&quot;&gt;');
    expect(result).toContain('My Post');
  });

  it('double-encodes ampersands in postTitle for the preamble link text', () => {
    const result = buildAtomSummary('content', 'https://example.com/post', 'Tech & Math');
    expect(result).toContain('Tech &amp;amp; Math');
  });

  it('omits the preamble when postUrl and postTitle are not provided', () => {
    const result = buildAtomSummary('content');
    expect(result).not.toContain('text-only version');
    expect(result).not.toContain('&lt;a');
  });
});

// Pipeline test: simulates the full path from raw markdown content (as stored in
// DynamoDB) to a feed entry summary. This is what the feed handler does:
//   markdownToText(post.content) → buildAtomSummary(...)
// A regression here means Thunderbird will show the article as one unbroken wall of text.
describe('markdown content → feed summary pipeline', () => {
  const markdown = [
    '## Introduction',
    '',
    'First paragraph with some text.',
    '',
    'Second paragraph.',
    '',
    '- List item one',
    '- List item two',
    '',
    'Closing paragraph.',
  ].join('\n');

  it('produces paragraph breaks in the Atom summary', () => {
    const text = markdownToText(markdown);
    const summary = buildAtomSummary(text);
    expect(summary).toContain('&lt;/p&gt;&lt;p&gt;');
  });

  it('produces line breaks within paragraphs', () => {
    const singleBreakMd = 'Line one.\nLine two.';
    const text = markdownToText(singleBreakMd);
    const summary = buildAtomSummary(text);
    expect(summary).toContain('&lt;br/&gt;');
  });

  it('strips heading markers before building the summary', () => {
    const text = markdownToText(markdown);
    const summary = buildAtomSummary(text);
    expect(summary).not.toContain('#');
  });
});
