import { GraphQLQuery, GraphQLField } from "@graphql-lint/core";
import { Project, SourceFile, Node, SyntaxKind } from "ts-morph";
import { parse as babelParse } from "@babel/parser";
import traverse from "@babel/traverse";
import * as t from "@babel/types";

export class ClintUsageAnalyzer {
  private project: Project;
  private clintPatternUsages: Map<string, Array<{
    file: string;
    line: number;
    column: number;
    pattern: string;
  }>> = new Map();

  constructor() {
    this.project = new Project({
      useInMemoryFileSystem: true,
      compilerOptions: {
        allowJs: true,
        allowSyntheticDefaultImports: true,
        esModuleInterop: true,
      },
    });
  }

  /**
   * Analisa uso de padrões Clint específicos: clint.entity.method
   */
  async analyzeClintUsagePatterns(
    actionsQueries: GraphQLQuery[],
    fileContents: Map<string, string>
  ): Promise<Map<string, any[]>> {
    
    console.log(`🔍 Analisando padrões Clint para ${actionsQueries.length} actions em ${fileContents.size} arquivos`);

    // Para cada action, procurar o padrão correspondente no código
    for (const action of actionsQueries) {
      const clintPattern = this.actionToClintPattern(action.name);
      if (clintPattern) {
        await this.findClintPatternUsages(clintPattern, action.name, fileContents);
      }
    }

    return this.clintPatternUsages;
  }

  /**
   * Procura usos de um padrão Clint específico no código
   * Ex: clint.owner.getName
   */
  private async findClintPatternUsages(
    clintPattern: string,
    actionName: string,
    fileContents: Map<string, string>
  ): Promise<void> {
    
    for (const [filePath, content] of fileContents) {
      if (filePath.includes('node_modules') || filePath.includes('.test.') || filePath.includes('.spec.')) {
        continue;
      }

      try {
        if (filePath.endsWith('.ts') || filePath.endsWith('.tsx')) {
          await this.analyzeTypeScriptClintPattern(filePath, content, clintPattern, actionName);
        } else if (filePath.endsWith('.js') || filePath.endsWith('.jsx')) {
          await this.analyzeJavaScriptClintPattern(filePath, content, clintPattern, actionName);
        }
      } catch (error) {
        console.warn(`⚠️ Erro ao analisar padrão Clint em ${filePath}:`, error);
      }
    }
  }

  /**
   * Analisa padrões Clint em arquivos TypeScript
   */
  private async analyzeTypeScriptClintPattern(
    filePath: string,
    content: string,
    clintPattern: string,
    actionName: string
  ): Promise<void> {
    
    const sourceFile = this.project.createSourceFile(filePath, content, { overwrite: true });

    // Procurar por member expressions que correspondem ao padrão Clint
    // Ex: clint.owner.getName
    sourceFile.getDescendantsOfKind(SyntaxKind.PropertyAccessExpression).forEach(propAccess => {
      const fullText = propAccess.getText();
      
      if (fullText === clintPattern) {
        this.recordClintPatternUsage(actionName, {
          file: filePath,
          line: propAccess.getStartLineNumber(),
          column: propAccess.getStart(),
          pattern: clintPattern
        });
      }
    });

    // Procurar por call expressions do padrão
    // Ex: clint.owner.getName() ou clint.owner.getName(id)
    sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression).forEach(callExpr => {
      const expression = callExpr.getExpression();
      
      if (Node.isPropertyAccessExpression(expression)) {
        const fullText = expression.getText();
        
        if (fullText === clintPattern) {
          this.recordClintPatternUsage(actionName, {
            file: filePath,
            line: callExpr.getStartLineNumber(),
            column: callExpr.getStart(),
            pattern: `${clintPattern}()`
          });
        }
      }
    });
  }

  /**
   * Analisa padrões Clint em arquivos JavaScript
   */
  private async analyzeJavaScriptClintPattern(
    filePath: string,
    content: string,
    clintPattern: string,
    actionName: string
  ): Promise<void> {
    
    try {
      const ast = babelParse(content, {
        sourceType: "module",
        allowImportExportEverywhere: true,
        plugins: ["jsx", "typescript", "decorators-legacy"],
      });

      traverse(ast, {
        MemberExpression: (path) => {
          const memberChain = this.getMemberExpressionChain(path.node);
          const fullPattern = memberChain.join('.');
          
          if (fullPattern === clintPattern) {
            this.recordClintPatternUsage(actionName, {
              file: filePath,
              line: path.node.loc?.start.line || 0,
              column: path.node.loc?.start.column || 0,
              pattern: clintPattern
            });
          }
        },
        CallExpression: (path) => {
          if (t.isMemberExpression(path.node.callee)) {
            const memberChain = this.getMemberExpressionChain(path.node.callee);
            const fullPattern = memberChain.join('.');
            
            if (fullPattern === clintPattern) {
              this.recordClintPatternUsage(actionName, {
                file: filePath,
                line: path.node.loc?.start.line || 0,
                column: path.node.loc?.start.column || 0,
                pattern: `${clintPattern}()`
              });
            }
          }
        }
      });
    } catch (error) {
      console.warn(`⚠️ Erro ao parsear JavaScript para padrão Clint em ${filePath}:`, error);
    }
  }

  /**
   * Constrói cadeia de member expressions
   * clint.owner.getName -> ['clint', 'owner', 'getName']
   */
  private getMemberExpressionChain(memberExpr: any): string[] {
    const chain: string[] = [];
    let current = memberExpr;
    
    while (t.isMemberExpression(current)) {
      if (t.isIdentifier(current.property)) {
        chain.unshift(current.property.name);
      }
      current = current.object;
    }
    
    if (t.isIdentifier(current)) {
      chain.unshift(current.name);
    }
    
    return chain;
  }

  /**
   * Registra uso de padrão Clint
   */
  private recordClintPatternUsage(actionName: string, usage: {
    file: string;
    line: number;
    column: number;
    pattern: string;
  }): void {
    
    if (!this.clintPatternUsages.has(actionName)) {
      this.clintPatternUsages.set(actionName, []);
    }
    
    this.clintPatternUsages.get(actionName)!.push(usage);
  }

  /**
   * Converte nome de action para padrão Clint
   * owner_get_name -> clint.owner.getName
   */
  private actionToClintPattern(actionName: string): string | null {
    const match = actionName.match(/^(\w+)_(.+)$/);
    if (!match) return null;
    
    const [, entity, action] = match;
    
    // Converter action para camelCase
    const camelAction = action.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
    
    return `clint.${entity}.${camelAction}`;
  }

  /**
   * Retorna estatísticas de uso dos padrões Clint
   */
  getUsageStats(): {
    totalActions: number;
    usedActions: number;
    unusedActions: string[];
    usageDetails: Map<string, any[]>;
  } {
    const totalActions = this.clintPatternUsages.size;
    const usedActions = Array.from(this.clintPatternUsages.values()).filter(usages => usages.length > 0).length;
    const unusedActions = Array.from(this.clintPatternUsages.entries())
      .filter(([, usages]) => usages.length === 0)
      .map(([actionName]) => actionName);

    return {
      totalActions,
      usedActions,
      unusedActions,
      usageDetails: this.clintPatternUsages
    };
  }
}
