/**
 * @fileoverview OrdoJS Parser - Refactored modular implementation
 * @author OrdoJS Framework Team
 */

import {
  ExpressionType,
  LifecycleType,
  StatementType,
  SyntaxError,
  TokenType,
  type AttributeNode,
  type ClientBlockNode,
  type ComponentAST,
  type ComponentNode,
  type ExpressionNode,
  type FunctionNode,
  type HTMLElementNode,
  type InterpolationNode,
  type LifecycleHookNode,
  type MarkupBlockNode,
  type ParameterNode,
  type PropDefinition,
  type ReactiveVariableNode,
  type ServerBlockNode,
  type ServerFunctionNode,
  type SourcePosition,
  type StatementNode,
  type TextNode,
  type Token,
  type TokenStream,
  type TypeAnnotation
} from '../types/index.js';

/**
 * Parser configuration options
 */
export interface ParserOptions {
  /** Enable error recovery mode */
  allowRecovery?: boolean;
  /** Maximum number of errors before stopping */
  maxErrors?: number;
  /** Enable strict mode parsing */
  strictMode?: boolean;
  /** Generate source maps */
  generateSourceMaps?: boolean;
  /** Custom AST node processors */
  nodeProcessors?: ASTNodeProcessor[];
  /** Enable experimental features */
  experimentalFeatures?: string[];
}

/**
 * AST node processor for plugin-based AST transformation
 */
export interface ASTNodeProcessor {
  /** Processor name */
  name: string;
  /** Node types this processor handles */
  handles: string[];
  /** Process AST node and return modified node */
  process<T extends { type: string }>(node: T, context: ParserContext): T;
}

/**
 * Parser context for tracking state during parsing
 */
export interface ParserContext {
  /** Current component being parsed */
  component?: ComponentNode;
  /** Current block context */
  blockContext: 'component' | 'client' | 'server' | 'markup';
  /** Symbol table for scope tracking */
  symbolTable: Map<string, SymbolInfo>;
  /** Current scope depth */
  scopeDepth: number;
  /** Parser options */
  options: Required<ParserOptions>;
}

/**
 * Symbol information for scope tracking
 */
export interface SymbolInfo {
  name: string;
  type: 'variable' | 'function' | 'parameter' | 'prop';
  dataType?: TypeAnnotation;
  scope: number;
  position: SourcePosition;
  isConst?: boolean;
  isPublic?: boolean;
}

/**
 * Parser state for error recovery and debugging
 */
export interface ParserState {
  tokenStream: TokenStream;
  current: number;
  filename: string;
  errors: SyntaxError[];
  warnings: SyntaxError[];
  context: ParserContext;
}

/**
 * Expression precedence levels for operator precedence parsing
 */
enum Precedence {
  NONE = 0,
  ASSIGNMENT = 1,    // =
  OR = 2,           // ||
  AND = 3,          // &&
  EQUALITY = 4,     // == !=
  COMPARISON = 5,   // < > <= >=
  TERM = 6,         // + -
  FACTOR = 7,       // * / %
  UNARY = 8,        // ! -
  CALL = 9,         // . ()
  PRIMARY = 10
}

/**
 * Parse rule for expression parsing
 */
interface ParseRule {
  prefix?: (parser: OrdoJSParser) => ExpressionNode;
  infix?: (parser: OrdoJSParser, left: ExpressionNode) => ExpressionNode;
  precedence: Precedence;
}

/**
 * Enhanced OrdoJS Parser with modular architecture
 */
export class OrdoJSParser {
  private state: ParserState;
  private options: Required<ParserOptions>;
  private parseRules: Map<TokenType, ParseRule>;

  constructor(tokenStream: TokenStream, options: ParserOptions = {}, filename: string = 'unknown') {
    this.options = {
      allowRecovery: false,
      maxErrors: 10,
      strictMode: true,
      generateSourceMaps: true,
      nodeProcessors: [],
      experimentalFeatures: [],
      ...options
    };

    this.state = {
      tokenStream,
      current: 0,
      filename,
      errors: [],
      warnings: [],
      context: {
        blockContext: 'component',
        symbolTable: new Map(),
        scopeDepth: 0,
        options: this.options
      }
    };

    this.parseRules = this.initializeParseRules();
  }

  /**
   * Parse token stream into Component AST
   */
  parse(): ComponentAST {
    try {
      const component = this.parseComponent();

      if (this.state.errors.length > 0 && !this.options.allowRecovery) {
        throw this.state.errors[0];
      }

      // Process AST through registered processors
      this.processAST(component);

      return {
        component,
        dependencies: this.extractDependencies(component),
        exports: this.extractExports(component),
        sourceMap: this.options.generateSourceMaps ? this.generateSourceMap() : {
          version: 3,
          sources: [this.state.filename],
          names: [],
          mappings: '',
          sourcesContent: []
        }
      };
    } catch (error) {
      if (error instanceof SyntaxError) {
        throw error;
      }
      throw new SyntaxError(
        `Parser error: ${error instanceof Error ? error.message : 'Unknown error'}`,
        this.getCurrentPosition(),
        ['valid component syntax'],
        this.peek().value,
        this.state.filename
      );
    }
  }

  /**
   * Get all errors encountered during parsing
   */
  getErrors(): SyntaxError[] {
    return [...this.state.errors];
  }

  /**
   * Get all warnings encountered during parsing
   */
  getWarnings(): SyntaxError[] {
    return [...this.state.warnings];
  }

  /**
   * Add custom AST node processor
   */
  addNodeProcessor(processor: ASTNodeProcessor): void {
    this.options.nodeProcessors.push(processor);
  }

  /**
   * Remove AST node processor by name
   */
  removeNodeProcessor(name: string): boolean {
    const index = this.options.nodeProcessors.findIndex(p => p.name === name);
    if (index >= 0) {
      this.options.nodeProcessors.splice(index, 1);
      return true;
    }
    return false;
  }

  private initializeParseRules(): Map<TokenType, ParseRule> {
    const rules = new Map<TokenType, ParseRule>();

    // Literals
    rules.set(TokenType.BOOLEAN, { prefix: this.parseLiteral.bind(this), precedence: Precedence.NONE });
    rules.set(TokenType.NUMBER, { prefix: this.parseLiteral.bind(this), precedence: Precedence.NONE });
    rules.set(TokenType.STRING, { prefix: this.parseLiteral.bind(this), precedence: Precedence.NONE });
    rules.set(TokenType.IDENTIFIER, { prefix: this.parseIdentifier.bind(this), precedence: Precedence.NONE });

    // Grouping
    rules.set(TokenType.LEFT_PAREN, { prefix: this.parseGrouping.bind(this), precedence: Precedence.NONE });

    // Unary operators
    rules.set(TokenType.LOGICAL_NOT, { prefix: this.parseUnary.bind(this), precedence: Precedence.NONE });
    rules.set(TokenType.MINUS, { prefix: this.parseUnary.bind(this), infix: this.parseBinary.bind(this), precedence: Precedence.TERM });

    // Binary operators
    rules.set(TokenType.PLUS, { infix: this.parseBinary.bind(this), precedence: Precedence.TERM });
    rules.set(TokenType.MULTIPLY, { infix: this.parseBinary.bind(this), precedence: Precedence.FACTOR });
    rules.set(TokenType.DIVIDE, { infix: this.parseBinary.bind(this), precedence: Precedence.FACTOR });
    rules.set(TokenType.MODULO, { infix: this.parseBinary.bind(this), precedence: Precedence.FACTOR });

    // Comparison operators
    rules.set(TokenType.EQUALS, { infix: this.parseBinary.bind(this), precedence: Precedence.EQUALITY });
    rules.set(TokenType.NOT_EQUALS, { infix: this.parseBinary.bind(this), precedence: Precedence.EQUALITY });
    rules.set(TokenType.LESS_THAN, { infix: this.parseBinary.bind(this), precedence: Precedence.COMPARISON });
    rules.set(TokenType.GREATER_THAN, { infix: this.parseBinary.bind(this), precedence: Precedence.COMPARISON });
    rules.set(TokenType.LESS_EQUAL, { infix: this.parseBinary.bind(this), precedence: Precedence.COMPARISON });
    rules.set(TokenType.GREATER_EQUAL, { infix: this.parseBinary.bind(this), precedence: Precedence.COMPARISON });

    // Logical operators
    rules.set(TokenType.LOGICAL_AND, { infix: this.parseBinary.bind(this), precedence: Precedence.AND });
    rules.set(TokenType.LOGICAL_OR, { infix: this.parseBinary.bind(this), precedence: Precedence.OR });

    // Assignment
    rules.set(TokenType.ASSIGN, { infix: this.parseAssignment.bind(this), precedence: Precedence.ASSIGNMENT });

    // Call and member access
    rules.set(TokenType.LEFT_PAREN, { infix: this.parseCall.bind(this), precedence: Precedence.CALL });
    rules.set(TokenType.DOT, { infix: this.parseMember.bind(this), precedence: Precedence.CALL });

    return rules;
  }

  private parseComponent(): ComponentNode {
    const start = this.getCurrentPosition();
    this.state.context.blockContext = 'component';

    // Expect 'component' keyword
    if (!this.match(TokenType.COMPONENT)) {
      this.throwError("Expected 'component' keyword", ['component']);
    }

    // Parse component name
    const nameToken = this.consume(TokenType.IDENTIFIER, 'Expected component name');
    const name = nameToken.value;

    // Validate component name
    if (!this.isValidComponentName(name)) {
      this.throwError(
        'Component name must start with uppercase letter and contain only alphanumeric characters',
        []
      );
    }

    // Add component to symbol table
    this.addSymbol(name, 'function', undefined, this.getCurrentPosition());

    // Parse optional props
    const props = this.parseProps();

    // Expect component body
    this.consume(TokenType.LEFT_BRACE, "Expected '{' after component declaration");
    this.enterScope();

    // Parse component blocks
    let clientBlock: ClientBlockNode | undefined;
    let serverBlock: ServerBlockNode | undefined;
    let markupBlock: MarkupBlockNode | undefined;

    while (!this.check(TokenType.RIGHT_BRACE) && !this.isAtEnd()) {
      if (this.match(TokenType.CLIENT)) {
        if (clientBlock) {
          this.throwError('Duplicate client block', []);
        }
        clientBlock = this.parseClientBlock();
      } else if (this.match(TokenType.SERVER)) {
        if (serverBlock) {
          this.throwError('Duplicate server block', []);
        }
        serverBlock = this.parseServerBlock();
      } else if (this.match(TokenType.MARKUP)) {
        if (markupBlock) {
          this.throwError('Duplicate markup block', []);
        }
        markupBlock = this.parseMarkupBlock();
      } else {
        this.throwError('Expected client, server, or markup block', ['client', 'server', 'markup']);
      }
    }

    this.exitScope();
    this.consume(TokenType.RIGHT_BRACE, "Expected '}' after component body");

    // Markup block is required
    if (!markupBlock) {
      this.throwError('Component must have a markup block', ['markup']);
    }

    const end = this.getCurrentPosition();

    const component: ComponentNode = {
      type: 'Component',
      name,
      props,
      clientBlock,
      serverBlock,
      markupBlock: markupBlock!,
      range: { start, end }
    };

    this.state.context.component = component;
    return component;
  }

  private parseProps(): PropDefinition[] {
    const props: PropDefinition[] = [];

    if (!this.check(TokenType.LEFT_PAREN)) {
      return props;
    }

    this.advance(); // consume '('

    while (!this.check(TokenType.RIGHT_PAREN) && !this.isAtEnd()) {
      const prop = this.parseProp();
      props.push(prop);

      // Add prop to symbol table
      this.addSymbol(prop.name, 'prop', prop.dataType, prop.range.start);

      if (!this.match(TokenType.COMMA)) {
        break;
      }
    }

    this.consume(TokenType.RIGHT_PAREN, "Expected ')' after props");
    return props;
  }

  private parseProp(): PropDefinition {
    const start = this.getCurrentPosition();

    const nameToken = this.consume(TokenType.IDENTIFIER, 'Expected prop name');
    this.consume(TokenType.COLON, "Expected ':' after prop name");

    const dataType = this.parseTypeAnnotation();

    let defaultValue: ExpressionNode | undefined;
    let isRequired = true;

    if (this.match(TokenType.ASSIGN)) {
      isRequired = false;
      defaultValue = this.parseExpression();
    }

    const end = this.getCurrentPosition();

    return {
      type: 'PropDefinition',
      name: nameToken.value,
      dataType,
      defaultValue,
      isRequired,
      range: { start, end }
    } as PropDefinition;
  }

  private parseTypeAnnotation(): TypeAnnotation {
    const nameToken = this.consume(TokenType.IDENTIFIER, 'Expected type name');
    let isArray = false;
    let isOptional = false;
    const genericTypes: TypeAnnotation[] = [];

    // Handle array types
    if (this.match(TokenType.LEFT_BRACKET)) {
      this.consume(TokenType.RIGHT_BRACKET, "Expected ']' after '['");
      isArray = true;
    }

    // Handle optional types
    if (this.match(TokenType.QUESTION)) {
      isOptional = true;
    }

    // Handle generic types (simplified)
    if (this.match(TokenType.LESS_THAN)) {
      do {
        genericTypes.push(this.parseTypeAnnotation());
      } while (this.match(TokenType.COMMA));

      this.consume(TokenType.GREATER_THAN, "Expected '>' after generic types");
    }

    return {
      name: nameToken.value,
      isArray,
      isOptional,
      genericTypes
    };
  }

  private parseClientBlock(): ClientBlockNode {
    const start = this.getCurrentPosition();
    this.state.context.blockContext = 'client';

    this.consume(TokenType.LEFT_BRACE, "Expected '{' after 'client'");
    this.enterScope();

    const reactiveVariables: ReactiveVariableNode[] = [];
    const computedValues: any[] = [];
    const eventHandlers: any[] = [];
    const functions: FunctionNode[] = [];
    const lifecycle: LifecycleHookNode[] = [];

    while (!this.check(TokenType.RIGHT_BRACE) && !this.isAtEnd()) {
      if (this.check(TokenType.LET) || this.check(TokenType.CONST)) {
        reactiveVariables.push(this.parseReactiveVariable());
      } else if (this.checkLifecycleHook()) {
        lifecycle.push(this.parseLifecycleHook());
      } else if (this.check(TokenType.IDENTIFIER)) {
        functions.push(this.parseFunction());
      } else {
        this.throwError('Expected variable declaration, function, or lifecycle hook',
          ['let', 'const', 'function', 'onMount', 'onUnmount', 'onUpdate']);
      }
    }

    this.exitScope();
    this.consume(TokenType.RIGHT_BRACE, "Expected '}' after client block");

    const end = this.getCurrentPosition();

    return {
      type: 'ClientBlock',
      reactiveVariables,
      computedValues,
      eventHandlers,
      functions,
      lifecycle,
      range: { start, end }
    };
  }

  private parseServerBlock(): ServerBlockNode {
    const start = this.getCurrentPosition();
    this.state.context.blockContext = 'server';

    this.consume(TokenType.LEFT_BRACE, "Expected '{' after 'server'");
    this.enterScope();

    const functions: ServerFunctionNode[] = [];
    const middleware: any[] = [];
    const dataFetchers: any[] = [];
    const imports: any[] = [];

    while (!this.check(TokenType.RIGHT_BRACE) && !this.isAtEnd()) {
      if (this.check(TokenType.PUBLIC) || this.check(TokenType.IDENTIFIER)) {
        functions.push(this.parseServerFunction());
      } else {
        this.throwError('Expected function declaration', ['public', 'function']);
      }
    }

    this.exitScope();
    this.consume(TokenType.RIGHT_BRACE, "Expected '}' after server block");

    const end = this.getCurrentPosition();

    return {
      type: 'ServerBlock',
      functions,
      middleware,
      dataFetchers,
      imports,
      range: { start, end }
    };
  }

  private parseMarkupBlock(): MarkupBlockNode {
    const start = this.getCurrentPosition();
    this.state.context.blockContext = 'markup';

    this.consume(TokenType.LEFT_BRACE, "Expected '{' after 'markup'");

    const elements: HTMLElementNode[] = [];
    const textNodes: TextNode[] = [];
    const interpolations: InterpolationNode[] = [];

    while (!this.check(TokenType.RIGHT_BRACE) && !this.isAtEnd()) {
      if (this.check(TokenType.HTML_TAG_OPEN)) {
        elements.push(this.parseHTMLElement());
      } else if (this.check(TokenType.INTERPOLATION_START)) {
        interpolations.push(this.parseInterpolation());
      } else if (this.check(TokenType.HTML_TEXT)) {
        textNodes.push(this.parseTextNode());
      } else {
        // Skip unknown tokens in markup context
        this.advance();
      }
    }

    this.consume(TokenType.RIGHT_BRACE, "Expected '}' after markup block");

    const end = this.getCurrentPosition();

    return {
      type: 'MarkupBlock',
      elements,
      textNodes,
      interpolations,
      range: { start, end }
    };
  }

  private parseReactiveVariable(): ReactiveVariableNode {
    const start = this.getCurrentPosition();

    const isConst = this.match(TokenType.CONST);
    if (!isConst) {
      this.consume(TokenType.LET, "Expected 'let' or 'const'");
    }

    const nameToken = this.consume(TokenType.IDENTIFIER, 'Expected variable name');

    // Check for duplicate declaration
    if (this.hasSymbolInCurrentScope(nameToken.value)) {
      this.throwError(`Variable '${nameToken.value}' is already declared in this scope`, []);
    }

    let dataType: TypeAnnotation = { name: 'any', isArray: false, isOptional: false, genericTypes: [] };

    if (this.match(TokenType.COLON)) {
      dataType = this.parseTypeAnnotation();
    }

    this.consume(TokenType.ASSIGN, "Expected '=' after variable declaration");

    const initialValue = this.parseExpression();

    this.match(TokenType.SEMICOLON); // Optional semicolon

    // Add variable to symbol table
    this.addSymbol(nameToken.value, 'variable', dataType, start, isConst);

    const end = this.getCurrentPosition();

    return {
      type: 'ReactiveVariable',
      name: nameToken.value,
      initialValue,
      dataType,
      isConst,
      range: { start, end }
    };
  }

  private parseFunction(): FunctionNode {
    const start = this.getCurrentPosition();

    const nameToken = this.consume(TokenType.IDENTIFIER, 'Expected function name');

    // Check for duplicate declaration
    if (this.hasSymbolInCurrentScope(nameToken.value)) {
      this.throwError(`Function '${nameToken.value}' is already declared in this scope`, []);
    }

    this.consume(TokenType.LEFT_PAREN, "Expected '(' after function name");

    this.enterScope();
    const parameters = this.parseParameters();

    this.consume(TokenType.RIGHT_PAREN, "Expected ')' after parameters");
    this.consume(TokenType.COLON, "Expected ':' after parameters");

    const returnType = this.parseTypeAnnotation();

    this.consume(TokenType.LEFT_BRACE, "Expected '{' after function signature");

    const body = this.parseStatements();

    this.consume(TokenType.RIGHT_BRACE, "Expected '}' after function body");
    this.exitScope();

    // Add function to symbol table
    this.addSymbol(nameToken.value, 'function', returnType, start);

    const end = this.getCurrentPosition();

    return {
      type: 'Function',
      name: nameToken.value,
      parameters,
      body,
      returnType,
      isAsync: false,
      range: { start, end }
    };
  }

  private parseServerFunction(): ServerFunctionNode {
    const start = this.getCurrentPosition();

    const isPublic = this.match(TokenType.PUBLIC);

    const nameToken = this.consume(TokenType.IDENTIFIER, 'Expected function name');

    // Check for duplicate declaration
    if (this.hasSymbolInCurrentScope(nameToken.value)) {
      this.throwError(`Function '${nameToken.value}' is already declared in this scope`, []);
    }

    this.consume(TokenType.LEFT_PAREN, "Expected '(' after function name");

    this.enterScope();
    const parameters = this.parseParameters();

    this.consume(TokenType.RIGHT_PAREN, "Expected ')' after parameters");
    this.consume(TokenType.COLON, "Expected ':' after parameters");

    const returnType = this.parseTypeAnnotation();

    this.consume(TokenType.LEFT_BRACE, "Expected '{' after function signature");

    const body = this.parseStatements();

    this.consume(TokenType.RIGHT_BRACE, "Expected '}' after function body");
    this.exitScope();

    // Add function to symbol table
    this.addSymbol(nameToken.value, 'function', returnType, start, false, isPublic);

    const end = this.getCurrentPosition();

    return {
      type: 'ServerFunction',
      name: nameToken.value,
      parameters,
      body,
      returnType,
      isAsync: false,
      isPublic,
      middleware: [],
      permissions: [],
      range: { start, end }
    };
  }

  private parseLifecycleHook(): LifecycleHookNode {
    const start = this.getCurrentPosition();

    const hookName = this.advance().value;

    let hookType: LifecycleType;
    switch (hookName) {
      case 'onMount':
        hookType = LifecycleType.ON_MOUNT;
        break;
      case 'onUnmount':
        hookType = LifecycleType.ON_UNMOUNT;
        break;
      case 'onUpdate':
        hookType = LifecycleType.ON_UPDATE;
        break;
      case 'beforeUpdate':
        hookType = LifecycleType.BEFORE_UPDATE;
        break;
      case 'afterUpdate':
        hookType = LifecycleType.AFTER_UPDATE;
        break;
      default:
        this.throwError(`Unknown lifecycle hook: ${hookName}`,
          ['onMount', 'onUnmount', 'onUpdate', 'beforeUpdate', 'afterUpdate']);
        hookType = LifecycleType.ON_MOUNT;
    }

    this.consume(TokenType.LEFT_PAREN, "Expected '(' after lifecycle hook");
    this.consume(TokenType.RIGHT_PAREN, "Expected ')' after lifecycle hook");
    this.consume(TokenType.LEFT_BRACE, "Expected '{' after lifecycle hook");

    this.enterScope();
    const handler = this.parseStatements();
    this.exitScope();

    this.consume(TokenType.RIGHT_BRACE, "Expected '}' after lifecycle hook body");

    const end = this.getCurrentPosition();

    return {
      type: 'LifecycleHook',
      hookType,
      handler,
      range: { start, end }
    };
  }

  private parseParameters(): ParameterNode[] {
    const parameters: ParameterNode[] = [];

    while (!this.check(TokenType.RIGHT_PAREN) && !this.isAtEnd()) {
      const param = this.parseParameter();
      parameters.push(param);

      // Add parameter to symbol table
      this.addSymbol(param.name, 'parameter', param.dataType, param.range.start);

      if (!this.match(TokenType.COMMA)) {
        break;
      }
    }

    return parameters;
  }

  private parseParameter(): ParameterNode {
    const start = this.getCurrentPosition();

    const nameToken = this.consume(TokenType.IDENTIFIER, 'Expected parameter name');

    this.consume(TokenType.COLON, "Expected ':' after parameter name");

    const dataType = this.parseTypeAnnotation();

    let defaultValue: ExpressionNode | undefined;
    let isOptional = dataType.isOptional;

    if (this.match(TokenType.ASSIGN)) {
      isOptional = true;
      defaultValue = this.parseExpression();
    }

    const end = this.getCurrentPosition();

    return {
      type: 'Parameter',
      name: nameToken.value,
      dataType,
      defaultValue,
      isOptional,
      range: { start, end }
    } as ParameterNode;
  }

  private parseStatements(): StatementNode[] {
    const statements: StatementNode[] = [];

    while (!this.check(TokenType.RIGHT_BRACE) && !this.isAtEnd()) {
      statements.push(this.parseStatement());
    }

    return statements;
  }

  private parseStatement(): StatementNode {
    const start = this.getCurrentPosition();

    // For now, treat everything as expression statements
    const expression = this.parseExpression();

    this.match(TokenType.SEMICOLON); // Optional semicolon

    const end = this.getCurrentPosition();

    return {
      type: 'Statement',
      statementType: StatementType.EXPRESSION,
      expression,
      range: { start, end }
    };
  }

  private parseExpression(): ExpressionNode {
    return this.parsePrecedence(Precedence.ASSIGNMENT);
  }

  private parsePrecedence(precedence: Precedence): ExpressionNode {
    const prefixRule = this.parseRules.get(this.peek().type)?.prefix;
    if (!prefixRule) {
      this.throwError('Expected expression', ['identifier', 'number', 'string', 'boolean', '(']);
      // Return dummy expression for error recovery
      return this.createDummyExpression();
    }

    let left = prefixRule(this);

    while (precedence <= this.getPrecedence()) {
      const infixRule = this.parseRules.get(this.peek().type)?.infix;
      if (!infixRule) break;

      left = infixRule(this, left);
    }

    return left;
  }

  private getPrecedence(): Precedence {
    const rule = this.parseRules.get(this.peek().type);
    return rule?.precedence || Precedence.NONE;
  }

  // Expression parsing methods
  private parseLiteral(): ExpressionNode {
    const token = this.previous();
    let value: any = token.value;

    if (token.type === TokenType.BOOLEAN) {
      value = token.value === 'true';
    } else if (token.type === TokenType.NUMBER) {
      value = parseFloat(token.value);
    }

    return {
      type: 'Expression',
      expressionType: ExpressionType.LITERAL,
      value,
      range: token.range
    };
  }

  private parseIdentifier(): ExpressionNode {
    const token = this.previous();

    // Check if identifier is declared
    if (this.options.strictMode && !this.hasSymbol(token.value)) {
      this.addWarning(`Undefined variable: '${token.value}'`, token.range.start);
    }

    return {
      type: 'Expression',
      expressionType: ExpressionType.IDENTIFIER,
      identifier: token.value,
      range: token.range
    };
  }

  private parseGrouping(): ExpressionNode {
    const expr = this.parseExpression();
    this.consume(TokenType.RIGHT_PAREN, "Expected ')' after expression");
    return expr;
  }

  private parseUnary(): ExpressionNode {
    const operator = this.previous();
    const right = this.parsePrecedence(Precedence.UNARY);

    return {
      type: 'Expression',
      expressionType: ExpressionType.UNARY,
      operator: operator.value,
      right,
      range: { start: operator.range.start, end: right.range.end }
    };
  }

  private parseBinary(parser: OrdoJSParser, left: ExpressionNode): ExpressionNode {
    const operator = this.previous();
    const rule = this.parseRules.get(operator.type);
    const right = this.parsePrecedence((rule?.precedence || Precedence.NONE) + 1);

    return {
      type: 'Expression',
      expressionType: ExpressionType.BINARY,
      operator: operator.value,
      left,
      right,
      range: { start: left.range.start, end: right.range.end }
    };
  }

  private parseAssignment(parser: OrdoJSParser, left: ExpressionNode): ExpressionNode {
    const operator = this.previous();
    const right = this.parseExpression();

    return {
      type: 'Expression',
      expressionType: ExpressionType.ASSIGNMENT,
      operator: operator.value,
      left,
      right,
      range: { start: left.range.start, end: right.range.end }
    };
  }

  private parseCall(parser: OrdoJSParser, left: ExpressionNode): ExpressionNode {
    const args: ExpressionNode[] = [];

    if (!this.check(TokenType.RIGHT_PAREN)) {
      do {
        args.push(this.parseExpression());
      } while (this.match(TokenType.COMMA));
    }

    const paren = this.consume(TokenType.RIGHT_PAREN, "Expected ')' after arguments");

    return {
      type: 'Expression',
      expressionType: ExpressionType.CALL,
      callee: left,
      arguments: args,
      range: { start: left.range.start, end: paren.range.end }
    };
  }

  private parseMember(parser: OrdoJSParser, left: ExpressionNode): ExpressionNode {
    const name = this.consume(TokenType.IDENTIFIER, "Expected property name after '.'");

    return {
      type: 'Expression',
      expressionType: ExpressionType.MEMBER,
      object: left,
      property: {
        type: 'Expression',
        expressionType: ExpressionType.IDENTIFIER,
        identifier: name.value,
        range: name.range
      },
      range: { start: left.range.start, end: name.range.end }
    };
  }

  private parseHTMLElement(): HTMLElementNode {
    const start = this.getCurrentPosition();

    // Simplified HTML parsing - would need much more sophisticated implementation
    const tagName = 'div'; // Placeholder
    const attributes: AttributeNode[] = [];
    const children: (HTMLElementNode | TextNode | InterpolationNode)[] = [];

    const end = this.getCurrentPosition();

    return {
      type: 'HTMLElement',
      tagName,
      attributes,
      children,
      isSelfClosing: false,
      isVoidElement: false,
      range: { start, end }
    };
  }

  private parseInterpolation(): InterpolationNode {
    const start = this.getCurrentPosition();

    this.consume(TokenType.INTERPOLATION_START, "Expected '{' for interpolation");

    const expression = this.parseExpression();

    this.consume(TokenType.INTERPOLATION_END, "Expected '}' after interpolation");

    const end = this.getCurrentPosition();

    return {
      type: 'Interpolation',
      expression,
      range: { start, end }
    };
  }

  private parseTextNode(): TextNode {
    const start = this.getCurrentPosition();

    const content = this.consume(TokenType.HTML_TEXT, 'Expected text content').value;

    const end = this.getCurrentPosition();

    return {
      type: 'Text',
      content,
      range: { start, end }
    };
  }

  // Symbol table management
  private enterScope(): void {
    this.state.context.scopeDepth++;
  }

  private exitScope(): void {
    // Remove symbols from current scope
    for (const [name, symbol] of this.state.context.symbolTable) {
      if (symbol.scope === this.state.context.scopeDepth) {
        this.state.context.symbolTable.delete(name);
      }
    }
    this.state.context.scopeDepth--;
  }

  private addSymbol(
    name: string,
    type: SymbolInfo['type'],
    dataType: TypeAnnotation | undefined,
    position: SourcePosition,
    isConst: boolean = false,
    isPublic: boolean = false
  ): void {
    this.state.context.symbolTable.set(name, {
      name,
      type,
      dataType,
      scope: this.state.context.scopeDepth,
      position,
      isConst,
      isPublic
    });
  }

  private hasSymbol(name: string): boolean {
    return this.state.context.symbolTable.has(name);
  }

  private hasSymbolInCurrentScope(name: string): boolean {
    const symbol = this.state.context.symbolTable.get(name);
    return symbol?.scope === this.state.context.scopeDepth;
  }

  private getSymbol(name: string): SymbolInfo | undefined {
    return this.state.context.symbolTable.get(name);
  }

  // AST processing
  private processAST(component: ComponentNode): void {
    if (this.options.nodeProcessors.length === 0) return;

    this.processNode(component);
  }

  private processNode(node: any): void {
    for (const processor of this.options.nodeProcessors) {
      if (processor.handles.includes(node.type)) {
        node = processor.process(node, this.state.context);
      }
    }

    // Recursively process child nodes
    if (node.children) {
      for (const child of node.children) {
        this.processNode(child);
      }
    }

    // Process specific node types
    if (node.clientBlock) this.processNode(node.clientBlock);
    if (node.serverBlock) this.processNode(node.serverBlock);
    if (node.markupBlock) this.processNode(node.markupBlock);
    if (node.functions) {
      for (const func of node.functions) {
        this.processNode(func);
      }
    }
    if (node.reactiveVariables) {
      for (const variable of node.reactiveVariables) {
        this.processNode(variable);
      }
    }
  }

  // Utility methods
  private checkLifecycleHook(): boolean {
    const value = this.peek().value;
    return ['onMount', 'onUnmount', 'onUpdate', 'beforeUpdate', 'afterUpdate'].includes(value);
  }

  private isValidComponentName(name: string): boolean {
    return /^[A-Z][a-zA-Z0-9]*$/.test(name);
  }

  private createDummyExpression(): ExpressionNode {
    return {
      type: 'Expression',
      expressionType: ExpressionType.LITERAL,
      value: null,
      range: { start: this.getCurrentPosition(), end: this.getCurrentPosition() }
    };
  }

  private extractDependencies(component: ComponentNode): string[] {
    // Extract import dependencies from the component
    const dependencies: string[] = [];
    // Implementation would analyze the AST for imports
    return dependencies;
  }

  private extractExports(component: ComponentNode): string[] {
    // Extract exports from the component
    const exports: string[] = [component.name];
    // Implementation would analyze the AST for exports
    return exports;
  }

  private generateSourceMap(): any {
    // Generate source map for debugging
    return {
      version: 3,
      sources: [this.state.filename],
      names: [],
      mappings: '',
      sourcesContent: []
    };
  }

  // Token stream utilities
  private match(...types: TokenType[]): boolean {
    for (const type of types) {
      if (this.check(type)) {
        this.advance();
        return true;
      }
    }
    return false;
  }

  private check(type: TokenType): boolean {
    if (this.isAtEnd()) return false;
    return this.peek().type === type;
  }

  private advance(): Token {
    if (!this.isAtEnd()) this.state.current++;
    return this.previous();
  }

  private isAtEnd(): boolean {
    return this.peek().type === TokenType.EOF;
  }

  private peek(): Token {
    return this.state.tokenStream.tokens[this.state.current] || this.createEOFToken();
  }

  private previous(): Token {
    return this.state.tokenStream.tokens[this.state.current - 1] || this.createEOFToken();
  }

  private consume(type: TokenType, message: string): Token {
    if (this.check(type)) return this.advance();

    this.throwError(message, [type.toString()]);

    // Return dummy token for error recovery
    return this.createEOFToken();
  }

  private createEOFToken(): Token {
    return {
      type: TokenType.EOF,
      value: '',
      position: this.getCurrentPosition(),
      range: { start: this.getCurrentPosition(), end: this.getCurrentPosition() }
    };
  }

  private throwError(message: string, expected: string[]): never {
    const error = new SyntaxError(
      message,
      this.getCurrentPosition(),
      expected,
      this.peek().value,
      this.state.filename
    );

    if (this.options.allowRecovery && this.state.errors.length < this.options.maxErrors) {
      this.state.errors.push(error);
      this.synchronize();
      throw error;
    }

    throw error;
  }

  private addWarning(message: string, position: SourcePosition): void {
    const warning = new SyntaxError(
      message,
      position,
      [],
      '',
      this.state.filename
    );
    this.state.warnings.push(warning);
  }

  private synchronize(): void {
    this.advance();

    while (!this.isAtEnd()) {
      if (this.previous().type === TokenType.SEMICOLON) return;

      switch (this.peek().type) {
        case TokenType.COMPONENT:
        case TokenType.CLIENT:
        case TokenType.SERVER:
        case TokenType.MARKUP:
        case TokenType.LET:
        case TokenType.CONST:
          return;
      }

      this.advance();
    }
  }

  private getCurrentPosition(): SourcePosition {
    return this.peek().position;
  }
}

/**
 * Default AST node processors
 */
export const defaultNodeProcessors: ASTNodeProcessor[] = [
  {
    name: 'symbol-validator',
    handles: ['Expression'],
    process: <T extends { type: string }>(node: T, context: ParserContext): T => {
      if (node.type === 'Expression') {
        const expr = node as any as ExpressionNode;
        if (expr.expressionType === ExpressionType.IDENTIFIER && expr.identifier) {
          if (!context.symbolTable.has(expr.identifier)) {
            // Could emit warning about undefined variable
          }
        }
      }
      return node;
    }
  },
  {
    name: 'type-checker',
    handles: ['ReactiveVariable', 'Function', 'ServerFunction'],
    process: (node: any, context: ParserContext): any => {
      // Could perform type checking here
      return node;
    }
  }
];
