import { GraphQLQuery, GraphQLField } from "graphql-lint-unused-fields";
import fs from "fs";
import path from "path";
import { glob } from "glob";

interface ClintAction {
  name: string;
  pattern: string; // clint.entity.method
  file: string;
  line: number;
}

interface ClintUsageResult {
  unusedActions: string[];
  usedActions: Record<string, Array<{file: string, line: number, code: string}>>;
  totalActions: number;
}

export class ClintUsageAnalyzer {
  private actionPatterns: Record<string, string[]> = {};
  private sourceFiles: string[] = [];

  constructor(actionPatterns: Record<string, string[]>) {
    this.actionPatterns = actionPatterns;
  }

  private async findSourceFiles(sourcePath: string): Promise<string[]> {
    const tsFiles = await glob('**/*.{ts,tsx,js,jsx}', {
      cwd: sourcePath,
      ignore: ['node_modules/**', 'dist/**', 'build/**', '.git/**'],
      absolute: true
    });
    
    return tsFiles;
  }
  
  async analyzeUsageInPath(
    sourcePath: string,
    verbose: boolean = false
  ): Promise<ClintUsageResult> {
    if (verbose) {
      console.log('📋 Iniciando análise DETALHADA de uso dos actions da Clint...\n');
      console.log(`🎯 Total de actions detectadas: ${Object.keys(this.actionPatterns).length}`);
    }
    
    // Encontrar arquivos fonte
    this.sourceFiles = await this.findSourceFiles(sourcePath);
    
    if (verbose) {
      console.log(`📁 Total de arquivos para análise: ${this.sourceFiles.length}\n`);
    }
    
    const unusedActions: string[] = [];
    const usedActions: Record<string, Array<{file: string, line: number, code: string}>> = {};
    
    // Buscar por todos os patterns de todas as actions
    for (const [actionName, patterns] of Object.entries(this.actionPatterns)) {
      if (verbose) {
        console.log(`🔍 Analisando action: ${actionName}`);
        console.log(`   🎨 Padrões gerados (${patterns.length}): ${patterns.join(', ')}`);
      }
      
      let hasUsage = false;
      const actionUsages: Array<{file: string, line: number, code: string}> = [];
      
      // Testar cada padrão com logging detalhado
      for (const pattern of patterns) {
        if (verbose) {
          console.log(`     🔎 Testando padrão: "${pattern}"`);
        }
        
        const usages = await this.findUsageInFiles(pattern);
        if (usages.length > 0) {
          hasUsage = true;
          actionUsages.push(...usages);
          if (verbose) {
            console.log(`     ✅ ${usages.length} uso(s) encontrado(s) para "${pattern}"`);
          }
        } else {
          if (verbose) {
            console.log(`     ❌ Nenhum uso para "${pattern}"`);
          }
        }
      }
      
      if (hasUsage) {
        usedActions[actionName] = actionUsages;
        if (verbose) {
          console.log(`   ✅ USADO - Total: ${actionUsages.length} ocorrência(s)\n`);
        }
      } else {
        unusedActions.push(actionName);
        if (verbose) {
          console.log(`   ❌ NÃO USADO - Nenhuma ocorrência encontrada\n`);
        }
      }
    }
    
    if (verbose) {
      console.log(`📊 RESUMO DA ANÁLISE:`);
      console.log(`   ✅ Actions usadas: ${Object.keys(usedActions).length}`);
      console.log(`   ❌ Actions não usadas: ${unusedActions.length}`);
      console.log(`   📈 Total: ${Object.keys(this.actionPatterns).length}\n`);
    }
    
    return {
      unusedActions,
      usedActions,
      totalActions: Object.keys(this.actionPatterns).length
    };
  }

  private async findUsageInFiles(pattern: string): Promise<Array<{file: string, line: number, code: string}>> {
    const allUsages: Array<{file: string, line: number, code: string}> = [];
    
    for (const filePath of this.sourceFiles) {
      const usages = await this.findUsageInFile(filePath, pattern);
      allUsages.push(...usages);
    }
    
    return allUsages;
  }

  private async findUsageInFile(filePath: string, pattern: string): Promise<Array<{file: string, line: number, code: string}>> {
    try {
      const content = fs.readFileSync(filePath, 'utf-8');
      const lines = content.split('\n');
      const usages: Array<{file: string, line: number, code: string}> = [];
      
      for (let i = 0; i < lines.length; i++) {
        const line = lines[i];
        const lineNumber = i + 1;
        
        // MÚLTIPLAS ESTRATÉGIAS DE BUSCA:
        
        // 1. Busca exata pelo padrão
        if (line.includes(pattern)) {
          usages.push({
            file: filePath,
            line: lineNumber,
            code: line.trim()
          });
          continue;
        }
        
        // 2. Busca por regex - padrões com pontos
        if (pattern.includes('.')) {
          // Escapar pontos para regex
          const regexPattern = pattern.replace(/\./g, '\\.');
          const regex = new RegExp(`\\b${regexPattern}\\b`);
          if (regex.test(line)) {
            usages.push({
              file: filePath,
              line: lineNumber,
              code: line.trim()
            });
            continue;
          }
        }
        
        // 3. Busca por partes do padrão (mais flexível)
        const patternParts = pattern.split('.');
        if (patternParts.length >= 2) {
          // Verificar se todas as partes estão na linha
          const allPartsPresent = patternParts.every(part => 
            line.toLowerCase().includes(part.toLowerCase())
          );
          
          if (allPartsPresent) {
            // Verificação adicional: deve ser código, não comentário
            const trimmedLine = line.trim();
            if (!trimmedLine.startsWith('//') && !trimmedLine.startsWith('/*') && !trimmedLine.startsWith('*')) {
              usages.push({
                file: filePath,
                line: lineNumber,
                code: line.trim()
              });
            }
          }
        }
        
        // 4. Busca por snake_case original (fallback)
        const snakeCasePattern = pattern.replace(/\./g, '_').replace(/([A-Z])/g, '_$1').toLowerCase().replace(/^_/, '');
        if (line.toLowerCase().includes(snakeCasePattern)) {
          usages.push({
            file: filePath,
            line: lineNumber,
            code: line.trim()
          });
        }
      }
      
      if (usages.length > 0) {
        console.log(`   ✅ Padrão "${pattern}" encontrado ${usages.length}x em ${filePath}`);
      }
      
      return usages;
    } catch (error) {
      console.warn(`⚠️  Erro ao analisar arquivo ${filePath}:`, error);
      return [];
    }
  }

  /**
   * Analisa uso de actions customizadas da Clint no código
   * Detecta padrões como: clint.owner.getName, clint.user.createAccount
   */
  analyzeClintUsage(sourceCode: string, filePath: string): Array<{pattern: string, line: number, column: number}> {
    const usages: Array<{pattern: string, line: number, column: number}> = [];
    const lines = sourceCode.split('\n');
    
    for (let i = 0; i < lines.length; i++) {
      const line = lines[i];
      const lineNumber = i + 1;
      
      // Detectar padrões clint.*.* 
      const clintMatches = line.matchAll(/clint\.([a-zA-Z_]+)\.([a-zA-Z_]+)/g);
      
      for (const match of clintMatches) {
        const fullPattern = match[0]; // clint.owner.getName
        const column = match.index || 0;
        
        usages.push({
          pattern: fullPattern,
          line: lineNumber,
          column: column
        });
      }
    }
    
    return usages;
  }

  /**
   * Mapeia uso de padrões Clint para actions do Hasura
   */
  mapClintUsageToActions(clintUsages: Array<{pattern: string, line: number, column: number}>): Array<{actionName: string, line: number, column: number}> {
    return clintUsages.map(usage => ({
      actionName: this.clintPatternToAction(usage.pattern),
      line: usage.line,
      column: usage.column
    }));
  }

  /**
   * Converte padrão Clint para nome de action do Hasura
   * Exemplo: clint.owner.getName -> owner_get_name
   */
  private clintPatternToAction(pattern: string): string {
    const match = pattern.match(/^clint\.([^.]+)\.([^.]+)$/);
    if (!match) {
      return pattern.replace('clint.', '');
    }
    
    const [, entity, method] = match;
    
    // Converter camelCase para snake_case
    const actionPart = method.replace(/([A-Z])/g, '_$1').toLowerCase();
    
    return `${entity}_${actionPart}`;
  }
}

export { ClintAction, ClintUsageResult };
