#!/usr/bin/env node
/* eslint-disable no-case-declarations */
/* eslint-disable no-await-in-loop */
/* eslint-disable no-plusplus */
/* eslint-disable no-shadow */
/* eslint-disable no-restricted-syntax */

/**
 * Utility Scripts for Deployment Management
 * Provides standalone functions for rollback, health check, and other operations
 */
import { execSync } from 'child_process';
import fs from 'fs';
import path, { dirname } from 'path';
import { fileURLToPath } from 'url';
import http from 'http';
import { config } from 'dotenv';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

// Colors for console output
const colors = {
  reset: '\x1b[0m',
  red: '\x1b[31m',
  green: '\x1b[32m',
  yellow: '\x1b[33m',
  blue: '\x1b[34m',
  cyan: '\x1b[36m',
};

// Load environment configuration
function loadEnvConfig(environment) {
  const envFiles = [`.env.${environment}.local`, `.env.${environment}`, '.env.local', '.env'];

  for (const envFile of envFiles) {
    const envPath = path.join(process.cwd(), envFile);
    if (fs.existsSync(envPath)) {
      console.log(`${colors.blue}📄 Loading config from: ${envFile}${colors.reset}`);
      config({ path: envPath });
      return true;
    }
  }
  return false;
}

// Get configuration from environment
const getConfig = (environment) => ({
  APP_NAME: process.env.APP_NAME || `app-${environment}`,
  APP_PORT: parseInt(process.env.APP_PORT || '9020', 10),
  FINAL_DIST_DIR: process.env.FINAL_DIST_DIR || 'dist',
  BACKUP_DIST_DIR: process.env.BACKUP_DIST_DIR || 'dist_backup',
  HEALTH_CHECK_PATH: process.env.HEALTH_CHECK_PATH || '/',
  HEALTH_CHECK_RETRIES: parseInt(process.env.HEALTH_CHECK_RETRIES || '5', 10),
  HEALTH_CHECK_INTERVAL: parseInt(process.env.HEALTH_CHECK_INTERVAL || '2000', 10),
  HEALTH_CHECK_TIMEOUT: parseInt(process.env.HEALTH_CHECK_TIMEOUT || '5000', 10),
  LOG_DIR: process.env.LOG_DIR || 'logs',
  ENVIRONMENT: environment,
});

// Execute shell command
function executeCommand(command, silent = false) {
  if (!silent) {
    console.log(`${colors.cyan}⚙️  Executing: ${command}${colors.reset}`);
  }
  try {
    const output = execSync(command, { encoding: 'utf8', stdio: silent ? 'pipe' : 'inherit' });
    return output ? output.trim() : '';
  } catch (error) {
    if (!silent) {
      console.error(`${colors.red}❌ Error: ${error.message}${colors.reset}`);
    }
    return null;
  }
}

// Health check function
async function performHealthCheck(config, verbose = true) {
  if (verbose) {
    console.log('');
    console.log('═'.repeat(60));
    console.log(`${colors.blue}🏥 HEALTH CHECK${colors.reset}`);
    console.log('═'.repeat(60));
    console.log(`Environment: ${config.ENVIRONMENT}`);
    console.log(`URL: http://localhost:${config.APP_PORT}${config.HEALTH_CHECK_PATH}`);
    console.log(`Retries: ${config.HEALTH_CHECK_RETRIES}`);
    console.log(`Interval: ${config.HEALTH_CHECK_INTERVAL}ms`);
    console.log('─'.repeat(60));
  }

  for (let i = 0; i < config.HEALTH_CHECK_RETRIES; i++) {
    try {
      const startTime = Date.now();

      await new Promise((resolve, reject) => {
        const url = `http://localhost:${config.APP_PORT}${config.HEALTH_CHECK_PATH}`;
        const req = http.get(url, (res) => {
          const responseTime = Date.now() - startTime;

          if (res.statusCode === 200) {
            if (verbose) {
              console.log(
                `${colors.green}✅ Health check passed (attempt ${i + 1}/${
                  config.HEALTH_CHECK_RETRIES
                })${colors.reset}`
              );
              console.log(`   Status: ${res.statusCode} | Response time: ${responseTime}ms`);
            }
            resolve(true);
          } else {
            reject(new Error(`Status code: ${res.statusCode}`));
          }
        });

        req.on('error', (error) => {
          reject(error);
        });

        req.setTimeout(config.HEALTH_CHECK_TIMEOUT, () => {
          req.destroy();
          reject(new Error('Timeout'));
        });
      });

      if (verbose) {
        console.log('─'.repeat(60));
        console.log(`${colors.green}✅ HEALTH CHECK SUCCESSFUL${colors.reset}`);
        console.log('═'.repeat(60));
        console.log('');
      }

      return true;
    } catch (error) {
      if (verbose) {
        console.log(
          `${colors.yellow}⚠️  Health check failed (attempt ${i + 1}/${
            config.HEALTH_CHECK_RETRIES
          })${colors.reset}`
        );
        console.log(`   Error: ${error.message}`);
      }

      if (i < config.HEALTH_CHECK_RETRIES - 1) {
        if (verbose) {
          console.log(`   Waiting ${config.HEALTH_CHECK_INTERVAL}ms before retry...`);
        }
        await new Promise((resolve) => setTimeout(resolve, config.HEALTH_CHECK_INTERVAL));
      }
    }
  }

  if (verbose) {
    console.log('─'.repeat(60));
    console.log(`${colors.red}❌ HEALTH CHECK FAILED${colors.reset}`);
    console.log(`All ${config.HEALTH_CHECK_RETRIES} attempts failed`);
    console.log('═'.repeat(60));
    console.log('');
  }

  return false;
}

// Rollback function
async function performRollback(config, verbose = true) {
  if (verbose) {
    console.log('');
    console.log('═'.repeat(60));
    console.log(`${colors.yellow}🔙 ROLLBACK${colors.reset}`);
    console.log('═'.repeat(60));
    console.log(`Environment: ${config.ENVIRONMENT}`);
    console.log(`App Name: ${config.APP_NAME}`);
    console.log('─'.repeat(60));
  }

  const finalPath = path.join(process.cwd(), config.FINAL_DIST_DIR);
  const backupPath = path.join(process.cwd(), config.BACKUP_DIST_DIR);

  // Check if backup exists
  if (!fs.existsSync(backupPath)) {
    console.error(`${colors.red}❌ No backup found at: ${backupPath}${colors.reset}`);
    console.log('');
    console.log('Cannot rollback without a backup.');
    console.log('═'.repeat(60));
    return false;
  }

  try {
    // Remove current dist
    if (fs.existsSync(finalPath)) {
      if (verbose) {
        console.log(`🗑️  Removing current: ${config.FINAL_DIST_DIR}`);
      }
      fs.rmSync(finalPath, { recursive: true, force: true });
    }

    // Restore backup
    if (verbose) {
      console.log(`📦 Restoring backup: ${config.BACKUP_DIST_DIR} → ${config.FINAL_DIST_DIR}`);
    }
    fs.renameSync(backupPath, finalPath);

    // Reload PM2
    if (verbose) {
      console.log(`🔄 Reloading PM2: ${config.APP_NAME}`);
    }

    const reloadResult = executeCommand(`pm2 reload ${config.APP_NAME}`, !verbose);

    if (reloadResult === null) {
      console.error(`${colors.red}❌ Failed to reload PM2${colors.reset}`);
      return false;
    }

    // Wait for app to stabilize
    if (verbose) {
      console.log('⏳ Waiting for app to stabilize...');
    }
    await new Promise((resolve) => setTimeout(resolve, 3000));

    // Verify rollback with health check
    if (verbose) {
      console.log('🏥 Verifying rollback...');
    }

    const isHealthy = await performHealthCheck(config, verbose);

    if (isHealthy) {
      console.log('─'.repeat(60));
      console.log(`${colors.green}✅ ROLLBACK SUCCESSFUL${colors.reset}`);
      console.log('Application has been restored to previous version');
      console.log('═'.repeat(60));
      console.log('');
      return true;
    }
    console.log('─'.repeat(60));
    console.log(`${colors.red}❌ ROLLBACK COMPLETED BUT HEALTH CHECK FAILED${colors.reset}`);
    console.log('Previous version restored but application is not responding');
    console.log('═'.repeat(60));
    console.log('');
    return false;
  } catch (error) {
    console.error(`${colors.red}❌ Rollback failed: ${error.message}${colors.reset}`);
    console.log('═'.repeat(60));
    console.log('');
    return false;
  }
}

// Check PM2 app status
async function checkAppStatus(config, verbose = true) {
  if (verbose) {
    console.log('');
    console.log('═'.repeat(60));
    console.log(`${colors.blue}📊 APPLICATION STATUS${colors.reset}`);
    console.log('═'.repeat(60));
    console.log(`Environment: ${config.ENVIRONMENT}`);
    console.log(`App Name: ${config.APP_NAME}`);
    console.log('─'.repeat(60));
  }

  try {
    const output = executeCommand(`pm2 jlist`, true);

    if (!output) {
      console.log(`${colors.red}❌ Failed to get PM2 status${colors.reset}`);
      return false;
    }

    const processes = JSON.parse(output);
    const app = processes.find((p) => p.name === config.APP_NAME);

    if (!app) {
      console.log(`${colors.yellow}⚠️  App "${config.APP_NAME}" not found in PM2${colors.reset}`);
      console.log('');
      console.log('Available PM2 apps:');
      processes.forEach((p) => {
        console.log(`  - ${p.name} (status: ${p.pm2_env.status})`);
      });
      console.log('═'.repeat(60));
      console.log('');
      return false;
    }

    // Display app info
    const { status } = app.pm2_env;
    const uptime = app.pm2_env.pm_uptime;
    const memory = (app.monit.memory / 1024 / 1024).toFixed(2);
    const { cpu } = app.monit;
    const restarts = app.pm2_env.restart_time;

    const statusColor = status === 'online' ? colors.green : colors.red;

    console.log(`Status: ${statusColor}${status.toUpperCase()}${colors.reset}`);
    console.log(`PID: ${app.pid || 'N/A'}`);
    console.log(`Uptime: ${uptime ? new Date(uptime).toLocaleString() : 'N/A'}`);
    console.log(`Memory: ${memory} MB`);
    console.log(`CPU: ${cpu}%`);
    console.log(`Restarts: ${restarts}`);
    console.log(`Instances: ${app.pm2_env.instances || 1}`);
    console.log(`Mode: ${app.pm2_env.exec_mode || 'fork'}`);

    if (verbose) {
      console.log('─'.repeat(60));

      // Run health check
      const isHealthy = await performHealthCheck(config, false);

      if (isHealthy) {
        console.log(`Health: ${colors.green}✅ HEALTHY${colors.reset}`);
      } else {
        console.log(`Health: ${colors.red}❌ UNHEALTHY${colors.reset}`);
      }
    }

    console.log('═'.repeat(60));
    console.log('');

    return status === 'online';
  } catch (error) {
    console.error(`${colors.red}❌ Error checking status: ${error.message}${colors.reset}`);
    console.log('═'.repeat(60));
    console.log('');
    return false;
  }
}

// List all deployed apps
async function listApps(verbose = true) {
  console.log('');
  console.log('═'.repeat(60));
  console.log(`${colors.blue}📋 PM2 APPLICATIONS${colors.reset}`);
  console.log('═'.repeat(60));

  try {
    const output = executeCommand(`pm2 jlist`, true);

    if (!output) {
      console.log(`${colors.red}❌ Failed to get PM2 list${colors.reset}`);
      return;
    }

    const processes = JSON.parse(output);

    if (processes.length === 0) {
      console.log(`${colors.yellow}⚠️  No PM2 applications running${colors.reset}`);
      console.log('═'.repeat(60));
      console.log('');
      return;
    }

    console.log('');
    processes.forEach((app, index) => {
      const { status } = app.pm2_env;
      const statusColor = status === 'online' ? colors.green : colors.red;
      const memory = (app.monit.memory / 1024 / 1024).toFixed(2);

      console.log(`${index + 1}. ${colors.cyan}${app.name}${colors.reset}`);
      console.log(`   Status: ${statusColor}${status}${colors.reset}`);
      console.log(`   PID: ${app.pid || 'N/A'}`);
      console.log(`   Memory: ${memory} MB`);
      console.log(`   CPU: ${app.monit.cpu}%`);
      console.log(`   Restarts: ${app.pm2_env.restart_time}`);
      console.log('');
    });

    console.log('═'.repeat(60));
    console.log('');
  } catch (error) {
    console.error(`${colors.red}❌ Error listing apps: ${error.message}${colors.reset}`);
  }
}

// Cleanup old backups
function cleanupBackups(config, verbose = true) {
  if (verbose) {
    console.log('');
    console.log('═'.repeat(60));
    console.log(`${colors.blue}🧹 CLEANUP BACKUPS${colors.reset}`);
    console.log('═'.repeat(60));
  }

  const backupPath = path.join(process.cwd(), config.BACKUP_DIST_DIR);

  if (fs.existsSync(backupPath)) {
    if (verbose) {
      console.log(`Removing backup: ${config.BACKUP_DIST_DIR}`);
    }
    fs.rmSync(backupPath, { recursive: true, force: true });
    console.log(`${colors.green}✅ Backup removed successfully${colors.reset}`);
  } else {
    console.log(`${colors.yellow}⚠️  No backup found${colors.reset}`);
  }

  if (verbose) {
    console.log('═'.repeat(60));
    console.log('');
  }
}

// Main CLI handler
async function main() {
  const args = process.argv.slice(2);
  const command = args[0];
  const environment = args[1] || 'production';

  // Validate environment
  const validEnvironments = ['development', 'staging', 'production'];
  if (!validEnvironments.includes(environment)) {
    console.error(`${colors.red}❌ Invalid environment: ${environment}${colors.reset}`);
    console.log(`Valid environments: ${validEnvironments.join(', ')}`);
    process.exit(1);
  }

  // Load environment configuration
  loadEnvConfig(environment);
  const config = getConfig(environment);

  // Execute command
  switch (command) {
    case 'health-check':
    case 'health':
      const healthResult = await performHealthCheck(config);
      process.exit(healthResult ? 0 : 1);
      break;

    case 'rollback':
      const rollbackResult = await performRollback(config);
      process.exit(rollbackResult ? 0 : 1);
      break;

    case 'status':
      const statusResult = await checkAppStatus(config);
      process.exit(statusResult ? 0 : 1);
      break;

    case 'list':
      await listApps();
      break;

    case 'cleanup':
      cleanupBackups(config);
      break;

    default:
      console.log('');
      console.log('═'.repeat(60));
      console.log(`${colors.blue}🛠️  DEPLOYMENT UTILITIES${colors.reset}`);
      console.log('═'.repeat(60));
      console.log('');
      console.log('Usage: node utils.js <command> [environment]');
      console.log('');
      console.log('Commands:');
      console.log('  health-check    Check application health');
      console.log('  rollback        Rollback to previous version');
      console.log('  status          Show application status');
      console.log('  list            List all PM2 applications');
      console.log('  cleanup         Remove backup files');
      console.log('');
      console.log('Environments:');
      console.log('  development');
      console.log('  staging');
      console.log('  production (default)');
      console.log('');
      console.log('Examples:');
      console.log(`  ${colors.cyan}node utils.js health-check production${colors.reset}`);
      console.log(`  ${colors.cyan}node utils.js rollback staging${colors.reset}`);
      console.log(`  ${colors.cyan}node utils.js status development${colors.reset}`);
      console.log(`  ${colors.cyan}node utils.js list${colors.reset}`);
      console.log('');
      console.log('Or use package.json scripts:');
      console.log(`  ${colors.cyan}yarn health:prod${colors.reset}`);
      console.log(`  ${colors.cyan}yarn rollback:prod${colors.reset}`);
      console.log(`  ${colors.cyan}yarn status:prod${colors.reset}`);
      console.log('═'.repeat(60));
      console.log('');
      process.exit(1);
  }
}

// Run if called directly
if (process.argv[1] === fileURLToPath(import.meta.url)) {
  main();
}

export { performHealthCheck, performRollback, checkAppStatus, listApps, cleanupBackups };
