import { Node } from 'web-tree-sitter';
import { BaseIndexer } from './base-indexer.js';
import { ExportMetadata, SymbolType } from '@scandiumsys/owstra-types';

export class RubyIndexer extends BaseIndexer {
  public readonly language = 'ruby';

  public extractExports(root: Node, filePath: string): ExportMetadata[] {
    const exports: ExportMetadata[] = [];

    const addExport = (name: string, type: SymbolType, node: Node) => {
      exports.push({
        name,
        type,
        filePath,
        line: node.startPosition.row + 1,
        column: node.startPosition.column + 1,
        parameters: this.extractParameters(node)
      });
    };

    // 1. Methods
    const methodNodes = root.descendantsOfType('method');
    for (const node of methodNodes) {
        const nameNode = node.childForFieldName('name');
        if (nameNode) addExport(nameNode.text, 'method', node);
    }

    // 2. Classes
    const classNodes = root.descendantsOfType('class');
    for (const node of classNodes) {
        const nameNode = node.childForFieldName('name');
        if (nameNode) addExport(nameNode.text, 'class', node);
    }

    // 3. Singleton methods (def self.func)
    const singletonNodes = root.descendantsOfType('singleton_method');
    for (const node of singletonNodes) {
        const nameNode = node.childForFieldName('name');
        if (nameNode) addExport(nameNode.text, 'method', node);
    }

    return exports;
  }

  public extractImports(root: Node): Record<string, string> {
    const imports: Record<string, string> = {};

    // require 'library'
    const callNodes = root.descendantsOfType('call');
    for (const node of callNodes) {
        const methodName = node.childForFieldName('method')?.text;
        if (methodName === 'require' || methodName === 'load') {
            const argsNode = node.childForFieldName('arguments');
            if (argsNode) {
                const firstArg = argsNode.children.find(c => c.type === 'string' || c.type === 'simple_symbol');
                if (firstArg) {
                    const fullPath = firstArg.text.replace(/['":]/g, '');
                    const parts = fullPath.split('/');
                    const localName = parts[parts.length - 1];
                    if (localName) imports[localName] = fullPath;
                }
            }
        }
    }

    return imports;
  }

  public extractCalls(root: Node): Array<{ name: string, argPositions: Array<{ row: number; column: number }> }> {
    const calls: Array<{ name: string, argPositions: Array<{ row: number; column: number }> }> = [];
    const callNodes = root.descendantsOfType('call');

    for (const node of callNodes) {
        const methodNode = node.childForFieldName('method');
        if (methodNode) {
            const name = methodNode.text;
            const argsNode = node.childForFieldName('arguments');
            const argPositions: Array<{ row: number; column: number }> = [];
            if (argsNode) {
                for (let i = 0; i < argsNode.childCount; i++) {
                    const arg = argsNode.child(i);
                    if (arg && arg.isNamed && arg.text !== '(' && arg.text !== ')') {
                        argPositions.push({
                            row: arg.startPosition.row,
                            column: arg.startPosition.column
                        });
                    }
                }
            }
            calls.push({ name, argPositions });
        }
    }

    return calls;
  }

  private extractParameters(node: Node): string[] {
    const params: string[] = [];
    const paramNode = node.childForFieldName('parameters');
    if (paramNode) {
        const identifiers = paramNode.descendantsOfType('identifier');
        for (const id of identifiers) {
            // Ruby parameters can be complicated, but identifiers in parameters are usually the names
            params.push(id.text);
        }
    }
    return params;
  }
}
