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

export class PythonIndexer extends BaseIndexer {
  public readonly language = 'python';

  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.children.filter(c => c.type === 'function_definition' || c.type === 'async_function_definition');
    for (const node of funcNodes) {
      const name = node.childForFieldName('name')?.text;
      if (name) addExport(name, 'function', node);
    }

    // 2. Classes
    const classNodes = root.children.filter(c => c.type === 'class_definition');
    for (const node of classNodes) {
      const name = node.childForFieldName('name')?.text;
      if (name) addExport(name, 'class', node);
    }

    return exports;
  }

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

    // 1. import x, import x as y
    const importNodes = root.descendantsOfType('import_statement');
    for (const node of importNodes) {
      // children are 'import' then aliased_import or dotted_name
      for (const child of node.children) {
          if (child.type === 'dotted_name') {
              imports[child.text] = child.text;
          } else if (child.type === 'aliased_import') {
              const name = child.childForFieldName('name')?.text;
              const alias = child.childForFieldName('alias')?.text;
              if (name && alias) imports[alias] = name;
              else if (name) imports[name] = name;
          }
      }
    }

    // 2. from x import y, from x import y as z
    const importFromNodes = root.descendantsOfType('import_from_statement');
    for (const node of importFromNodes) {
      const moduleName = node.childForFieldName('module_name')?.text;
      if (!moduleName) continue;

      const importList = node.children.find(c => c.type === 'import_list' || c.type === 'aliased_import' || c.type === 'dotted_name');
      if (importList) {
          const aliased = importList.descendantsOfType('aliased_import');
          if (aliased.length > 0) {
              for (const a of aliased) {
                  const name = a.childForFieldName('name')?.text;
                  const alias = a.childForFieldName('alias')?.text;
                  if (name && alias) imports[alias] = `${moduleName}.${name}`;
                  else if (name) imports[name] = `${moduleName}.${name}`;
              }
          } else {
              // from x import y (where y is a single name)
              const names = importList.descendantsOfType('dotted_name');
              for (const n of names) {
                  imports[n.text] = `${moduleName}.${n.text}`;
              }
          }
      }
    }

    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 func = node.childForFieldName('function');
      if (func) {
        // Handle simple calls: func()
        // Handle member calls: obj.method() -> we track 'method' as the name for now
        let name = func.text;
        if (func.type === 'attribute') {
            name = func.childForFieldName('attribute')?.text || name;
        }

        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) {
          // In python, check if it's a direct parameter not a sub-expression
          if (id.parent?.type === 'parameters' || id.parent?.type === 'default_parameter' || id.parent?.type === 'typed_parameter') {
            params.push(id.text);
          }
      }
    }
    return params;
  }
}
