import { GBVMService } from './GBVMService';
import path from 'path';

interface Token {
  type: string;
  value: string;
  line: number;
  column: number;
}

interface ASTNode {
  type: string;
  value?: any;
  children?: ASTNode[];
  line: number;
  column: number;
}

export class GBCompiler {
  private source: string = '';
  private tokens: Token[] = [];
  private currentToken = 0;
  private line = 1;
  private column = 1;
  private output: string[] = [];
  private inString = false;
  private stringChar = '';
  private buffer = '';
  private lineMap: {[key: number]: number} = {};
  private tasks: any[] = [];

  // Internal state tracking
  private inTalkBlock = false;
  private talkBuffer = '';
  private inSystemPrompt = false; 
  private systemPromptBuffer = '';
  private currentTable: any = null;

  constructor() {
    // Initialize state
  }

  public compile(source: string, options: {
    filename: string;
    mainName: string;
    pid: string;
  }): {
    code: string;
    map: {[key: number]: number};
    metadata: any;
    tasks: any[];
    systemPrompt: string;
  } {
    this.source = source;
    this.reset();

    // Compilation phases
    this.tokenize();
    const ast = this.parse(); 
    this.generateCode(ast);

    return {
      code: this.output.join('\n'),
      map: this.lineMap,
      metadata: this.generateMetadata(options.mainName),
      tasks: this.tasks,
      systemPrompt: this.systemPromptBuffer
    };
  }

  private reset() {
    this.tokens = [];
    this.currentToken = 0;
    this.line = 1;
    this.column = 1;
    this.output = [];
    this.lineMap = {};
    this.tasks = [];
    this.inTalkBlock = false;
    this.talkBuffer = '';
    this.inSystemPrompt = false;
    this.systemPromptBuffer = '';
    this.currentTable = null;
  }

  private tokenize() {
    let current = 0;
    const source = this.source;

    while (current < source.length) {
      let char = source[current];

      // Handle whitespace
      if (/\s/.test(char)) {
        if (char === '\n') {
          this.line++;
          this.column = 1;
          
          if (this.buffer) {
            this.addToken('WORD', this.buffer);
            this.buffer = '';
          }
          
          this.addToken('NEWLINE', '\n');
        }
        current++;
        this.column++;
        continue;
      }

      // Handle strings
      if (char === '"' || char === "'" || char === '`') {
        if (this.buffer) {
          this.addToken('WORD', this.buffer);
          this.buffer = '';
        }

        const stringValue = this.readString(source, current, char);
        this.addToken('STRING', stringValue.value);
        current = stringValue.position;
        continue;
      }

      // Handle operators
      if ('=()[]{}+-*/%<>!&|,'.includes(char)) {
        if (this.buffer) {
          this.addToken('WORD', this.buffer);
          this.buffer = '';
        }

        const operator = this.readOperator(source, current);
        this.addToken('OPERATOR', operator.value);
        current = operator.position;
        continue;
      }

      // Build up word buffer
      this.buffer += char;
      current++;
      this.column++;
    }

    // Add any remaining buffer
    if (this.buffer) {
      this.addToken('WORD', this.buffer);
    }

    this.addToken('EOF', '');
  }

  private readString(source: string, start: number, quote: string) {
    let value = '';
    let position = start + 1;
    let escaped = false;

    while (position < source.length) {
      const char = source[position];
      
      if (escaped) {
        value += char;
        escaped = false;
      } else if (char === '\\') {
        escaped = true;
      } else if (char === quote) {
        position++;
        break;
      } else {
        value += char;
      }
      
      position++;
    }

    return { value, position };
  }

  private readOperator(source: string, start: number) {
    const twoCharOperators = ['==', '!=', '>=', '<=', '&&', '||'];
    const oneChar = source[start];
    const twoChar = oneChar + (source[start + 1] || '');

    if (twoCharOperators.includes(twoChar)) {
      return {
        value: twoChar,
        position: start + 2
      };
    }

    return {
      value: oneChar,
      position: start + 1
    };
  }

  private addToken(type: string, value: string) {
    this.tokens.push({
      type,
      value,
      line: this.line,
      column: this.column
    });
  }

  private parse(): ASTNode {
    const program: ASTNode = {
      type: 'Program',
      children: [],
      line: 1,
      column: 1
    };

    while (this.currentToken < this.tokens.length) {
      const statement = this.parseStatement();
      if (statement) {
        program.children.push(statement);
      }
    }

    return program;
  }

  private parseWhileStatement(): ASTNode {
    this.currentToken++; // Skip DO
    
    if (this.peek().value.toUpperCase() !== 'WHILE') {
      throw new Error('Expected WHILE after DO');
    }
    this.currentToken++; // Skip WHILE

    const condition = this.parseCondition();
    const body = this.parseBlock();

    // Must end with LOOP
    if (this.peek().value.toUpperCase() !== 'LOOP') {
      throw new Error('Expected LOOP at end of while statement');
    }
    this.currentToken++;

    return {
      type: 'WhileStatement',
      value: {
        condition,
        body
      },
      line: this.tokens[this.currentToken].line,
      column: this.tokens[this.currentToken].column
    };
  }

  private parseExpression(): ASTNode {
    return this.parseAssignment();
  }

  private parseAssignment(): ASTNode {
    const left = this.parseLogicalOr();

    if (this.peek().value === '=') {
      this.currentToken++;
      const right = this.parseAssignment();
      return {
        type: 'AssignmentExpression',
        value: { left, right },
        line: this.tokens[this.currentToken].line,
        column: this.tokens[this.currentToken].column
      };
    }

    return left;
  }

  private parseLogicalOr(): ASTNode {
    let left = this.parseLogicalAnd();

    while (
      this.peek().value.toUpperCase() === 'OR' || 
      this.peek().value === '||'
    ) {
      const operator = this.tokens[this.currentToken].value;
      this.currentToken++;
      const right = this.parseLogicalAnd();
      left = {
        type: 'LogicalExpression',
        value: { operator: '||', left, right },
        line: this.tokens[this.currentToken].line,
        column: this.tokens[this.currentToken].column
      };
    }

    return left;
  }

  private parseLogicalAnd(): ASTNode {
    let left = this.parseEquality();

    while (
      this.peek().value.toUpperCase() === 'AND' || 
      this.peek().value === '&&'
    ) {
      const operator = this.tokens[this.currentToken].value;
      this.currentToken++;
      const right = this.parseEquality();
      left = {
        type: 'LogicalExpression',
        value: { operator: '&&', left, right },
        line: this.tokens[this.currentToken].line,
        column: this.tokens[this.currentToken].column
      };
    }

    return left;
  }

  private parseEquality(): ASTNode {
    let left = this.parseRelational();

    while (
      this.peek().value === '=' || 
      this.peek().value === '<>' ||
      this.peek().value === '==' ||
      this.peek().value === '!='
    ) {
      let operator = this.tokens[this.currentToken].value;
      this.currentToken++;
      
      // Convert BASIC operators to JS
      if (operator === '=') operator = '===';
      if (operator === '<>') operator = '!==';
      
      const right = this.parseRelational();
      left = {
        type: 'BinaryExpression',
        value: { operator, left, right },
        line: this.tokens[this.currentToken].line,
        column: this.tokens[this.currentToken].column
      };
    }

    return left;
  }

  private parseRelational(): ASTNode {
    let left = this.parseAdditive();

    while (
      this.peek().value === '<' ||
      this.peek().value === '>' ||
      this.peek().value === '<=' ||
      this.peek().value === '>='
    ) {
      const operator = this.tokens[this.currentToken].value;
      this.currentToken++;
      const right = this.parseAdditive();
      left = {
        type: 'BinaryExpression',
        value: { operator, left, right },
        line: this.tokens[this.currentToken].line,
        column: this.tokens[this.currentToken].column
      };
    }

    return left;
  }

  private parseAdditive(): ASTNode {
    let left = this.parseMultiplicative();

    while (
      this.peek().value === '+' ||
      this.peek().value === '-'
    ) {
      const operator = this.tokens[this.currentToken].value;
      this.currentToken++;
      const right = this.parseMultiplicative();
      left = {
        type: 'BinaryExpression',
        value: { operator, left, right },
        line: this.tokens[this.currentToken].line,
        column: this.tokens[this.currentToken].column
      };
    }

    return left;
  }

  private parseMultiplicative(): ASTNode {
    let left = this.parseUnary();

    while (
      this.peek().value === '*' ||
      this.peek().value === '/' ||
      this.peek().value === '%'
    ) {
      const operator = this.tokens[this.currentToken].value;
      this.currentToken++;
      const right = this.parseUnary();
      left = {
        type: 'BinaryExpression',
        value: { operator, left, right },
        line: this.tokens[this.currentToken].line,
        column: this.tokens[this.currentToken].column
      };
    }

    return left;
  }

  private parseUnary(): ASTNode {
    if (
      this.peek().value === '-' ||
      this.peek().value === '!' ||
      this.peek().value.toUpperCase() === 'NOT'
    ) {
      const operator = this.tokens[this.currentToken].value;
      this.currentToken++;
      const argument = this.parseUnary();
      return {
        type: 'UnaryExpression',
        value: { 
          operator: operator.toUpperCase() === 'NOT' ? '!' : operator, 
          argument 
        },
        line: this.tokens[this.currentToken].line,
        column: this.tokens[this.currentToken].column
      };
    }

    return this.parsePrimary();
  }

  private parsePrimary(): ASTNode {
    const token = this.peek();

    switch (token.type) {
      case 'NUMBER':
        this.currentToken++;
        return {
          type: 'NumberLiteral',
          value: parseFloat(token.value),
          line: token.line,
          column: token.column
        };

      case 'STRING':
        this.currentToken++;
        return {
          type: 'StringLiteral',
          value: token.value,
          line: token.line,
          column: token.column
        };

      case 'WORD':
        // Check for special keywords
        switch (token.value.toUpperCase()) {
          case 'TRUE':
          case 'FALSE':
            this.currentToken++;
            return {
              type: 'BooleanLiteral',
              value: token.value.toUpperCase() === 'TRUE',
              line: token.line,
              column: token.column
            };

          case 'NULL':
            this.currentToken++;
            return {
              type: 'NullLiteral',
              value: null,
              line: token.line,
              column: token.column
            };

          default:
            // Check if it's a function call
            if (this.tokens[this.currentToken + 1]?.value === '(') {
              return this.parseFunctionCall();
            }
            // Otherwise it's an identifier
            this.currentToken++;
            return {
              type: 'Identifier',
              value: token.value,
              line: token.line,
              column: token.column
            };
        }

      case 'LEFT_PAREN':
        this.currentToken++; // Skip (
        const expr = this.parseExpression();
        if (this.peek().value !== ')') {
          throw new Error('Expected closing parenthesis');
        }
        this.currentToken++; // Skip )
        return expr;

      default:
        throw new Error(
          `Unexpected token ${token.type} "${token.value}" at line ${token.line} column ${token.column}`
        );
    }
  }

  private parseFunctionCall(): ASTNode {
    const identifier = this.parseIdentifier();
    
    if (this.peek().value !== '(') {
      throw new Error('Expected ( after function name');
    }
    this.currentToken++; // Skip (

    const args = [];
    while (this.peek().value !== ')') {
      args.push(this.parseExpression());
      
      if (this.peek().value === ',') {
        this.currentToken++;
      } else if (this.peek().value !== ')') {
        throw new Error('Expected , or ) in function call');
      }
    }
    this.currentToken++; // Skip )

    return {
      type: 'FunctionCall',
      value: {
        callee: identifier,
        arguments: args
      },
      line: this.tokens[this.currentToken].line,
      column: this.tokens[this.currentToken].column
    };
  }

  private parseForStatement(): ASTNode {
    this.currentToken++; // Skip FOR
    
    const variable = this.parseIdentifier();
    
    if (this.peek().value !== '=') {
      throw new Error('Expected = in FOR loop initialization');
    }
    this.currentToken++;
    
    const start = this.parseExpression();
    
    if (this.peek().value.toUpperCase() !== 'TO') {
      throw new Error('Expected TO in FOR loop');
    }
    this.currentToken++;
    
    const end = this.parseExpression();
    let step = null;

    // Optional STEP
    if (this.peek().value.toUpperCase() === 'STEP') {
      this.currentToken++;
      step = this.parseExpression();
    }

    const body = this.parseBlock();

    // Must end with NEXT
    if (this.peek().value.toUpperCase() !== 'NEXT') {
      throw new Error('Expected NEXT at end of FOR loop');
    }
    this.currentToken++;

    return {
      type: 'ForStatement',
      value: {
        variable,
        start,
        end,
        step,
        body
      },
      line: this.tokens[this.currentToken].line,
      column: this.tokens[this.currentToken].column
    };
  }

  private parseForEachStatement(): ASTNode {
    this.currentToken++; // Skip FOR
    
    if (this.peek().value.toUpperCase() !== 'EACH') {
      throw new Error('Expected EACH after FOR');
    }
    this.currentToken++;

    const variable = this.parseIdentifier();
    
    if (this.peek().value.toUpperCase() !== 'IN') {
      throw new Error('Expected IN in FOR EACH statement');
    }
    this.currentToken++;

    const collection = this.parseExpression();
    const body = this.parseBlock();

    // Must end with NEXT
    if (this.peek().value.toUpperCase() !== 'NEXT') {
      throw new Error('Expected NEXT at end of FOR EACH loop');
    }
    this.currentToken++;

    return {
      type: 'ForEachStatement', 
      value: {
        variable,
        collection,
        body
      },
      line: this.tokens[this.currentToken].line,
      column: this.tokens[this.currentToken].column
    };
  }

  private parseFunctionStatement(): ASTNode {
    this.currentToken++; // Skip FUNCTION
    
    const name = this.parseIdentifier();
    
    if (this.peek().value !== '(') {
      throw new Error('Expected ( after function name');
    }
    this.currentToken++;
    
    const params = this.parseParameterList();
    
    if (this.peek().value !== ')') {
      throw new Error('Expected ) after parameter list');
    }
    this.currentToken++;

    const body = this.parseBlock();

    // Must end with END FUNCTION
    if (this.peek().value.toUpperCase() !== 'END' || 
        this.tokens[this.currentToken + 1].value.toUpperCase() !== 'FUNCTION') {
      throw new Error('Expected END FUNCTION');
    }
    this.currentToken += 2;

    return {
      type: 'FunctionDeclaration',
      value: {
        name,
        params,
        body
      },
      line: this.tokens[this.currentToken].line,
      column: this.tokens[this.currentToken].column
    };
  }

  private parseSelectStatement(): ASTNode {
    this.currentToken++; // Skip SELECT
    
    const fields = this.parseSelectList();
    
    if (this.peek().value.toUpperCase() !== 'FROM') {
      throw new Error('Expected FROM in SELECT statement');
    }
    this.currentToken++;

    const tableName = this.parseIdentifier();
    let whereClause = null;

    // Optional WHERE clause
    if (this.peek().value.toUpperCase() === 'WHERE') {
      this.currentToken++;
      whereClause = this.parseExpression();
    }

    return {
      type: 'SelectStatement',
      value: {
        fields,
        tableName,
        whereClause
      },
      line: this.tokens[this.currentToken].line,
      column: this.tokens[this.currentToken].column
    };
  }

  private parseChartStatement(): ASTNode {
    this.currentToken++; // Skip CHART
    
    const type = this.parseExpression();
    
    if (this.peek().value !== ',') {
      throw new Error('Expected , after chart type');
    }
    this.currentToken++;

    const data = this.parseExpression();
    let legends = null;
    let transpose = null;
    let prompt = null;

    // Check for additional parameters
    while (this.peek().value === ',') {
      this.currentToken++;
      
      if (this.peek().value.toUpperCase() === 'LEGENDS') {
        this.currentToken++;
        legends = this.parseExpression();
      } else if (this.peek().value.toUpperCase() === 'TRANSPOSE') {
        this.currentToken++;
        transpose = this.parseExpression();
      } else if (this.peek().value.toUpperCase() === 'PROMPT') {
        this.currentToken++;
        prompt = this.parseExpression();
      }
    }

    return {
      type: 'ChartStatement',
      value: {
        type,
        data,
        legends,
        transpose,
        prompt
      },
      line: this.tokens[this.currentToken].line,
      column: this.tokens[this.currentToken].column
    };
  }

  private parseHearStatement(): ASTNode {
    this.currentToken++; // Skip HEAR
    
    const variable = this.parseIdentifier();
    let kind = null;
    let args = null;

    // Check for AS clause
    if (this.peek().value.toUpperCase() === 'AS') {
      this.currentToken++;
      kind = this.parseIdentifier();
      
      // Optional arguments
      if (this.peek().value === ',') {
        this.currentToken++;
        args = this.parseExpressionList();
      }
    }

    return {
      type: 'HearStatement',
      value: {
        variable,
        kind,
        args
      },
      line: this.tokens[this.currentToken].line,
      column: this.tokens[this.currentToken].column  
    };
  }

  private parseTalkStatement(): ASTNode {
    this.currentToken++; // Skip TALK
    
    const text = this.parseExpression();

    return {
      type: 'TalkStatement',
      value: text,
      line: this.tokens[this.currentToken].line, 
      column: this.tokens[this.currentToken].column
    };
  }

  // Helper parsers
  private parseIdentifier(): {value: string, line: number, column: number} {
    const token = this.tokens[this.currentToken];
    if (token.type !== 'WORD') {
      throw new Error(`Expected identifier but got ${token.type}`);
    }
    this.currentToken++;
    return {
      value: token.value,
      line: token.line,
      column: token.column
    };
  }

  private parseExpressionList(): ASTNode[] {
    const expressions = [];
    
    do {
      expressions.push(this.parseExpression());
      
      if (this.peek().value !== ',') {
        break;
      }
      this.currentToken++;
    } while (true);

    return expressions;
  }

  private parseParameterList(): string[] {
    const params = [];
    
    while (this.peek().value !== ')') {
      params.push(this.parseIdentifier().value);
      
      if (this.peek().value !== ',') {
        break;
      }
      this.currentToken++;
    }

    return params;
  }

  private parseSelectList(): string[] {
    const fields = [];
    
    do {
      fields.push(this.parseIdentifier().value);
      
      if (this.peek().value !== ',') {
        break;
      }
      this.currentToken++; 
    } while (true);

    return fields;
  }
  private parseTalkBlock(): ASTNode {
    // Check if we're starting a talk block
    if (!this.inTalkBlock && this.peek().value.toUpperCase() === 'BEGIN' && 
        this.tokens[this.currentToken + 1]?.value.toUpperCase() === 'TALK') {
      this.currentToken += 2; // Skip BEGIN TALK
      this.inTalkBlock = true;
      this.talkBuffer = '';
      return null;
    }

    // Check if we're ending a talk block
    if (this.inTalkBlock && this.peek().value.toUpperCase() === 'END' && 
        this.tokens[this.currentToken + 1]?.value.toUpperCase() === 'TALK') {
      this.currentToken += 2; // Skip END TALK
      this.inTalkBlock = false;
      
      return {
        type: 'TalkBlock',
        value: this.talkBuffer.trim(),
        line: this.tokens[this.currentToken].line,
        column: this.tokens[this.currentToken].column
      };
    }

    // If we're inside a talk block, accumulate text
    if (this.inTalkBlock) {
      const line = this.tokens[this.currentToken].value;
      this.talkBuffer += line + '\n';
      this.currentToken++;
      return null;
    }

    throw new Error(
      `Unexpected token in TALK block at line ${this.tokens[this.currentToken].line}`
    );
  }

  private parseSystemPromptBlock(): ASTNode {
    // Check if we're starting a system prompt block
    if (!this.inSystemPrompt && this.peek().value.toUpperCase() === 'BEGIN' && 
        this.tokens[this.currentToken + 1]?.value.toUpperCase() === 'SYSTEM' &&
        this.tokens[this.currentToken + 2]?.value.toUpperCase() === 'PROMPT') {
      this.currentToken += 3; // Skip BEGIN SYSTEM PROMPT
      this.inSystemPrompt = true;
      this.systemPromptBuffer = '';
      return null;
    }

    // Check if we're ending a system prompt block
    if (this.inSystemPrompt && this.peek().value.toUpperCase() === 'END' && 
        this.tokens[this.currentToken + 1]?.value.toUpperCase() === 'SYSTEM' &&
        this.tokens[this.currentToken + 2]?.value.toUpperCase() === 'PROMPT') {
      this.currentToken += 3; // Skip END SYSTEM PROMPT
      this.inSystemPrompt = false;
      
      return {
        type: 'SystemPromptBlock',
        value: this.systemPromptBuffer.trim(),
        line: this.tokens[this.currentToken].line,
        column: this.tokens[this.currentToken].column
      };
    }

    // If we're inside a system prompt block, accumulate text
    if (this.inSystemPrompt) {
      const line = this.tokens[this.currentToken].value;
      this.systemPromptBuffer += line + '\n';
      this.currentToken++;
      return null;
    }

    throw new Error(
      `Unexpected token in SYSTEM PROMPT block at line ${this.tokens[this.currentToken].line}`
    );
  }

  private parseTableDefinition(): ASTNode {
    // Check if we're starting a table definition
    if (!this.currentTable && this.peek().value.toUpperCase() === 'TABLE') {
      this.currentToken++; // Skip TABLE
      
      const tableName = this.parseIdentifier();
      
      if (this.peek().value.toUpperCase() !== 'ON') {
        throw new Error('Expected ON after table name');
      }
      this.currentToken++; // Skip ON
      
      const connection = this.parseIdentifier();
      
      this.currentTable = {
        name: tableName.value,
        connection: connection.value,
        fields: {}
      };
      
      return null;
    }

    // Check if we're ending a table definition
    if (this.currentTable && this.peek().value.toUpperCase() === 'END' &&
        this.tokens[this.currentToken + 1]?.value.toUpperCase() === 'TABLE') {
      this.currentToken += 2; // Skip END TABLE
      
      const table = this.currentTable;
      this.currentTable = null;
      
      return {
        type: 'TableDefinition',
        value: table,
        line: this.tokens[this.currentToken].line,
        column: this.tokens[this.currentToken].column
      };
    }

    // If we're inside a table definition, parse field definition
    if (this.currentTable) {
      const field = this.parseFieldDefinition();
      if (field) {
        this.currentTable.fields[field.name] = field.definition;
      }
      this.currentToken++;
      return null;
    }

    throw new Error(
      `Unexpected token in TABLE definition at line ${this.tokens[this.currentToken].line}`
    );
  }

  private parseFieldDefinition(): { name: string; definition: any } | null {
    const token = this.peek();
    
    // Skip empty lines or comments
    if (token.type === 'NEWLINE' || 
        token.value.startsWith("'") || 
        token.value.toUpperCase().startsWith('REM')) {
      return null;
    }

    const name = this.parseIdentifier();
    
    if (this.peek().value.toUpperCase() !== 'AS') {
      throw new Error('Expected AS in field definition');
    }
    this.currentToken++; // Skip AS

    const type = this.parseIdentifier();
    let length = null;
    let precision = null;
    let scale = null;

    // Check for size specification
    if (this.peek().value === '(') {
      this.currentToken++; // Skip (
      length = this.parseNumber();
      
      // Check for decimal precision
      if (this.peek().value === ',') {
        this.currentToken++; // Skip ,
        precision = length;
        scale = this.parseNumber();
        length = null;
      }
      
      if (this.peek().value !== ')') {
        throw new Error('Expected ) in field size specification');
      }
      this.currentToken++; // Skip )
    }

    return {
      name: name.value,
      definition: {
        type: type.value,
        length,
        precision,
        scale
      }
    };
  }

  private parseNumber(): number {
    const token = this.peek();
    if (token.type !== 'NUMBER') {
      throw new Error('Expected number');
    }
    this.currentToken++;
    return parseFloat(token.value);
  }
  
  private parseStatement(): ASTNode {
    const token = this.tokens[this.currentToken];

    // Skip empty lines
    if (token.type === 'NEWLINE') {
      this.currentToken++;
      return null;
    }

    // Handle special blocks first
    if (this.inTalkBlock) {
      return this.parseTalkBlock();
    }

    if (this.inSystemPrompt) {
      return this.parseSystemPromptBlock();
    }

    if (this.currentTable) {
      return this.parseTableDefinition();
    }

    // Match tokens to specific statement types
    switch (token.value.toUpperCase()) {
      case 'INPUT':
        return this.parseInputStatement();
      
      case 'PRINT':
        return this.parsePrintStatement();
      
      case 'WRITE':
        return this.parseWriteStatement();
      
      case 'REM':
        return this.parseRemStatement();
      
      case 'CLOSE':
        return this.parseCloseStatement();
      
      case 'OPEN':
        return this.parseOpenStatement();
      
      case 'SELECT':
        return this.parseSelectStatement();
      
      case 'IF':
        return this.parseIfStatement();
      
      case 'FUNCTION':
        return this.parseFunctionStatement();
      
      case 'FOR':
        return this.parseForStatement();
      
      case 'DO':
        return this.parseDoWhileStatement();
      
      case 'WHILE':
        return this.parseWhileStatement();
      
      case 'TALK':
        return this.parseTalkStatement();
      
      case 'HEAR':
        return this.parseHearStatement();

      // Handle assignment statements
      default:
        if (this.isAssignment()) {
          return this.parseAssignmentStatement();
        }
        
        throw new Error(`Unexpected token ${token.value} at line ${token.line}`);
    }
  }

  private parseInputStatement(): ASTNode {
    this.currentToken++; // Skip INPUT
    
    const prompt = this.parseExpression();
    let variable = null;

    if (this.peek().value === ',') {
      this.currentToken++; // Skip comma
      variable = this.parseIdentifier();
    }

    return {
      type: 'InputStatement',
      value: {
        prompt,
        variable
      },
      line: this.tokens[this.currentToken].line,
      column: this.tokens[this.currentToken].column
    };
  }

  private parsePrintStatement(): ASTNode {
    this.currentToken++; // Skip PRINT
    
    const expression = this.parseExpression();
    let sessionName = null;

    // Handle special #file printing
    if (expression.value && expression.value.toString().startsWith('#')) {
      sessionName = expression.value.substr(1, expression.value.indexOf(',') - 1);
      const items = expression.value.substr(expression.value.indexOf(',') + 1)
        .split(/[,;]/)
        .map(item => item.trim());

      return {
        type: 'FilePrintStatement',
        value: {
          sessionName,
          items
        },
        line: this.tokens[this.currentToken].line,
        column: this.tokens[this.currentToken].column
      };
    }

    return {
      type: 'PrintStatement',
      value: expression,
      line: this.tokens[this.currentToken].line,
      column: this.tokens[this.currentToken].column
    };
  }

  private parseOpenStatement(): ASTNode {
    this.currentToken++; // Skip OPEN
    
    const filepath = this.parseExpression();
    let mode = null;
    let sessionName = null;

    // Handle FOR APPEND/OUTPUT
    if (this.peek().value.toUpperCase() === 'FOR') {
      this.currentToken++;
      mode = this.parseIdentifier().value;
    }

    // Handle AS #num or WITH #name
    if (this.peek().value.toUpperCase() === 'AS' || 
        this.peek().value.toUpperCase() === 'WITH') {
      const kind = this.peek().value.toUpperCase();
      this.currentToken += 2; // Skip AS/WITH and #
      sessionName = this.parseExpression();

      return {
        type: 'OpenFileStatement',
        value: {
          filepath,
          mode,
          kind,
          sessionName
        },
        line: this.tokens[this.currentToken].line,
        column: this.tokens[this.currentToken].column
      };
    }

    return {
      type: 'OpenStatement',
      value: {
        filepath,
        mode
      },
      line: this.tokens[this.currentToken].line,
      column: this.tokens[this.currentToken].column
    };
  }

  private parseIfStatement(): ASTNode {
    this.currentToken++; // Skip IF
    
    const condition = this.parseCondition();
    
    // Must have THEN
    if (this.peek().value.toUpperCase() !== 'THEN') {
      throw new Error('Expected THEN after IF condition');
    }
    this.currentToken++;

    const thenBlock = this.parseBlock();
    let elseBlock = null;

    // Optional ELSE
    if (this.peek().value.toUpperCase() === 'ELSE') {
      this.currentToken++;
      elseBlock = this.parseBlock();
    }

    // Must end with END IF
    if (this.peek().value.toUpperCase() !== 'END' || 
        this.tokens[this.currentToken + 1].value.toUpperCase() !== 'IF') {
      throw new Error('Expected END IF');
    }
    this.currentToken += 2;

    return {
      type: 'IfStatement',
      value: {
        condition,
        thenBlock,
        elseBlock
      },
      line: this.tokens[this.currentToken].line,
      column: this.tokens[this.currentToken].column
    };
  }

  private parseCondition(): ASTNode {
    const condition = this.parseExpression();
    
    // Convert BASIC comparisons to JS
    if (condition.type === 'BinaryExpression') {
      switch (condition.value.operator) {
        case '=':
          condition.value.operator = '===';
          break;
        case '<>':
          condition.value.operator = '!==';
          break;
      }
    }

    return condition;
  }

  private parseBlock(): ASTNode[] {
    const statements: ASTNode[] = [];
    
    while (this.currentToken < this.tokens.length) {
      const token = this.peek();
      
      if (['END', 'ELSE'].includes(token.value.toUpperCase())) {
        break;
      }

      const statement = this.parseStatement();
      if (statement) {
        statements.push(statement);
      }
    }

    return statements;
  }
  private generateIfCode(ast: ASTNode): void {
    const { condition, thenBlock, elseBlock } = ast.value;

    // Convert the condition and emit the if statement
    const convertedCondition = this.generateCondition(condition);
    this.emitCode(`if (${convertedCondition}) {`);
    
    // Indent for then block
    this.indent++;
    
    // Generate code for the then block
    thenBlock.forEach(statement => {
      this.generateCode(statement);
    });
    
    this.indent--;

    // Handle optional else block
    if (elseBlock) {
      this.emitCode('} else {');
      this.indent++;
      
      elseBlock.forEach(statement => {
        this.generateCode(statement);
      });
      
      this.indent--;
    }

    this.emitCode('}');
  }

  private generateCondition(condition: ASTNode): string {
    switch (condition.type) {
      case 'BinaryExpression':
        return this.generateBinaryCondition(condition);
      
      case 'LogicalExpression':
        return this.generateLogicalCondition(condition);
      
      case 'UnaryExpression':
        return this.generateUnaryCondition(condition);
      
      case 'ParenthesizedExpression':
        return `(${this.generateCondition(condition.value)})`;
      
      case 'Identifier':
        return condition.value;
      
      case 'NumberLiteral':
      case 'StringLiteral':
      case 'BooleanLiteral':
        return this.generateLiteral(condition);
      
      case 'FunctionCall':
        return this.generateFunctionCall(condition);
      
      default:
        throw new Error(`Unknown condition type: ${condition.type}`);
    }
  }

  private generateBinaryCondition(condition: ASTNode): string {
    const { operator, left, right } = condition.value;
    
    // Convert BASIC operators to JavaScript
    const jsOperator = this.convertOperator(operator);
    
    return `${this.generateCondition(left)} ${jsOperator} ${this.generateCondition(right)}`;
  }

  private generateLogicalCondition(condition: ASTNode): string {
    const { operator, left, right } = condition.value;
    
    // Convert BASIC logical operators to JavaScript
    const jsOperator = this.convertLogicalOperator(operator);
    
    return `${this.generateCondition(left)} ${jsOperator} ${this.generateCondition(right)}`;
  }

  private generateUnaryCondition(condition: ASTNode): string {
    const { operator, argument } = condition.value;
    
    // Convert BASIC unary operators to JavaScript
    const jsOperator = this.convertUnaryOperator(operator);
    
    return `${jsOperator}${this.generateCondition(argument)}`;
  }

  private convertOperator(operator: string): string {
    switch (operator.toUpperCase()) {
      case '=': return '===';
      case '<>': return '!==';
      case '>=': return '>=';
      case '<=': return '<=';
      case '>': return '>';
      case '<': return '<';
      default: return operator;
    }
  }

  private convertLogicalOperator(operator: string): string {
    switch (operator.toUpperCase()) {
      case 'AND': return '&&';
      case 'OR': return '||';
      case '&&': return '&&';
      case '||': return '||';
      default: return operator;
    }
  }

  private convertUnaryOperator(operator: string): string {
    switch (operator.toUpperCase()) {
      case 'NOT': return '!';
      case '!': return '!';
      case '-': return '-';
      default: return operator;
    }
  }

  private generateLiteral(literal: ASTNode): string {
    switch (literal.type) {
      case 'NumberLiteral':
        return literal.value.toString();
      
      case 'StringLiteral':
        return `"${literal.value}"`;
      
      case 'BooleanLiteral':
        return literal.value.toString();
      
      default:
        throw new Error(`Unknown literal type: ${literal.type}`);
    }
  }

  private generateFunctionCall(call: ASTNode): string {
    const { callee, arguments: args } = call.value;
    const generatedArgs = args.map(arg => this.generateCondition(arg)).join(', ');
    return `${callee.value}(${generatedArgs})`;
  }

  // Indentation handling
  private indent: number = 0;
  private getIndentation(): string {
    return '  '.repeat(this.indent);
  }


  // Handle special BASIC condition functions
  private isIntrinsicConditionFunction(name: string): boolean {
    return [
      'ISNULL', 
      'ISNUMERIC', 
      'ISDATE', 
      'ISEMPTY',
      'CONTAINS',
      'STARTSWITH',
      'ENDSWITH'
    ].includes(name.toUpperCase());
  }

  private generateIntrinsicCondition(call: ASTNode): string {
    const { callee, arguments: args } = call.value;
    
    switch (callee.value.toUpperCase()) {
      case 'ISNULL':
        return `${this.generateCondition(args[0])} === null`;
      
      case 'ISNUMERIC':
        return `!isNaN(parseFloat(${this.generateCondition(args[0])}))`;
      
      case 'ISDATE':
        return `!isNaN(Date.parse(${this.generateCondition(args[0])}))`;
      
      case 'ISEMPTY':
        return `${this.generateCondition(args[0])} === ""`;
      
      case 'CONTAINS':
        return `${this.generateCondition(args[0])}.includes(${this.generateCondition(args[1])})`;
      
      case 'STARTSWITH':
        return `${this.generateCondition(args[0])}.startsWith(${this.generateCondition(args[1])})`;
      
      case 'ENDSWITH':
        return `${this.generateCondition(args[0])}.endsWith(${this.generateCondition(args[1])})`;
      
      default:
        throw new Error(`Unknown intrinsic function: ${callee.value}`);
    }
  }
  private generateForCode(ast: ASTNode): void {
    const { variable, start, end, step, body } = ast.value;

    if (this.isForEach(ast)) {
      this.generateForEachCode(ast);
      return;
    }

    // Generate standard FOR loop initialization
    const initValue = this.generateExpression(start);
    const endValue = this.generateExpression(end);
    const stepValue = step ? this.generateExpression(step) : '1';
    const varName = this.generateExpression(variable);

    // Generate the for loop with proper direction check
    this.emitCode(`for (let ${varName} = ${initValue}; ` +
      `${stepValue} > 0 ? ${varName} <= ${endValue} : ${varName} >= ${endValue}; ` +
      `${varName} += ${stepValue}) {`);

    // Indent and generate loop body
    this.indent++;
    body.forEach(statement => {
      this.generateCode(statement);
    });
    this.indent--;

    this.emitCode('}');
  }

  private generateForEachCode(ast: ASTNode): void {
    const { variable, collection, body } = ast.value;

    // Special handling for collection with paging
    if (this.hasPagedCollection(collection)) {
      this.generatePagedForEachCode(ast);
      return;
    }

    // Generate initialization code
    this.emitCode(`
      __totalCalls = 10;
      __next = true;
      __calls = 0;
      __data = ${this.generateExpression(collection)};
      __index = 0;

      if (__data[0] && __data[0]['gbarray']) {
        __data = __data.slice(1);
      }

      __pageMode = __data?.pageMode ? __data.pageMode : "none";
      __url = __data?.links?.next?.uri;
      __seekToken = __data.links?.self?.headers["MS-ContinuationToken"];
      __totalCount = __data?.totalCount ? __data.totalCount : __data.length;

      while (__next && __totalCount) {
        let ${this.generateExpression(variable)} = __data?.items ? 
          __data?.items[__index] : __data[__index];
    `);

    // Indent and generate loop body
    this.indent++;
    body.forEach(statement => {
      this.generateCode(statement);
    });
    this.indent--;

    // Generate paging logic
    this.emitCode(`
        __index = __index + 1;

        if (__index === __totalCount) {
          if (__calls < __totalCalls && __pageMode === "auto") {
            let ___data = null;
            await retry(async (bail) => {
              await ensureTokens();
              ___data = await sys.getHttp({
                pid: pid,
                file: __url,
                addressOrHeaders: headers,
                httpUsername,
                httpPs
              });
            }, { retries: 5 });

            __data = ___data;
            ___data = null;

            __url = __data?.links?.next?.uri;
            __seekToken = __data?.links?.self?.headers["MS-ContinuationToken"];
            __totalCount = __data?.totalCount ? __data.totalCount : __data.length;

            __index = 0;
            __calls++;
          } else {
            __next = false;
          }
        }
        __data = null;
      }`);
  }

  private generatePagedForEachCode(ast: ASTNode): void {
    const { variable, collection, pageSize, body } = ast.value;

    this.emitCode(`
      if (!limit) limit = ${pageSize || 100};
      __page = 1;
      while (__page > 0 && __page < pages) {
        let __res = null;
        await retry(async (bail) => {
          await ensureTokens();
          __res = await sys.getHttp({
            pid: pid,
            file: host + ${this.generateExpression(collection.url)} + 
                  '?' + pageVariable + '=' + __page + 
                  '&' + limitVariable + '=' + limit,
            addressOrHeaders: headers,
            httpUsername,
            httpPs
          });
        }, { retries: 5 });

        await sleep(330);

        res = __res;
        __res = null;
        list1 = res.data;
        res = null;

        let j1 = 0;
        items1 = [];
        while (j1 < ubound(list1)) {
          let ${this.generateExpression(variable)} = list1[j1];
    `);

    // Indent and generate loop body
    this.indent++;
    body.forEach(statement => {
      this.generateCode(statement);
    });
    this.indent--;

    // Generate paging footer
    this.emitCode(`
          j1 = j1 + 1;
        }

        list1 = null;
        __page = list1?.length < limit ? 0 : __page + 1;
      }`);
  }

  private isForEach(ast: ASTNode): boolean {
    return ast.type === 'ForEachStatement';
  }

  private hasPagedCollection(collection: ASTNode): boolean {
    return collection.type === 'PagedCollection';
  }

  private generateExpression(expr: ASTNode): string {
    switch (expr.type) {
      case 'Identifier':
        return expr.value;

      case 'NumberLiteral':
        return expr.value.toString();

      case 'StringLiteral':
        return `"${expr.value}"`;

      case 'BinaryExpression':
        return this.generateBinaryExpression(expr);

      case 'FunctionCall':
        return this.generateFunctionCall(expr);

      default:
        throw new Error(`Unknown expression type: ${expr.type}`);
    }
  }

  private generateBinaryExpression(expr: ASTNode): string {
    const { operator, left, right } = expr.value;
    return `${this.generateExpression(left)} ${operator} ${this.generateExpression(right)}`;
  }

  // Special handling for collection operations
  private generateCollectionOperation(operation: ASTNode): string {
    const { method, args } = operation.value;
    
    switch (method) {
      case 'FILTER':
        return `filter(${args.map(arg => this.generateExpression(arg)).join(', ')})`;
      
      case 'MAP':
        return `map(${args.map(arg => this.generateExpression(arg)).join(', ')})`;
      
      case 'REDUCE':
        return `reduce(${args.map(arg => this.generateExpression(arg)).join(', ')})`;
      
      default:
        throw new Error(`Unknown collection operation: ${method}`);
    }
  }

  // Loop control statement handling
  private generateContinue(): void {
    this.emitCode('continue;');
  }

  private generateBreak(): void {
    this.emitCode('break;');
  }

  private generateExit(): void {
    this.emitCode('break;');
  }


  private generateCode(ast: ASTNode): void {
    switch (ast.type) {
      case 'Program':
        ast.children?.forEach(child => this.generateCode(child));
        break;

      case 'InputStatement':
        this.generateInputCode(ast);
        break;

      case 'PrintStatement':
        this.generatePrintCode(ast);
        break;

      case 'FilePrintStatement': 
        this.generateFilePrintCode(ast);
        break;

      case 'OpenStatement':
        this.generateOpenCode(ast);
        break;

      case 'IfStatement':
        this.generateIfCode(ast);
        break;

      case 'ForStatement':
        this.generateForCode(ast);
        break;

      case 'WhileStatement':
        this.generateWhileCode(ast);
        break;

      case 'ForEachStatement':
        this.generateForEachCode(ast);
        break;

      case 'HearStatement':
        this.generateHearCode(ast);
        break;

      case 'TalkStatement':
        this.generateTalkCode(ast);
        break;

      case 'AssignmentStatement':
        this.generateAssignmentCode(ast);
        break;
    }
  }

  private generateInputCode(ast: ASTNode): void {
    const {prompt, variable} = ast.value;
    
    if (variable) {
      this.emitCode(`
        TALK ${this.generateExpression(prompt)}
        HEAR ${this.generateExpression(variable)}
      `);
    } else {
      this.emitCode(`HEAR ${this.generateExpression(prompt)}`);
    }
  }

  private generatePrintCode(ast: ASTNode): void {
    this.emitCode(`await dk.talk({pid: pid, text: ${this.generateExpression(ast.value)}})`);
  }

  private generateFilePrintCode(ast: ASTNode): void {
    const {sessionName, items} = ast.value;

    if (items.length > 1) {
      this.emitCode(
        `await sys.save({pid: pid, file: files[${sessionName}], args:[${items.join(',')}]})`
      );
    } else {
      this.emitCode(
        `await sys.set({pid: pid, file: files[${sessionName}], address: col++, name: "${items[0]}", value: ${items[0]}})`
      );
    }
  }
  private parseDoWhileStatement(): ASTNode {
    this.currentToken++; // Skip DO
    
    if (this.peek().value.toUpperCase() !== 'WHILE') {
      throw new Error(`Expected WHILE after DO at line ${this.line}`);
    }
    this.currentToken++; // Skip WHILE

    const condition = this.parseCondition();
    const body = this.parseBlock();

    // Must end with LOOP
    if (this.peek().value.toUpperCase() !== 'LOOP') {
      throw new Error(`Expected LOOP at line ${this.line}`);
    }
    this.currentToken++; // Skip LOOP

    return {
      type: 'WhileStatement',
      value: {
        condition,
        body
      },
      line: this.tokens[this.currentToken].line,
      column: this.tokens[this.currentToken].column
    };
  }

  private isAssignment(): boolean {
    // Look ahead to check for assignment pattern
    let i = this.currentToken;
    
    // Skip identifier(s)
    while (i < this.tokens.length && 
           (this.tokens[i].type === 'WORD' || 
            this.tokens[i].value === '.' || 
            this.tokens[i].value === '[' || 
            this.tokens[i].value === ']')) {
      i++;
    }

    // Check if next non-whitespace token is =
    while (i < this.tokens.length) {
      if (this.tokens[i].type !== 'WHITESPACE') {
        return this.tokens[i].value === '=';
      }
      i++;
    }

    return false;
  }

  private generateWhileCode(ast: ASTNode): void {
    const { condition, body } = ast.value;

    // Convert the condition
    const convertedCondition = this.generateCondition(condition);
    
    // Emit while loop
    this.emitCode(`while (${convertedCondition}) {`);
    
    // Indent and generate loop body
    this.indent++;
    body.forEach(statement => {
      this.generateCode(statement);
    });
    this.indent--;

    this.emitCode('}');
  }

  private parseAssignmentStatement(): ASTNode {
    let left = this.parseLeftHandSide();
    
    if (this.peek().value !== '=') {
      throw new Error(`Expected = but got ${this.peek().value} at line ${this.line}`);
    }
    this.currentToken++; // Skip =

    // Handle special assignments
    const nextToken = this.peek();
    let right;

    switch (nextToken.value.toUpperCase()) {
      case 'SELECT':
        right = this.parseSelectStatement();
        break;

      case 'CHART':
        right = this.parseChartStatement();
        break;

      case 'GET':
        right = this.parseGetStatement();
        break;

      case 'POST':
        right = this.parseHttpStatement('POST');
        break;

      case 'PUT':
        right = this.parseHttpStatement('PUT');
        break;

      case 'BLUR':
        right = this.parseImageOperation('BLUR');
        break;

      case 'SHARPEN':
        right = this.parseImageOperation('SHARPEN');
        break;

      case 'FORMAT':
        right = this.parseFormatStatement();
        break;

      case 'DATEDIFF':
        right = this.parseDateOperation('DATEDIFF');
        break;

      case 'DATEADD':
        right = this.parseDateOperation('DATEADD');
        break;

      case 'NEW':
        right = this.parseNewStatement();
        break;

      case 'FIND':
        right = this.parseFindStatement();
        break;

      case 'CREATE':
        if (this.tokens[this.currentToken + 1]?.value.toUpperCase() === 'DEAL') {
          right = this.parseCreateDealStatement();
        } else {
          right = this.parseCreateStatement();
        }
        break;

      case 'ACTIVE':
        if (this.tokens[this.currentToken + 1]?.value.toUpperCase() === 'TASKS') {
          right = this.parseActiveTasksStatement();
        } else {
          right = this.parseExpression();
        }
        break;

      case 'UPLOAD':
        right = this.parseUploadStatement();
        break;

      case 'FILL':
        right = this.parseFillStatement();
        break;

      case 'CARD':
        right = this.parseCardStatement();
        break;

      case 'ALLOW':
        if (this.tokens[this.currentToken + 1]?.value.toUpperCase() === 'ROLE') {
          right = this.parseAllowRoleStatement();
        } else {
          right = this.parseExpression();
        }
        break;

      default:
        right = this.parseExpression();
    }

    return {
      type: 'AssignmentStatement',
      value: {
        left,
        right,
        operator: '='
      },
      line: this.tokens[this.currentToken].line,
      column: this.tokens[this.currentToken].column
    };
  }

  private parseLeftHandSide(): ASTNode {
    let identifier = this.parseIdentifier();
    
    // Handle property access (obj.prop) and array access (arr[index])
    while (
      this.peek().value === '.' || 
      this.peek().value === '[') {
      if (this.peek().value === '.') {
        this.currentToken++; // Skip .
        const property = this.parseIdentifier();
        identifier = {
          type: 'MemberExpression',
          value: {
            object: identifier,
            property,
            computed: false
          },
          line: this.tokens[this.currentToken].line,
          column: this.tokens[this.currentToken].column
        };
      } else {
        this.currentToken++; // Skip [
        const index = this.parseExpression();
        
        if (this.peek().value !== ']') {
          throw new Error(`Expected ] at line ${this.line}`);
        }
        this.currentToken++; // Skip ]
        
        identifier = {
          type: 'MemberExpression',
          value: {
            object: identifier,
            property: index,
            computed: true
          },
          line: this.tokens[this.currentToken].line,
          column: this.tokens[this.currentToken].column
        };
      }
    }

    return identifier;
  }

  // Helper method to handle async operations in assignments
  private isAsyncOperation(rightType: string): boolean {
    const asyncOperations = [
      'SelectStatement',
      'HttpRequest',
      'ImageOperation',
      'ChartStatement',
      'FindStatement',
      'UploadStatement',
      'CreateDealStatement',
      'ActiveTasksStatement',
      'AllowRoleStatement',
      'FillStatement',
      'CardStatement'
    ];
    return asyncOperations.includes(rightType);
  }

  private generateAssignmentCode(ast: ASTNode): void {
    const { left, right, operator } = ast.value;
    const leftCode = this.generateExpression(left);
    
    // Check if this is an async operation
    if (this.isAsyncOperation(right.type)) {
      this.emitCode(`${leftCode} = await ${this.generateAsyncOperation(right)};`);
    } else {
      this.emitCode(`${leftCode} = ${this.generateExpression(right)};`);
    }
  }

  private generateAsyncOperation(operation: ASTNode): string {
    switch (operation.type) {
      case 'SelectStatement':
        return this.generateSelectOperation(operation);
      case 'HttpRequest':
        return this.generateHttpOperation(operation);
      case 'ImageOperation':
        return this.generateImageOperation(operation);
      case 'ChartStatement':
        return this.generateChartOperation(operation);
      // ... handle other async operations
      default:
        throw new Error(`Unknown async operation type: ${operation.type}`);
    }
  }
  
  private generateOpenCode(ast: ASTNode): void {
    let {filepath, mode, kind, sessionName} = ast.value;

    if (kind === 'AS' && this.isNumber(sessionName)) {
      const filename = `${filepath.substr(0, filepath.lastIndexOf('.'))}.xlsx`;
      this.emitCode(`
        col = 1
        await sys.save({pid: pid, file: "${filename}", args: [id]})
        await dk.setFilter({pid: pid, value: "id=" + id})
        files[${sessionName}] = "${filename}"
      `);
    } else {
      const params = this.generateParams(['url', 'username', 'password'], [filepath]);
      sessionName = sessionName ? `"${sessionName}"` : null;
      const kindStr = `"${kind}"`;
      
      this.emitCode(
        `page = await wa.openPage({pid: pid, handle: page, sessionKind: ${kindStr}, sessionName: ${sessionName}, ${params}})`
      );
    }
  }



  private generateHearCode(ast: ASTNode): void {
    const {variable, kind, args} = ast.value;

    if (kind) {
      if (kind === 'sheet') {
        this.emitCode(
          `${variable} = await dk.hear({pid: pid, kind:"sheet", arg: "${args[0]}"})`
        );
      } else {
        this.emitCode(
          `${variable} = await dk.hear({pid: pid, kind:"${kind}"${args ? `, args: [${args}]` : ''}})`
        );
      }
    } else {
      this.emitCode(`${variable} = await dk.hear({pid: pid})`);
    }
  }

  private generateTalkCode(ast: ASTNode): void {
    const text = this.normalizeQuotes(ast.value);
    this.emitCode(`await dk.talk({pid: pid, text: ${text}})`);
  }
  private generateChartAssignment(left: string, right: ASTNode): void {
    const {type, data, legends, transpose, prompt} = right.value;
    
    // Handle regular chart
    if (!prompt) {
      this.emitCode(`
        ${left} = await dk.chart({
          pid: pid,
          type: ${type},
          data: ${data},
          legends: ${legends},
          transpose: ${transpose}
        })`
      );
    } 
    // Handle chart with prompt (LLM chart)
    else {
      this.emitCode(`
        ${left} = await dk.llmChart({
          pid: pid,
          type: ${type},
          data: ${data},
          prompt: ${prompt}
        })`
      );
    }
  }

  private generateSelectAssignment(left: string, right: ASTNode): void {
    const {tableName, sql} = right.value;
    
    // Replace table name with ? in SQL
    const sqlWithPlaceholder = sql.replace(tableName, '?');

    this.emitCode(`
      ${left} = await sys.executeSQL({
        pid: pid,
        data: ${tableName},
        sql: \`${sqlWithPlaceholder}\`
      })`
    );
  }

  private generateHttpAssignment(left: string, right: ASTNode): void {
    const {method, url, data} = right.value;

    switch (method.toUpperCase()) {
      case 'GET':
        this.emitCode(`
          if (${url}.endsWith('.pdf') && !${url}.startsWith('https')) {
            ${left} = await sys.getPdf({pid: pid, file: ${url}});
          } else {
            let __${left} = null;
            await retry(async (bail) => {
              await ensureTokens();
              __${left} = await sys.getHttp({
                pid: pid,
                file: ${url},
                addressOrHeaders: headers,
                httpUsername,
                httpPs
              });
            }, { retries: 5 });

            ${left} = __${left};
            __${left} = null;
          }
        `);
        break;

      case 'POST':
        this.emitCode(`
          await retry(async (bail) => {
            await ensureTokens();
            __${left} = await sys.postByHttp({
              pid: pid,
              url: ${url},
              data: ${data},
              headers
            });
          }, { retries: 5 });

          ${left} = __${left};
        `);
        break;

      case 'PUT':
        this.emitCode(`
          await retry(async (bail) => {
            await ensureTokens();
            __${left} = await sys.putByHttp({
              pid: pid,
              url: ${url},
              data: ${data},
              headers
            });
          }, { retries: 5 });

          ${left} = __${left};
        `);
        break;
    }
  }

  private generateImageAssignment(left: string, right: ASTNode): void {
    const {operation, args} = right.value;

    switch (operation) {
      case 'BLUR':
        this.emitCode(`
          ${left} = await img.blur({
            pid: pid,
            args: [${args.join(',')}]
          })`
        );
        break;

      case 'SHARPEN':
        this.emitCode(`
          ${left} = await img.sharpen({
            pid: pid,
            args: [${args.join(',')}]
          })`
        );
        break;

      case 'GET IMAGE':
        this.emitCode(`
          ${left} = await img.getImageFromPrompt({
            pid: pid,
            prompt: ${args[0]}
          })`
        );
        break;
    }
  }

  private generateDateAssignment(left: string, right: ASTNode): void {
    const {operation, params} = right.value;

    switch (operation) {
      case 'DATEDIFF':
        this.emitCode(`
          ${left} = await dk.getDateDiff({
            pid: pid,
            date1: ${params.date1},
            date2: ${params.date2},
            mode: ${params.mode}
          })`
        );
        break;

      case 'DATEADD':
        this.emitCode(`
          ${left} = await dk.dateAdd({
            pid: pid,
            date: ${params.date},
            mode: ${params.mode},
            units: ${params.units}
          })`
        );
        break;
    }
  }
  private parseWriteStatement(): ASTNode {
    this.currentToken++; // Skip WRITE

    // Parse the expression to be written
    const expression = this.parseExpression();

    // WRITE is syntactic sugar for PRINT in BASIC
    return {
      type: 'PrintStatement',  // We reuse PrintStatement since WRITE converts to PRINT
      value: expression,
      line: this.tokens[this.currentToken].line,
      column: this.tokens[this.currentToken].column
    };
  }

  private parseRemStatement(): ASTNode {
    this.currentToken++; // Skip REM
    
    // Collect all tokens until end of line as comment text
    let commentText = '';
    while (this.currentToken < this.tokens.length && 
           this.tokens[this.currentToken].type !== 'NEWLINE') {
      commentText += this.tokens[this.currentToken].value + ' ';
      this.currentToken++;
    }

    return {
      type: 'CommentStatement',
      value: commentText.trim(),
      line: this.tokens[this.currentToken].line,
      column: this.tokens[this.currentToken].column
    };
  }

  private parseCloseStatement(): ASTNode {
    this.currentToken++; // Skip CLOSE

    // Optional file number or identifier
    let fileRef = null;
    if (this.peek().type !== 'NEWLINE') {
      fileRef = this.parseExpression();
    }

    return {
      type: 'CloseStatement',
      value: fileRef,
      line: this.tokens[this.currentToken].line,
      column: this.tokens[this.currentToken].column
    };
  }

  private generateCloseCode(ast: ASTNode): void {
    // CLOSE is typically a no-op in the modern context
    // but we might want to handle cleanup of file handles
    if (ast.value) {
      this.emitCode(`// Closing file ${ast.value}`);
    }
  }

  private generateWriteCode(ast: ASTNode): void {
    // WRITE statement gets converted to PRINT
    this.emitCode(`PRINT${this.generateExpression(ast.value)}`);
  }

  private generateRemCode(ast: ASTNode): void {
    // Comments are preserved but as JS-style comments
    this.emitCode(`// ${ast.value}`);
  }

  // Helper method to handle file operations
  private isFileOperation(token: Token): boolean {
    return token.value.startsWith('#') || 
           this.peek(1)?.value === '#' ||
           this.isFileHandle(token);
  }

  private isFileHandle(token: Token): boolean {
    // Check if token represents a file handle
    return token.type === 'WORD' && 
           this.fileHandles.has(token.value);
  }

  // Additional helper for parsing file specifications
  private parseFileSpec(): {
    handle: string;
    mode?: string;
    access?: string;
  } {
    let handle: string;
    let mode: string;
    let access: string;

    if (this.peek().value === '#') {
      this.currentToken++; // Skip #
      handle = this.parseExpression().value;
    } else {
      handle = this.parseExpression().value;
    }

    // Check for optional mode (FOR INPUT/OUTPUT/APPEND)
    if (this.peek().value.toUpperCase() === 'FOR') {
      this.currentToken++; // Skip FOR
      mode = this.peek().value.toUpperCase();
      this.currentToken++; // Skip mode
    }

    // Check for optional access (SHARED/LOCK READ/LOCK WRITE)
    if (this.peek().value.toUpperCase() === 'ACCESS') {
      this.currentToken++; // Skip ACCESS
      access = this.peek().value.toUpperCase();
      this.currentToken++; // Skip access mode
    }

    return { handle, mode, access };
  }

  // Helper for tracking file handles
  private fileHandles = new Set<string>();

  private registerFileHandle(handle: string): void {
    this.fileHandles.add(handle);
  }

  private unregisterFileHandle(handle: string): void {
    this.fileHandles.delete(handle);
  }

  // Extended parseStatement to handle all file operations
  private parseFileStatement(): ASTNode {
    const operation = this.peek().value.toUpperCase();
    this.currentToken++; // Skip operation keyword

    switch (operation) {
      case 'WRITE':
        return this.parseWriteStatement();
      
      case 'PRINT':
        if (this.isFileOperation(this.peek())) {
          return this.parseFilePrintStatement();
        }
        return this.parsePrintStatement();
      
      case 'CLOSE':
        return this.parseCloseStatement();
      
      case 'OPEN':
        return this.parseOpenStatement();
      
      default:
        throw new Error(
          `Unknown file operation ${operation} at line ${this.tokens[this.currentToken].line}`
        );
    }
  }

  private parseFilePrintStatement(): ASTNode {
    const fileSpec = this.parseFileSpec();
    
    if (this.peek().value !== ',') {
      throw new Error('Expected , after file specification');
    }
    this.currentToken++; // Skip ,

    const expressions: ASTNode[] = [];
    
    do {
      expressions.push(this.parseExpression());
      
      if (this.peek().value !== ',') {
        break;
      }
      this.currentToken++; // Skip ,
    } while (true);

    return {
      type: 'FilePrintStatement',
      value: {
        file: fileSpec,
        expressions
      },
      line: this.tokens[this.currentToken].line,
      column: this.tokens[this.currentToken].column
    };
  }

  private generateFormatAssignment(left: string, right: ASTNode): void {
    const {value, format} = right.value;
    
    this.emitCode(`
      ${left} = await dk.format({
        pid: pid,
        value: ${value},
        format: ${format}
      })`
    );
  }

  private generateCardAssignment(left: string, right: ASTNode): void {
    const {doc, data} = right.value;
    
    this.emitCode(`
      ${left} = await dk.card({
        pid: pid,
        args: [${doc}, ${data}]
      })`
    );
  }
  private generateAssignmentCode(ast: ASTNode): void {
    const {left, right} = ast.value;

    switch (right.type) {
      case 'SelectStatement':
        this.generateSelectAssignment(left, right);
        break;

      case 'ChartStatement':
        this.generateChartAssignment(left, right);
        break;

      case 'HttpRequest':
        this.generateHttpAssignment(left, right);
        break;

      default:
        this.emitCode(`${left} = ${this.generateExpression(right)}`);
    }
  }

  // Utility methods
  private emitCode(code: string): void {
    this.output.push(code);
    this.lineMap[this.output.length] = this.line;
  }

  private isNumber(value: any): boolean {
    return !isNaN(parseFloat(value)) && isFinite(value);
  }

  private normalizeQuotes(text: string): string {
    if (!text.trim().startsWith('`') && !text.trim().startsWith("'")) {
      return '`' + text + '`';
    }
    return text;
  }

  private generateParams(names: string[], values: any[]): string {
    let params = '';
    names.forEach((name, i) => {
      const value = values[i];
      params += `"${name}": ${value === undefined ? null : value}${i < names.length - 1 ? ', ' : ''}`;
    });
    return params;
  }

  private peek(offset: number = 0): Token {
    return this.tokens[this.currentToken + offset];
  }

  private generateMetadata(mainName: string): any {
    return {
      name: mainName,
      description: this.systemPromptBuffer || '',
      properties: []
    };
  }

  // Helper method to split params but ignore commas in quotes (from KeywordsExpressions)
  private splitParams(str: string): string[] {
    return str.split(',').reduce(
      (accum: {soFar: string[], isConcatting: boolean}, curr: string) => {
        if (accum.isConcatting) {
          accum.soFar[accum.soFar.length - 1] += ',' + curr;
        } else {
          accum.soFar.push(curr ? curr.trim() : '');
        }
        
        if (curr.split('`').length % 2 === 0) {
          accum.isConcatting = !accum.isConcatting;
        }
        
        return accum;
      },
      { soFar: [], isConcatting: false }
    ).soFar;
  }

  // Converts BASIC conditions to JS conditions
  private convertConditions(input: string): string {
    let result = input.replace(/ +and +/gi, ' && ');
    result = result.replace(/ +or +/gi, ' || ');
    result = result.replace(/ +not +/gi, ' !');
    result = result.replace(/ +<> +/gi, ' !== ');
    result = result.replace(/ += +/gi, ' === ');
    return result;
  }
}
