#!/usr/bin/env node

// 🎯 CREATE-DOMAIN V2.0 - API ANALYZER
// Análise automática de APIs para geração inteligente de interfaces TypeScript

const https = require('https');
const http = require('http');

/**
 * 🚀 ANALISADOR HÍBRIDO DE API
 * Implementa 4 estratégias para detectar automaticamente a estrutura dos dados:
 * 1. OpenAPI/Swagger
 * 2. API Response Analysis
 * 3. Smart Detection
 * 4. Intelligent Fallback
 */
class APIAnalyzer {
  constructor(baseUrl = 'https://prafrota-be-bff-tenant-api.grupopra.tech', headers = {}) {
    this.baseUrl = baseUrl;
    this.timeout = 10000; // 10 segundos
    this.customHeaders = headers; // Headers customizados para autenticação
  }

  /**
   * 🎯 MÉTODO PRINCIPAL - Análise Híbrida (MODO RIGOROSO)
   */
  async analyzeAPI(domainName, strictMode = true) {
    console.log(`🔍 Iniciando análise híbrida para domínio: ${domainName}`);
    console.log(`🔒 Modo rigoroso: ${strictMode ? 'ATIVADO' : 'DESATIVADO'}`);
    
    const results = {
      strategy: null,
      interface: null,
      fields: [],
      metadata: {},
      success: false
    };

    // ===== ESTRATÉGIA 1: OpenAPI/Swagger =====
    console.log('📋 Tentativa 1: OpenAPI/Swagger...');
    try {
      const swaggerResult = await this.analyzeOpenAPI(domainName);
      if (swaggerResult.success) {
        console.log('✅ OpenAPI/Swagger: Sucesso!');
        return { ...swaggerResult, strategy: 'openapi' };
      }
    } catch (error) {
      console.log(`⚠️ OpenAPI/Swagger falhou: ${error.message}`);
    }

    // ===== ESTRATÉGIA 2: Análise de Resposta (CRÍTICA) =====
    console.log('🔍 Tentativa 2: Análise de resposta da API...');
    try {
      const responseResult = await this.analyzeAPIResponse(domainName);
      if (responseResult.success) {
        console.log('✅ Análise de resposta: Sucesso!');
        return { ...responseResult, strategy: 'response_analysis' };
      } else if (strictMode && responseResult.endpointExists) {
        // 🚨 MODO RIGOROSO: Se endpoint existe mas falhou, PARAR execução
        const error = new Error(`🚨 ERRO CRÍTICO: Endpoint /${domainName}/ existe mas não conseguiu acessar dados. Verifique autenticação, CORS ou estrutura da resposta.`);
        error.code = 'ENDPOINT_ACCESS_FAILED';
        error.endpoint = responseResult.failedEndpoint;
        throw error;
      }
    } catch (error) {
      if (error.code === 'ENDPOINT_ACCESS_FAILED') {
        throw error; // Re-throw critical errors
      }
      console.log(`⚠️ Análise de resposta falhou: ${error.message}`);
    }

    // ===== ESTRATÉGIA 3: Detecção Inteligente =====
    console.log('🤖 Tentativa 3: Detecção inteligente...');
    try {
      const smartResult = await this.smartDetection(domainName);
      if (smartResult.success) {
        if (strictMode) {
          console.log('⚠️ AVISO: Usando Smart Detection em modo rigoroso - dados podem não ser precisos');
        }
        console.log('✅ Detecção inteligente: Sucesso!');
        return { ...smartResult, strategy: 'smart_detection' };
      }
    } catch (error) {
      console.log(`⚠️ Detecção inteligente falhou: ${error.message}`);
    }

    // ===== ESTRATÉGIA 4: Fallback Inteligente =====
    if (strictMode) {
      const error = new Error(`🚨 MODO RIGOROSO: Não foi possível obter dados reais da API para o domínio '${domainName}'. Todas as estratégias falharam.`);
      error.code = 'STRICT_MODE_FAILURE';
      throw error;
    }
    
    console.log('🔄 Estratégia 4: Fallback inteligente...');
    const fallbackResult = this.intelligentFallback(domainName);
    console.log('✅ Fallback aplicado com sucesso!');
    
    return { ...fallbackResult, strategy: 'intelligent_fallback' };
  }

  /**
   * 📋 ESTRATÉGIA 1: OpenAPI/Swagger Analysis
   */
  async analyzeOpenAPI(domainName) {
    const swaggerEndpoints = [
      `${this.baseUrl}/api-docs`,
      `${this.baseUrl}/swagger.json`,
      `${this.baseUrl}/openapi.json`,
      `${this.baseUrl}/docs/json`
    ];

    for (const endpoint of swaggerEndpoints) {
      try {
        console.log(`🔍 Testando endpoint: ${endpoint}`);
        const swaggerDoc = await this.fetchJSON(endpoint);
        
        if (swaggerDoc && swaggerDoc.components && swaggerDoc.components.schemas) {
          // Procurar schema do domínio
          const possibleSchemas = [
            `${this.capitalize(domainName)}`,
            `${this.capitalize(domainName)}Dto`,
            `${this.capitalize(domainName)}Entity`,
            `${this.capitalize(domainName)}Response`
          ];

          for (const schemaName of possibleSchemas) {
            const schema = swaggerDoc.components.schemas[schemaName];
            if (schema) {
              console.log(`✅ Schema encontrado: ${schemaName}`);
              return {
                success: true,
                interface: this.generateFromOpenAPISchema(domainName, schema),
                fields: this.extractFieldsFromSchema(schema),
                metadata: {
                  source: 'openapi',
                  schemaName: schemaName,
                  endpoint: endpoint
                }
              };
            }
          }
        }
      } catch (error) {
        console.log(`⚠️ Endpoint ${endpoint} falhou: ${error.message}`);
        continue;
      }
    }

    return { success: false };
  }

  /**
   * 🔍 ESTRATÉGIA 2: API Response Analysis (MODO RIGOROSO)
   */
  async analyzeAPIResponse(domainName) {
    // Testar tanto singular quanto plural
    const singularDomain = domainName.endsWith('s') ? domainName.slice(0, -1) : domainName;
    const pluralDomain = domainName.endsWith('s') ? domainName : `${domainName}s`;
    
    const apiEndpoints = [
      // Testar singular primeiro (mais comum em APIs REST)
      `${this.baseUrl}/${singularDomain}?page=1&limit=1`,
      `${this.baseUrl}/api/${singularDomain}?page=1&limit=1`,
      `${this.baseUrl}/${singularDomain}`,
      `${this.baseUrl}/api/${singularDomain}`,
      // Depois testar plural
      `${this.baseUrl}/${pluralDomain}?page=1&limit=1`,
      `${this.baseUrl}/api/${pluralDomain}?page=1&limit=1`,
      `${this.baseUrl}/${pluralDomain}`,
      `${this.baseUrl}/api/${pluralDomain}`
    ];

    let endpointExists = false;
    let failedEndpoint = null;

    for (const endpoint of apiEndpoints) {
      try {
        console.log(`🔍 Analisando endpoint: ${endpoint}`);
        const response = await this.fetchJSON(endpoint);
        
        if (response) {
          endpointExists = true; // Endpoint respondeu, existe
          
          // Verificar se tem estrutura de dados válida
          if (response.data && Array.isArray(response.data) && response.data.length > 0) {
            const sampleObject = response.data[0];
            console.log(`✅ Exemplo encontrado com ${Object.keys(sampleObject).length} campos`);
            
            return {
              success: true,
              interface: this.generateFromSample(domainName, sampleObject),
              fields: this.extractFieldsFromSample(sampleObject),
              metadata: {
                source: 'api_response',
                endpoint: endpoint,
                sampleSize: response.data.length,
                totalCount: response.totalCount || 'unknown'
              }
            };
          } else {
            // Endpoint existe mas não tem dados ou estrutura incorreta
            console.log(`⚠️ Endpoint ${endpoint} respondeu mas sem dados válidos`);
            failedEndpoint = endpoint;
            console.log(`📋 Estrutura da resposta:`, Object.keys(response));
            
            if (response.data) {
              console.log(`📦 response.data tipo: ${Array.isArray(response.data) ? 'array' : typeof response.data}`);
              console.log(`📈 response.data.length: ${response.data.length}`);
            }
          }
        }
      } catch (error) {
        if (error.message.includes('404') || error.message.includes('Not Found')) {
          console.log(`⚠️ Endpoint ${endpoint} não encontrado (404)`);
        } else {
          console.log(`⚠️ Endpoint ${endpoint} falhou: ${error.message}`);
          // Outros erros podem indicar que endpoint existe mas há problema de acesso
          if (!error.message.includes('Timeout') && !error.message.includes('ECONNREFUSED')) {
            endpointExists = true;
            failedEndpoint = endpoint;
          }
        }
        continue;
      }
    }

    return { 
      success: false, 
      endpointExists,
      failedEndpoint
    };
  }

  /**
   * 🤖 ESTRATÉGIA 3: Smart Detection
   */
  async smartDetection(domainName) {
    // Tentar detectar padrões baseados no nome do domínio
    const domainPatterns = this.getDomainPatterns(domainName);
    
    if (domainPatterns.length > 0) {
      return {
        success: true,
        interface: this.generateFromPatterns(domainName, domainPatterns),
        fields: domainPatterns,
        metadata: {
          source: 'smart_detection',
          patterns: domainPatterns.map(p => p.name)
        }
      };
    }

    return { success: false };
  }

  /**
   * 🔄 ESTRATÉGIA 4: Intelligent Fallback
   */
  intelligentFallback(domainName) {
    // Template base inteligente baseado em padrões comuns
    const baseFields = [
      { name: 'id', type: 'number', required: true, description: 'Identificador único' },
      { name: 'name', type: 'string', required: true, description: 'Nome do registro' },
      { name: 'description', type: 'string', required: false, description: 'Descrição opcional' },
      { name: 'status', type: 'string', required: false, description: 'Status do registro' },
      { name: 'created_at', type: 'string', required: false, description: 'Data de criação' },
      { name: 'updated_at', type: 'string', required: false, description: 'Data de atualização' }
    ];

    // Adicionar campos específicos baseados no nome do domínio
    const specificFields = this.getSpecificFieldsByDomain(domainName);
    const allFields = [...baseFields, ...specificFields];

    return {
      success: true,
      interface: this.generateFromFields(domainName, allFields),
      fields: allFields,
      metadata: {
        source: 'intelligent_fallback',
        baseFields: baseFields.length,
        specificFields: specificFields.length
      }
    };
  }

  /**
   * 🏗️ GERADORES DE INTERFACE
   */
  generateFromOpenAPISchema(domainName, schema) {
    const className = this.capitalize(domainName);
    let interfaceCode = `/**\n * 🎯 ${className} Interface - Generated from OpenAPI\n * \n * Auto-generated from API schema\n */\nexport interface ${className} {\n`;

    if (schema.properties) {
      Object.entries(schema.properties).forEach(([fieldName, fieldSchema]) => {
        const isRequired = schema.required && schema.required.includes(fieldName);
        const optional = isRequired ? '' : '?';
        const type = this.openAPITypeToTypeScript(fieldSchema);
        const description = fieldSchema.description ? ` // ${fieldSchema.description}` : '';
        
        interfaceCode += `  ${fieldName}${optional}: ${type};${description}\n`;
      });
    }

    interfaceCode += '}';
    return interfaceCode;
  }

  generateFromSample(domainName, sampleObject) {
    const className = this.capitalize(domainName);
    let interfaceCode = `/**\n * 🎯 ${className} Interface - Generated from API Response\n * \n * Auto-generated from real API data\n */\nexport interface ${className} {\n`;

    Object.entries(sampleObject).forEach(([key, value]) => {
      const type = this.detectTypeScript(value);
      const isOptional = value === null || value === undefined;
      const optional = isOptional ? '?' : '';
      const description = this.inferDescription(key, type);
      
      interfaceCode += `  ${key}${optional}: ${type}; // ${description}\n`;
    });

    interfaceCode += '}';
    return interfaceCode;
  }

  generateFromPatterns(domainName, patterns) {
    const className = this.capitalize(domainName);
    let interfaceCode = `/**\n * 🎯 ${className} Interface - Generated from Smart Patterns\n * \n * Auto-generated using intelligent pattern detection\n */\nexport interface ${className} {\n`;

    // 🔧 Sempre incluir campos base obrigatórios
    const baseFields = [
      { name: 'id', type: 'number', required: true, description: 'Identificador único' },
      { name: 'name', type: 'string', required: false, description: 'Nome do registro' },
      { name: 'status', type: 'string', required: false, description: 'Status do registro' },
      { name: 'created_at', type: 'string', required: false, description: 'Data de criação' },
      { name: 'updated_at', type: 'string', required: false, description: 'Data de atualização' }
    ];

    // Adicionar campos base primeiro
    baseFields.forEach(field => {
      const optional = field.required ? '' : '?';
      interfaceCode += `  ${field.name}${optional}: ${field.type}; // ${field.description}\n`;
    });

    // Depois adicionar campos específicos do domínio
    patterns.forEach(field => {
      const optional = field.required ? '' : '?';
      interfaceCode += `  ${field.name}${optional}: ${field.type}; // ${field.description}\n`;
    });

    interfaceCode += '}';
    return interfaceCode;
  }

  generateFromFields(domainName, fields) {
    const className = this.capitalize(domainName);
    let interfaceCode = `/**\n * 🎯 ${className} Interface - Intelligent Fallback\n * \n * Generated using intelligent fallback with domain-specific patterns\n */\nexport interface ${className} {\n`;

    fields.forEach(field => {
      const optional = field.required ? '' : '?';
      interfaceCode += `  ${field.name}${optional}: ${field.type}; // ${field.description}\n`;
    });

    interfaceCode += '}';
    return interfaceCode;
  }

  /**
   * 🔧 UTILITÁRIOS
   */
  async fetchJSON(url, timeout = this.timeout) {
    return new Promise((resolve, reject) => {
      const protocol = url.startsWith('https:') ? https : http;
      const timeoutId = setTimeout(() => reject(new Error('Timeout')), timeout);
      
      // Parse URL para adicionar headers
      const urlParts = new URL(url);
      
      const options = {
        hostname: urlParts.hostname,
        port: urlParts.port || (protocol === https ? 443 : 80),
        path: urlParts.pathname + urlParts.search,
        method: 'GET',
        headers: {
          'Accept': 'application/json',
          'User-Agent': 'API-Analyzer-V2.0',
          ...this.customHeaders // Adicionar headers customizados
        }
      };
      
      const req = protocol.request(options, (res) => {
        clearTimeout(timeoutId);
        let data = '';
        
        res.on('data', chunk => data += chunk);
        res.on('end', () => {
          try {
            resolve(JSON.parse(data));
          } catch (error) {
            reject(new Error('Invalid JSON'));
          }
        });
      });
      
      req.on('error', (error) => {
        clearTimeout(timeoutId);
        reject(error);
      });
      
      req.end();
    });
  }

  detectTypeScript(value) {
    if (value === null || value === undefined) return 'any';
    
    const type = typeof value;
    
    if (type === 'string') {
      // Detectar datas
      if (/^\d{4}-\d{2}-\d{2}/.test(value)) return 'string';
      // Detectar emails
      if (/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) return 'string';
      return 'string';
    }
    
    if (type === 'number') {
      return Number.isInteger(value) ? 'number' : 'number';
    }
    
    if (type === 'boolean') return 'boolean';
    
    if (Array.isArray(value)) {
      if (value.length === 0) return 'any[]';
      return `${this.detectTypeScript(value[0])}[]`;
    }
    
    if (type === 'object') {
      return 'any'; // Para objetos complexos
    }
    
    return 'any';
  }

  openAPITypeToTypeScript(schema) {
    if (schema.type === 'integer' || schema.type === 'number') return 'number';
    if (schema.type === 'string') return 'string';
    if (schema.type === 'boolean') return 'boolean';
    if (schema.type === 'array') {
      const itemType = schema.items ? this.openAPITypeToTypeScript(schema.items) : 'any';
      return `${itemType}[]`;
    }
    if (schema.type === 'object') return 'any';
    return 'any';
  }

  inferDescription(fieldName, type) {
    const descriptions = {
      id: 'Identificador único',
      name: 'Nome do registro',
      title: 'Título',
      description: 'Descrição',
      status: 'Status do registro',
      active: 'Se está ativo',
      created_at: 'Data de criação',
      updated_at: 'Data de atualização',
      deleted_at: 'Data de exclusão',
      email: 'Endereço de email',
      phone: 'Número de telefone',
      address: 'Endereço',
      price: 'Preço',
      amount: 'Quantidade/Valor',
      quantity: 'Quantidade',
      user_id: 'ID do usuário',
      company_id: 'ID da empresa'
    };
    
    return descriptions[fieldName] || `Campo ${fieldName} (${type})`;
  }

  getDomainPatterns(domainName) {
    const patterns = {
      // Padrões para veículos
      vehicle: [
        { name: 'brand', type: 'string', required: false, description: 'Marca do veículo' },
        { name: 'model', type: 'string', required: false, description: 'Modelo do veículo' },
        { name: 'year', type: 'number', required: false, description: 'Ano do veículo' },
        { name: 'plate', type: 'string', required: false, description: 'Placa do veículo' },
        { name: 'color', type: 'string', required: false, description: 'Cor do veículo' }
      ],
      
      // Padrões para usuários
      user: [
        { name: 'email', type: 'string', required: true, description: 'Email do usuário' },
        { name: 'password', type: 'string', required: false, description: 'Senha (hash)' },
        { name: 'role', type: 'string', required: false, description: 'Papel do usuário' },
        { name: 'avatar', type: 'string', required: false, description: 'URL do avatar' }
      ],
      
      // Padrões para produtos
      product: [
        { name: 'price', type: 'number', required: false, description: 'Preço do produto' },
        { name: 'category', type: 'string', required: false, description: 'Categoria do produto' },
        { name: 'stock', type: 'number', required: false, description: 'Estoque disponível' },
        { name: 'sku', type: 'string', required: false, description: 'Código SKU' }
      ],
      
      // Padrões para empresas
      company: [
        { name: 'cnpj', type: 'string', required: false, description: 'CNPJ da empresa' },
        { name: 'address', type: 'string', required: false, description: 'Endereço da empresa' },
        { name: 'phone', type: 'string', required: false, description: 'Telefone da empresa' }
      ]
    };
    
    // Buscar padrões exatos e similares
    const lowerDomain = domainName.toLowerCase();
    if (patterns[lowerDomain]) {
      return patterns[lowerDomain];
    }
    
    // Buscar padrões parciais
    for (const [pattern, fields] of Object.entries(patterns)) {
      if (lowerDomain.includes(pattern) || pattern.includes(lowerDomain)) {
        return fields;
      }
    }
    
    return [];
  }

  getSpecificFieldsByDomain(domainName) {
    // Retorna campos específicos baseados no nome do domínio
    const specificFields = this.getDomainPatterns(domainName);
    return specificFields;
  }

  extractFieldsFromSchema(schema) {
    if (!schema.properties) return [];
    
    return Object.entries(schema.properties).map(([name, fieldSchema]) => ({
      name,
      type: this.openAPITypeToTypeScript(fieldSchema),
      required: schema.required && schema.required.includes(name),
      description: fieldSchema.description || this.inferDescription(name, this.openAPITypeToTypeScript(fieldSchema))
    }));
  }

  extractFieldsFromSample(sampleObject) {
    return Object.entries(sampleObject).map(([name, value]) => ({
      name,
      type: this.detectTypeScript(value),
      required: value !== null && value !== undefined,
      description: this.inferDescription(name, this.detectTypeScript(value))
    }));
  }

  capitalize(str) {
    return str.charAt(0).toUpperCase() + str.slice(1);
  }
}

// Função principal para uso no create-domain-v2.js
async function analyzeAPIForDomain(domainName, baseUrl, strictMode = true) {
  const analyzer = new APIAnalyzer(baseUrl);
  return await analyzer.analyzeAPI(domainName, strictMode);
}

module.exports = {
  APIAnalyzer,
  analyzeAPIForDomain
}; 