/**
 * @fileoverview Comprehensive parser tests
 */

import { describe, expect, it } from 'vitest';
import { DirectiveType, ExpressionType, LifecycleType } from '../types/index.js';
import { OrdoJSLexer } from './lexer.js';
import { OrdoJSParser } from './parser.js';

describe('OrdoJSParser - Comprehensive Tests', () => {
  const parseComponent = (source: string) => {
    const lexer = new OrdoJSLexer(source, 'test.ordo');
    const tokens = lexer.tokenize();
    const parser = new OrdoJSParser(tokens, {}, 'test.ordo');
    return parser.parse();
  };

  it('should parse complex component with all features', () => {
    const source = `
      component TodoApp(initialTodos: Todo[] = []) {
        client {
          let todos = initialTodos;
          let newTodo = "";
          const filter = "all";

          onMount() {
            loadTodos();
          }

          addTodo(): void {
            if (newTodo.trim()) {
              todos = [...todos, {
                id: Date.now(),
                text: newTodo.trim(),
                completed: false
              }];
              newTodo = "";
            }
          }
        }

        server {
          public async loadTodos(): Promise<Todo[]> {
            return await db.todos.findMany();
          }
        }

        markup {
          <div class="todo-app">
            <header>
              <h1>Todo App</h1>
              <input
                bind:value={newTodo}
                on:keydown={handleKeydown}
                placeholder="Add a new todo..."
              />
              <button on:click={addTodo}>Add</button>
            </header>

            <main>
              <ul class="todo-list">
                <li>
                  <span>{todo.text}</span>
                  <button on:click={removeTodo}>×</button>
                </li>
              </ul>
            </main>
          </div>
        }
      }
    `;

    const ast = parseComponent(source);

    // Verify component structure
    expect(ast.component.name).toBe('TodoApp');
    expect(ast.component.props).toHaveLength(1);
    expect(ast.component.props[0].name).toBe('initialTodos');
    expect(ast.component.props[0].dataType.name).toBe('Todo');
    expect(ast.component.props[0].dataType.isArray).toBe(true);

    // Verify client block
    const clientBlock = ast.component.clientBlock!;
    expect(clientBlock).toBeDefined();
    expect(clientBlock.reactiveVariables).toHaveLength(3);
    expect(clientBlock.lifecycle).toHaveLength(1);
    expect(clientBlock.functions).toHaveLength(1);

    // Verify reactive variables
    expect(clientBlock.reactiveVariables[0].name).toBe('todos');
    expect(clientBlock.reactiveVariables[0].isConst).toBe(false);
    expect(clientBlock.reactiveVariables[1].name).toBe('newTodo');
    expect(clientBlock.reactiveVariables[2].name).toBe('filter');
    expect(clientBlock.reactiveVariables[2].isConst).toBe(true);

    // Verify lifecycle hooks
    expect(clientBlock.lifecycle[0].hookType).toBe(LifecycleType.ON_MOUNT);

    // Verify functions
    expect(clientBlock.functions[0].name).toBe('addTodo');
    expect(clientBlock.functions[0].returnType.name).toBe('void');

    // Verify server block
    const serverBlock = ast.component.serverBlock!;
    expect(serverBlock).toBeDefined();
    expect(serverBlock.functions).toHaveLength(1);
    expect(serverBlock.functions[0].name).toBe('loadTodos');
    expect(serverBlock.functions[0].isPublic).toBe(true);

    // Verify markup block
    const markupBlock = ast.component.markupBlock;
    expect(markupBlock).toBeDefined();
    expect(markupBlock.elements).toHaveLength(1);

    // Verify main div element
    const mainDiv = markupBlock.elements[0];
    expect(mainDiv.tagName).toBe('div');
    expect(mainDiv.attributes).toHaveLength(1);
    expect(mainDiv.attributes[0].name).toBe('class');
    expect(mainDiv.attributes[0].value).toBe('todo-app');

    // Verify nested structure
    expect(mainDiv.children).toHaveLength(2); // header and main

    console.log('✅ Complex component parsing successful!');
  });

  it('should parse HTML with directives and interpolations', () => {
    const source = `
      component TestComponent {
        markup {
          <div>
            <input bind:value={inputValue} />
            <button on:click={handleClick}>Click: {count}</button>
            <span>{user.name}</span>
          </div>
        }
      }
    `;

    const ast = parseComponent(source);
    const markupBlock = ast.component.markupBlock;
    const mainDiv = markupBlock.elements[0];

    // Find input element
    const inputElement = mainDiv.children.find(child =>
      child.type === 'HTMLElement' && (child as any).tagName === 'input'
    ) as any;

    expect(inputElement).toBeDefined();
    expect(inputElement.attributes[0].isDirective).toBe(true);
    expect(inputElement.attributes[0].directiveType).toBe(DirectiveType.BIND);

    // Find button element
    const buttonElement = mainDiv.children.find(child =>
      child.type === 'HTMLElement' && (child as any).tagName === 'button'
    ) as any;

    expect(buttonElement).toBeDefined();
    expect(buttonElement.attributes[0].isDirective).toBe(true);
    expect(buttonElement.attributes[0].directiveType).toBe(DirectiveType.ON);

    // Verify interpolations
    expect(markupBlock.interpolations.length).toBeGreaterThan(0);

    console.log('✅ HTML directives and interpolations parsing successful!');
  });

  it('should handle nested HTML elements correctly', () => {
    const source = `
      component TestComponent {
        markup {
          <div class="container">
            <header>
              <h1>Title</h1>
              <nav>
                <ul>
                  <li><a href="/">Home</a></li>
                  <li><a href="/about">About</a></li>
                </ul>
              </nav>
            </header>
            <main>
              <p>Content goes here</p>
            </main>
          </div>
        }
      }
    `;

    const ast = parseComponent(source);
    const markupBlock = ast.component.markupBlock;
    const containerDiv = markupBlock.elements[0];

    expect(containerDiv.tagName).toBe('div');
    expect(containerDiv.children).toHaveLength(2); // header and main

    // Verify nested structure
    const header = containerDiv.children[0] as any;
    expect(header.tagName).toBe('header');
    expect(header.children).toHaveLength(2); // h1 and nav

    const nav = header.children[1] as any;
    expect(nav.tagName).toBe('nav');

    const ul = nav.children[0] as any;
    expect(ul.tagName).toBe('ul');
    expect(ul.children).toHaveLength(2); // two li elements

    console.log('✅ Nested HTML elements parsing successful!');
  });

  it('should parse expressions correctly', () => {
    const source = `
      component TestComponent {
        client {
          let result = a + b * c - d / e;
          let comparison = x > y && z < w;
          let assignment = count++;
          let call = Math.max(a, b, c);
          let member = user.profile.name;
        }
        markup {
          <div>Test</div>
        }
      }
    `;

    const ast = parseComponent(source);
    const clientBlock = ast.component.clientBlock!;
    const variables = clientBlock.reactiveVariables;

    // Test binary expression
    expect(variables[0].initialValue.expressionType).toBe(ExpressionType.BINARY);

    // Test logical expression
    expect(variables[1].initialValue.expressionType).toBe(ExpressionType.BINARY);

    // Test call expression
    expect(variables[3].initialValue.expressionType).toBe(ExpressionType.CALL);

    // Test member expression
    expect(variables[4].initialValue.expressionType).toBe(ExpressionType.MEMBER);

    console.log('✅ Expression parsing successful!');
  });
});
