#!/usr/bin/env node

/**
 * AutoLlama Smart Setup Script
 * 🦙 Intelligent environment detection and configuration
 */

const fs = require('fs-extra');
const path = require('path');
const { execSync } = require('child_process');
const chalk = require('chalk');
const ora = require('ora');

class AutoLlamaSetup {
  constructor() {
    this.projectRoot = path.join(__dirname, '..');
    this.capabilities = {};
    this.config = {};
  }

  async run() {
    console.log(chalk.cyan.bold('\n🦙 AutoLlama Setup'));
    console.log(chalk.gray('Intelligent environment configuration\n'));

    try {
      // 1. Detect environment
      await this.detectEnvironment();
      
      // 2. Load existing configuration
      await this.loadConfiguration();
      
      // 3. Check dependencies
      await this.checkDependencies();
      
      // 4. Setup database
      await this.setupDatabase();
      
      // 5. Initialize services
      await this.initializeServices();
      
      // 6. Run migrations
      await this.runMigrations();
      
      // 7. Verify installation
      await this.verifyInstallation();
      
      console.log(chalk.green.bold('\n✅ Setup complete!'));
      this.printNextSteps();
      
    } catch (error) {
      console.error(chalk.red.bold('\n❌ Setup failed:'), error.message);
      this.suggestFixes(error);
      process.exit(1);
    }
  }

  async detectEnvironment() {
    const spinner = ora('Detecting environment...').start();
    
    this.capabilities = {
      hasDocker: await this.checkCommand('docker --version'),
      hasDockerCompose: await this.checkCommand('docker compose version'),
      hasPostgres: await this.checkCommand('psql --version'),
      hasNode: true,
      nodeVersion: process.version,
      hasPython: await this.checkCommand('python3 --version'),
      hasGit: await this.checkCommand('git --version'),
      platform: process.platform,
      arch: process.arch,
      memory: Math.round(require('os').totalmem() / (1024 * 1024 * 1024)) + 'GB'
    };
    
    // Determine optimal deployment mode
    if (this.capabilities.hasDocker && this.capabilities.hasDockerCompose) {
      this.deploymentMode = 'docker';
      spinner.succeed('Docker environment detected - optimal setup available');
    } else if (this.capabilities.hasPostgres) {
      this.deploymentMode = 'hybrid';
      spinner.succeed('PostgreSQL detected - native setup available');
    } else {
      this.deploymentMode = 'local';
      spinner.succeed('Local development mode - using SQLite');
    }
    
    // Display capabilities
    console.log(chalk.gray('\nEnvironment:'));
    console.log(chalk.gray(`  • Node.js:    ${this.capabilities.nodeVersion}`));
    console.log(chalk.gray(`  • Platform:   ${this.capabilities.platform} (${this.capabilities.arch})`));
    console.log(chalk.gray(`  • Memory:     ${this.capabilities.memory}`));
    if (this.capabilities.hasDocker) {
      console.log(chalk.gray(`  • Docker:     ✓ Available`));
    }
    if (this.capabilities.hasPostgres) {
      console.log(chalk.gray(`  • PostgreSQL: ✓ Available`));
    }
  }

  async loadConfiguration() {
    const spinner = ora('Loading configuration...').start();
    
    // Check for existing .env file
    const envPath = path.join(this.projectRoot, '.env');
    if (await fs.pathExists(envPath)) {
      require('dotenv').config({ path: envPath });
      spinner.succeed('Configuration loaded from .env');
    } else {
      // Create default .env
      await this.createDefaultEnv();
      spinner.succeed('Created default configuration');
    }
    
    // Load autollama.config.js if exists
    const configPath = path.join(this.projectRoot, 'autollama.config.js');
    if (await fs.pathExists(configPath)) {
      this.config = require(configPath);
    }
  }

  async createDefaultEnv() {
    const envContent = `# AutoLlama Configuration
# Generated by setup script

# Deployment Mode
DEPLOYMENT_MODE=${this.deploymentMode}

# AI Provider (set your API key)
OPENAI_API_KEY=your_openai_api_key_here
AI_PROVIDER=openai

# Database
${this.deploymentMode === 'local' ? 
`DATABASE_TYPE=sqlite
DATABASE_PATH=./data/autollama.db` :
`DATABASE_URL=postgresql://autollama:autollama@localhost:5432/autollama`}

# Vector Database
QDRANT_URL=http://localhost:6333

# RAG Configuration
ENABLE_CONTEXTUAL_EMBEDDINGS=true
CONTEXT_GENERATION_BATCH_SIZE=5
`;
    
    await fs.writeFile(path.join(this.projectRoot, '.env'), envContent);
  }

  async checkDependencies() {
    const spinner = ora('Checking dependencies...').start();
    
    // Check if node_modules exists
    const nodeModulesPath = path.join(this.projectRoot, 'node_modules');
    if (!await fs.pathExists(nodeModulesPath)) {
      spinner.text = 'Installing dependencies...';
      execSync('npm install', { cwd: this.projectRoot, stdio: 'pipe' });
    }
    
    // Check for required dependencies based on deployment mode
    if (this.deploymentMode === 'local') {
      // Check for SQLite
      try {
        require('sqlite3');
        spinner.succeed('All dependencies installed');
      } catch {
        spinner.text = 'Installing SQLite...';
        execSync('npm install sqlite3', { cwd: this.projectRoot, stdio: 'pipe' });
        spinner.succeed('Dependencies installed');
      }
    } else {
      // Check for PostgreSQL client
      try {
        require('pg');
        spinner.succeed('All dependencies installed');
      } catch {
        spinner.text = 'Installing PostgreSQL client...';
        execSync('npm install pg', { cwd: this.projectRoot, stdio: 'pipe' });
        spinner.succeed('Dependencies installed');
      }
    }
  }

  async setupDatabase() {
    const spinner = ora('Setting up database...').start();
    
    if (this.deploymentMode === 'docker') {
      spinner.text = 'Docker mode - database will be created by compose';
      spinner.succeed('Database configuration ready');
      return;
    }
    
    if (this.deploymentMode === 'local') {
      // Create SQLite database directory
      const dataDir = path.join(this.projectRoot, 'data');
      await fs.ensureDir(dataDir);
      spinner.succeed('SQLite database directory created');
    } else {
      // Check PostgreSQL connection
      try {
        const { Pool } = require('pg');
        const pool = new Pool({ connectionString: process.env.DATABASE_URL });
        await pool.query('SELECT 1');
        await pool.end();
        spinner.succeed('PostgreSQL connection verified');
      } catch (error) {
        spinner.warn('PostgreSQL not accessible - will retry on first run');
      }
    }
  }

  async initializeServices() {
    const spinner = ora('Initializing services...').start();
    
    // Create necessary directories
    const dirs = ['data', 'uploads', 'logs', 'temp'];
    for (const dir of dirs) {
      await fs.ensureDir(path.join(this.projectRoot, dir));
    }
    
    // Setup service configurations based on deployment mode
    if (this.deploymentMode === 'local') {
      // Create local service configs
      const servicesConfig = {
        bm25: {
          enabled: true,
          mode: 'embedded',
          port: 3002
        },
        vector: {
          enabled: true,
          mode: 'embedded',
          provider: 'qdrant-local'
        },
        api: {
          port: 3001,
          cors: true
        },
        frontend: {
          port: 8080,
          proxy: 'http://localhost:3001'
        }
      };
      
      await fs.writeFile(
        path.join(this.projectRoot, 'services.config.json'),
        JSON.stringify(servicesConfig, null, 2)
      );
    }
    
    spinner.succeed('Services initialized');
  }

  async runMigrations() {
    const spinner = ora('Running database migrations...').start();
    
    try {
      if (this.deploymentMode !== 'docker') {
        const migrationPath = path.join(this.projectRoot, 'api', 'run-migrations.js');
        if (await fs.pathExists(migrationPath)) {
          const MigrationRunner = require(migrationPath);
          const runner = new MigrationRunner();
          await runner.runMigrations();
          spinner.succeed('Database migrations complete');
        } else {
          spinner.warn('Migration runner not found - will run on first start');
        }
      } else {
        spinner.succeed('Migrations will run when Docker starts');
      }
    } catch (error) {
      spinner.warn('Migrations will complete on first run');
    }
  }

  async verifyInstallation() {
    const spinner = ora('Verifying installation...').start();
    
    const checks = {
      envFile: await fs.pathExists(path.join(this.projectRoot, '.env')),
      nodeModules: await fs.pathExists(path.join(this.projectRoot, 'node_modules')),
      dataDir: await fs.pathExists(path.join(this.projectRoot, 'data')),
      apiDir: await fs.pathExists(path.join(this.projectRoot, 'api')),
      frontendDir: await fs.pathExists(path.join(this.projectRoot, 'config', 'react-frontend'))
    };
    
    const failed = Object.entries(checks)
      .filter(([, status]) => !status)
      .map(([name]) => name);
    
    if (failed.length > 0) {
      spinner.fail(`Missing components: ${failed.join(', ')}`);
      throw new Error('Installation verification failed');
    }
    
    spinner.succeed('Installation verified');
  }

  printNextSteps() {
    console.log(chalk.cyan('\n📚 Next Steps:'));
    
    if (this.deploymentMode === 'docker') {
      console.log(chalk.white('  1. Run: npm run docker:up'));
      console.log(chalk.white('  2. Open: http://localhost:8080'));
    } else {
      console.log(chalk.white('  1. Run: npm run dev'));
      console.log(chalk.white('  2. Open: http://localhost:8080'));
    }
    
    if (!process.env.OPENAI_API_KEY || process.env.OPENAI_API_KEY === 'your_openai_api_key_here') {
      console.log(chalk.yellow('\n⚠️  Don\'t forget to set your OpenAI API key in .env'));
    }
    
    console.log(chalk.gray('\n📖 Documentation: https://github.com/autollama/autollama'));
  }

  suggestFixes(error) {
    console.log(chalk.yellow('\n💡 Suggestions:'));
    
    if (error.message.includes('EACCES')) {
      console.log('  • Try running with sudo');
      console.log('  • Check file permissions');
    } else if (error.message.includes('ENOENT')) {
      console.log('  • Some files may be missing');
      console.log('  • Try re-running the installer');
    } else if (error.message.includes('npm')) {
      console.log('  • Update npm: npm install -g npm');
      console.log('  • Clear cache: npm cache clean --force');
    } else if (error.message.includes('postgres')) {
      console.log('  • Install PostgreSQL or use Docker mode');
      console.log('  • Check DATABASE_URL in .env');
    }
  }

  async checkCommand(command) {
    try {
      execSync(command, { stdio: 'ignore' });
      return true;
    } catch {
      return false;
    }
  }
}

// Run setup
if (require.main === module) {
  const setup = new AutoLlamaSetup();
  setup.run();
}

module.exports = AutoLlamaSetup;