/**
 * @fileoverview Tests for the CSS Scoper
 */

import { describe, expect, it } from 'vitest';
import { OrdoJSCSSParser } from './css-parser.js';
import { OrdoJSCSSScoper } from './css-scoper.js';

describe('OrdoJSCSSScoper', () => {
  it('should scope a simple CSS rule', () => {
    const parser = new OrdoJSCSSParser();
    const scoper = new OrdoJSCSSScoper();

    const css = `.button { color: red; }`;
    const styleBlock = parser.parse(css, true);

    const scopedStyleBlock = scoper.scopeStyles(styleBlock, 'ButtonComponent');
    const scopedCSS = scoper.generateScopedCSS(scopedStyleBlock);

    expect(scopedStyleBlock.rules[0].selector).toContain('[data-ordojs-buttoncomponent-');
    expect(scopedCSS).toContain('.button[data-ordojs-buttoncomponent-');
  });

  it('should scope multiple selectors', () => {
    const parser = new OrdoJSCSSParser();
    const scoper = new OrdoJSCSSScoper();

    const css = `.button, .btn { color: red; }`;
    const styleBlock = parser.parse(css, true);

    const scopedStyleBlock = scoper.scopeStyles(styleBlock, 'ButtonComponent');
    const scopedCSS = scoper.generateScopedCSS(scopedStyleBlock);

    expect(scopedStyleBlock.rules[0].selector).toContain('.button[data-ordojs-buttoncomponent-');
    expect(scopedStyleBlock.rules[0].selector).toContain('.btn[data-ordojs-buttoncomponent-');
    expect(scopedCSS).toContain('.button[data-ordojs-buttoncomponent-');
    expect(scopedCSS).toContain('.btn[data-ordojs-buttoncomponent-');
  });

  it('should handle pseudo-classes and pseudo-elements', () => {
    const parser = new OrdoJSCSSParser();
    const scoper = new OrdoJSCSSScoper();

    const css = `
      .button:hover { color: blue; }
      .input::placeholder { color: gray; }
    `;

    const styleBlock = parser.parse(css, true);
    const scopedStyleBlock = scoper.scopeStyles(styleBlock, 'FormComponent');

    expect(scopedStyleBlock.rules[0].selector).toContain('.button[data-ordojs-formcomponent-');
    expect(scopedStyleBlock.rules[0].selector).toContain(':hover');
    expect(scopedStyleBlock.rules[1].selector).toContain('.input[data-ordojs-formcomponent-');
    expect(scopedStyleBlock.rules[1].selector).toContain('::placeholder');
  });

  it('should not scope global selectors like :root, html, body', () => {
    const parser = new OrdoJSCSSParser();
    const scoper = new OrdoJSCSSScoper();

    const css = `
      :root { --primary-color: blue; }
      html { font-size: 16px; }
      body { margin: 0; }
    `;

    const styleBlock = parser.parse(css, true);
    const scopedStyleBlock = scoper.scopeStyles(styleBlock, 'AppComponent');

    expect(scopedStyleBlock.rules[0].selector).toBe(':root');
    expect(scopedStyleBlock.rules[1].selector).toBe('html');
    expect(scopedStyleBlock.rules[2].selector).toBe('body');
  });

  it('should handle complex selectors with combinators', () => {
    const parser = new OrdoJSCSSParser();
    const scoper = new OrdoJSCSSScoper();

    const css = `
      .parent > .child { margin: 10px; }
      .sibling + .adjacent { padding: 5px; }
      .list ~ .item { border: 1px solid black; }
    `;

    const styleBlock = parser.parse(css, true);
    const scopedStyleBlock = scoper.scopeStyles(styleBlock, 'LayoutComponent');

    expect(scopedStyleBlock.rules[0].selector).toContain('.parent[data-ordojs-layoutcomponent-');
    expect(scopedStyleBlock.rules[0].selector).toContain('> .child[data-ordojs-layoutcomponent-');
    expect(scopedStyleBlock.rules[1].selector).toContain('.sibling[data-ordojs-layoutcomponent-');
    expect(scopedStyleBlock.rules[1].selector).toContain('+ .adjacent[data-ordojs-layoutcomponent-');
    expect(scopedStyleBlock.rules[2].selector).toContain('.list[data-ordojs-layoutcomponent-');
    expect(scopedStyleBlock.rules[2].selector).toContain('~ .item[data-ordojs-layoutcomponent-');
  });

  it('should use class-based scoping when configured', () => {
    const parser = new OrdoJSCSSParser();
    const scoper = new OrdoJSCSSScoper({ useDataAttributes: false });

    const css = `.button { color: red; }`;
    const styleBlock = parser.parse(css, true);

    const scopedStyleBlock = scoper.scopeStyles(styleBlock, 'ButtonComponent');
    const scopedCSS = scoper.generateScopedCSS(scopedStyleBlock);

    expect(scopedStyleBlock.rules[0].selector).toContain('.button.ordoica-buttoncomponent-');
    expect(scopedCSS).toContain('.button.ordoica-buttoncomponent-');
  });

  it('should preserve original selectors when configured', () => {
    const parser = new OrdoJSCSSParser();
    const scoper = new OrdoJSCSSScoper({ preserveOriginalSelectors: true });

    const css = `.button { color: red; }`;
    const styleBlock = parser.parse(css, true);

    const scopedStyleBlock = scoper.scopeStyles(styleBlock, 'ButtonComponent');
    const scopedCSS = scoper.generateScopedCSS(scopedStyleBlock);

    expect(scopedStyleBlock.rules[0].selector).toContain('.button, .button[data-ordojs-buttoncomponent-');
    expect(scopedCSS).toContain('.button, .button[data-ordojs-buttoncomponent-');
  });

  it('should not scope styles when scoped flag is false', () => {
    const parser = new OrdoJSCSSParser();
    const scoper = new OrdoJSCSSScoper();

    const css = `.button { color: red; }`;
    const styleBlock = parser.parse(css, false); // scoped = false

    const scopedStyleBlock = scoper.scopeStyles(styleBlock, 'ButtonComponent');

    expect(scopedStyleBlock.rules[0].selector).toBe('.button');
  });

  it('should generate consistent scope IDs for the same component', () => {
    const scoper = new OrdoJSCSSScoper();

    const attr1 = scoper.generateScopeAttribute('ButtonComponent');
    const attr2 = scoper.generateScopeAttribute('ButtonComponent');

    expect(attr1).toBe(attr2);
  });

  it('should generate different scope IDs for different components', () => {
    const scoper = new OrdoJSCSSScoper();

    const attr1 = scoper.generateScopeAttribute('ButtonComponent');
    const attr2 = scoper.generateScopeAttribute('InputComponent');

    expect(attr1).not.toBe(attr2);
  });
});
