/**
 * @fileoverview Tests for OrdoJS Code Generator
 */

import { describe, expect, it } from 'vitest';
import {
  ComponentAST,
  ComponentNode,
  HTMLElementNode,
  MarkupBlockNode,
  TextNode,
} from '../types/index.js';
import { OrdoJSCodeGenerator } from './code-generator.js';

// Helper to create a simple source range
function createRange(startLine: number, startCol: number, endLine: number, endCol: number) {
  return {
    start: { line: startLine, column: startCol, offset: 0 },
    end: { line: endLine, column: endCol, offset: 0 }
  };
}

describe('OrdoJSCodeGenerator', () => {
  it('should generate client code for a simple component', () => {
    // Create a simple AST manually
    const ast: ComponentAST = {
      component: {
        type: 'Component',
        name: 'SimpleComponent',
        props: [],
        markupBlock: {
          type: 'MarkupBlock',
          elements: [
            {
              type: 'HTMLElement',
              tagName: 'div',
              attributes: [
                {
                  type: 'Attribute',
                  name: 'class',
                  value: 'container',
                  isDirective: false,
                  range: createRange(1, 1, 1, 20)
                }
              ],
              children: [
                {
                  type: 'Text',
                  content: 'Hello, World!',
                  range: createRange(1, 21, 1, 34)
                } as TextNode
              ],
              isSelfClosing: false,
              isVoidElement: false,
              range: createRange(1, 1, 1, 40)
            } as HTMLElementNode
          ],
          textNodes: [],
          interpolations: [],
          range: createRange(1, 1, 1, 40)
        } as MarkupBlockNode,
        range: createRange(1, 1, 1, 50)
      } as ComponentNode,
      dependencies: [],
      exports: [],
      sourceMap: {
        version: 3,
        sources: [],
        names: [],
        mappings: '',
        sourcesContent: []
      }
    };

    const generator = new OrdoJSCodeGenerator();
    const code = generator.generateClientCode(ast);

    // Verify the generated code contains expected elements
    expect(code).toContain('function SimpleComponent(props = {})');
    expect(code).toContain('document.createElement("div")');
    expect(code).toContain('setAttribute("class", "container")');
    expect(code).toContain('document.createTextNode("Hello, World!")');
  });

  it('should generate HTML for a simple component', () => {
    // Create a simple AST manually
    const ast: ComponentAST = {
      component: {
        type: 'Component',
        name: 'SimpleComponent',
        props: [],
        markupBlock: {
          type: 'MarkupBlock',
          elements: [
            {
              type: 'HTMLElement',
              tagName: 'div',
              attributes: [
                {
                  type: 'Attribute',
                  name: 'class',
                  value: 'container',
                  isDirective: false,
                  range: createRange(1, 1, 1, 20)
                }
              ],
              children: [
                {
                  type: 'Text',
                  content: 'Hello, World!',
                  range: createRange(1, 21, 1, 34)
                } as TextNode
              ],
              isSelfClosing: false,
              isVoidElement: false,
              range: createRange(1, 1, 1, 40)
            } as HTMLElementNode
          ],
          textNodes: [],
          interpolations: [],
          range: createRange(1, 1, 1, 40)
        } as MarkupBlockNode,
        range: createRange(1, 1, 1, 50)
      } as ComponentNode,
      dependencies: [],
      exports: [],
      sourceMap: {
        version: 3,
        sources: [],
        names: [],
        mappings: '',
        sourcesContent: []
      }
    };

    const generator = new OrdoJSCodeGenerator();
    const html = generator.generateHTML(ast);

    // Verify the generated HTML contains expected elements
    expect(html).toContain('<div class="container" data-ordojs-hydrate="true">Hello, World!</div>');
    expect(html).toContain('data-ordojs-component="SimpleComponent"');
    expect(html).toContain('data-ordojs-version="1.0"');
  });

  it('should generate HTML with component props', () => {
    // Create a component AST with props
    const ast: ComponentAST = {
      component: {
        type: 'Component',
        name: 'PropsComponent',
        props: [
          {
            type: 'PropDefinition',
            name: 'title',
            dataType: { name: 'string', isArray: false, isOptional: false, genericTypes: [] },
            isRequired: true,
            range: createRange(1, 1, 1, 20)
          },
          {
            type: 'PropDefinition',
            name: 'count',
            dataType: { name: 'number', isArray: false, isOptional: false, genericTypes: [] },
            defaultValue: {
              type: 'Expression',
              expressionType: 'LITERAL',
              value: 0,
              range: createRange(1, 30, 1, 31)
            },
            isRequired: false,
            range: createRange(1, 21, 1, 40)
          }
        ],
        markupBlock: {
          type: 'MarkupBlock',
          elements: [
            {
              type: 'HTMLElement',
              tagName: 'div',
              attributes: [],
              children: [
                {
                  type: 'Text',
                  content: 'Component with props',
                  range: createRange(1, 21, 1, 40)
                } as TextNode
              ],
              isSelfClosing: false,
              isVoidElement: false,
              range: createRange(1, 1, 1, 50)
            } as HTMLElementNode
          ],
          textNodes: [],
          interpolations: [],
          range: createRange(1, 1, 1, 60)
        } as MarkupBlockNode,
        range: createRange(1, 1, 1, 70)
      } as ComponentNode,
      dependencies: [],
      exports: [],
      sourceMap: {
        version: 3,
        sources: [],
        names: [],
        mappings: '',
        sourcesContent: []
      }
    };

    // Generate HTML with initial props
    const generator = new OrdoJSCodeGenerator();
    const html = generator.generateHTML(ast, { title: 'Hello', count: 42 });

    // Verify the generated HTML contains props data
    expect(html).toContain('data-props=');

    // Just check that the HTML contains the expected values
    expect(html).toContain('title');
    expect(html).toContain('Hello');
    expect(html).toContain('count');
    expect(html).toContain('42');
  });

  it('should generate complete code bundle', () => {
    // Create a simple AST manually
    const ast: ComponentAST = {
      component: {
        type: 'Component',
        name: 'SimpleComponent',
        props: [],
        markupBlock: {
          type: 'MarkupBlock',
          elements: [
            {
              type: 'HTMLElement',
              tagName: 'div',
              attributes: [],
              children: [
                {
                  type: 'Text',
                  content: 'Hello, World!',
                  range: createRange(1, 21, 1, 34)
                } as TextNode
              ],
              isSelfClosing: false,
              isVoidElement: false,
              range: createRange(1, 1, 1, 40)
            } as HTMLElementNode
          ],
          textNodes: [],
          interpolations: [],
          range: createRange(1, 1, 1, 40)
        } as MarkupBlockNode,
        range: createRange(1, 1, 1, 50)
      } as ComponentNode,
      dependencies: [],
      exports: [],
      sourceMap: {
        version: 3,
        sources: [],
        names: [],
        mappings: '',
        sourcesContent: []
      }
    };

    const generator = new OrdoJSCodeGenerator();
    const bundle = generator.generate(ast);

    // Verify the bundle contains both client code and HTML
    expect(bundle.client).toBeDefined();
    expect(bundle.html).toBeDefined();
    expect(bundle.sourceMap).toBeDefined();
  });
});
