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

export class PHPIndexer extends BaseIndexer {
  public readonly language = 'php';

  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. Functions
    const funcNodes = root.descendantsOfType('function_definition');
    for (const node of funcNodes) {
        const nameNode = node.childForFieldName('name');
        if (nameNode) addExport(nameNode.text, 'function', node);
    }

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

    // 3. Methods
    const methodNodes = root.descendantsOfType('method_declaration');
    for (const node of methodNodes) {
        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> = {};

    // use Namespace\Class;
    const useNodes = root.descendantsOfType('namespace_use_declaration');
    for (const node of useNodes) {
        const clauses = node.descendantsOfType('namespace_use_clause');
        for (const clause of clauses) {
            const pathNode = clause.children.find(c => c.type === 'qualified_name' || c.type === 'name');
            if (pathNode) {
                const fullPath = pathNode.text;
                const parts = fullPath.split('\\');
                const localName = clause.childForFieldName('alias')?.text || 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 }> }> = [];
    
    // function_call_expression, member_call_expression, scoped_call_expression
    const callTypes = ['function_call_expression', 'member_call_expression', 'scoped_call_expression'];
    
    for (const type of callTypes) {
        const callNodes = root.descendantsOfType(type);
        for (const node of callNodes) {
            let name = '';
            if (type === 'function_call_expression') {
                name = node.childForFieldName('name')?.text || '';
            } else {
                name = node.childForFieldName('name')?.text || '';
            }

            if (!name) continue;

            const argsNode = node.childForFieldName('arguments');
            const argPositions: Array<{ row: number; column: number }> = [];
            if (argsNode) {
                // Skip '(' and ')'
                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 formalParams = paramNode.descendantsOfType('formal_parameter');
        for (const p of formalParams) {
            const nameNode = p.childForFieldName('name');
            if (nameNode) params.push(nameNode.text);
        }
    }
    return params;
  }
}
