import { describe, test, expect } from 'vitest';
import { HtmlBlock, purifyHtml } from './HtmlBlock';

const toHtml = (HtmlBlock as any).toHtml;

describe('HtmlBlock.toHtml sanitizes raw code (A4.1)', () => {
  test('strips <script> and on-handlers from exported output', () => {
    const { html } = toHtml({ code: '<script>alert(1)</script><p onclick="x">hi</p>' }, '');
    expect(html).not.toContain('<script');
    expect(html).not.toContain('onclick');
    expect(html).toContain('<p>hi</p>');
  });

  test('does not wrap output in an unsanitized element carrying the style prop raw', () => {
    // toHtml only ever returns the sanitized `code` blob -- there is no
    // wrapper <div style="..."> in the exported HTML, so a malicious
    // `style` prop (e.g. an attacker-controlled object with a breakout
    // toString()) has nothing to splice into.
    const malicious = { toString: () => 'color:red" onmouseover="alert(1)' } as any;
    const { html } = toHtml({ code: '<p>hi</p>', style: malicious }, '');
    expect(html).not.toMatch(/onmouseover/);
    expect(html).not.toMatch(/<div/);
    expect(html).toBe('<p>hi</p>');
  });
});

test('toHtml never emits the style prop (the other half of the render/export contract)', () => {
  const out = (HtmlBlock as any).toHtml(
    { code: '<p>hi</p>', style: { backgroundColor: '#ff0000', padding: '40px' } },
    '',
  );
  expect(out.html).toBe('<p>hi</p>');
  expect(out.html).not.toContain('background');
  expect(out.html).not.toContain('40px');
});

describe('HtmlBlock.toHtml markup path (C1 review finding)', () => {
  test('a style attribute inside `code` (e.g. from the toolbar colour picker) reaches exported output', () => {
    const { html } = toHtml({ code: '<p style="color: #ff0000">red text</p>' }, '');
    expect(html).toBe('<p style="color: #ff0000">red text</p>');
  });

  test('a table inside `code` reaches exported output', () => {
    const code = '<table><tbody><tr><td>Cell</td></tr></tbody></table>';
    const { html } = toHtml({ code }, '');
    expect(html).toBe(code);
  });
});

describe('HtmlBlock.toHtml -- Task 25: block-scoped <style>, and editor/export byte-parity', () => {
  test('a <style>-bearing block exports the same scoped wrapper purifyHtml() would produce in the editor canvas', () => {
    // The editor canvas (HtmlBlock component) and toHtml() (Preview +
    // Published export) both call the exact same purifyHtml(code) -- this
    // is the byte-parity invariant this project treats as a hard
    // requirement. Proven here by calling purifyHtml directly (as the
    // canvas's useMemo does) and toHtml (as export does) on the identical
    // code string and asserting the two never diverge.
    const code = '<style>h1 { color: red; }</style><h1>Hi</h1>';
    const { html } = toHtml({ code }, '');
    expect(html).toBe(purifyHtml(code));
  });

  test('a <style>-free block still exports byte-identical to pre-Task-25 output (no wrapper regression) via toHtml', () => {
    const code = '<p>hello</p>';
    const { html } = toHtml({ code }, '');
    expect(html).toBe('<p>hello</p>');
    expect(html).not.toContain('<div');
  });
});
