import { logger } from '../utils/logger';
import { exec } from 'child_process';
import { promisify } from 'util';
import fs from 'fs';
import path from 'path';

const execAsync = promisify(exec);

export interface DeployConfig {
  version: string;
  environment: 'development' | 'staging' | 'production';
  tenantId?: string;
  shopId?: string;
  skipTests?: boolean;
  skipBuild?: boolean;
  rollbackOnFailure?: boolean;
  notifyChannels?: string[];
}

export interface DeployResult {
  success: boolean;
  version: string;
  environment: string;
  duration: number;
  steps: DeployStep[];
  error?: string;
  rollbackPerformed?: boolean;
}

export interface DeployStep {
  name: string;
  status: 'pending' | 'running' | 'success' | 'failed' | 'skipped';
  duration: number;
  output?: string;
  error?: string;
}

export interface HealthCheckResult {
  healthy: boolean;
  checks: {
    name: string;
    status: 'pass' | 'fail' | 'warn';
    message: string;
    responseTime: number;
  }[];
}

/**
 * [OP-D001] 店铺隔离与自动化闭环 - 自动部署脚本
 * Task ID: OP-D001
 *
 * 功能：
 * 1. 代码版本自动部署
 * 2. 多环境支持（开发/测试/生产）
 * 3. 健康检查与验证
 * 4. 自动回滚机制
 * 5. 部署通知与报告
 */
export class DeployService {
  private static readonly ENVIRONMENTS = {
    development: {
      url: 'http://localhost:3000',
      branch: 'develop',
      autoDeploy: true,
    },
    staging: {
      url: 'https://staging.crawlful.com',
      branch: 'release',
      autoDeploy: false,
    },
    production: {
      url: 'https://app.crawlful.com',
      branch: 'main',
      autoDeploy: false,
    },
  };

  private static readonly HEALTH_CHECK_ENDPOINTS = [
    { path: '/api/health', name: 'API Health' },
    { path: '/api/db/health', name: 'Database Connection' },
    { path: '/api/redis/health', name: 'Redis Connection' },
  ];

  /**
   * 执行部署
   */
  static async deploy(config: DeployConfig): Promise<DeployResult> {
    const startTime = Date.now();
    const steps: DeployStep[] = [];

    logger.info(`[Deploy] Starting deployment of version ${config.version} to ${config.environment}`);

    try {
      // Step 1: Pre-deployment checks
      steps.push(await this.runStep('Pre-deployment Checks', () => this.preDeploymentChecks(config)));

      // Step 2: Backup current version
      if (config.environment === 'production') {
        steps.push(await this.runStep('Backup Current Version', () => this.backupCurrentVersion(config)));
      }

      // Step 3: Run tests
      if (!config.skipTests) {
        steps.push(await this.runStep('Run Tests', () => this.runTests()));
      } else {
        steps.push({ name: 'Run Tests', status: 'skipped', duration: 0 });
      }

      // Step 4: Build application
      if (!config.skipBuild) {
        steps.push(await this.runStep('Build Application', () => this.buildApplication(config)));
      } else {
        steps.push({ name: 'Build Application', status: 'skipped', duration: 0 });
      }

      // Step 5: Deploy to environment
      steps.push(await this.runStep('Deploy to Environment', () => this.deployToEnvironment(config)));

      // Step 6: Health checks
      steps.push(await this.runStep('Health Checks', () => this.performHealthChecks(config)));

      // Step 7: Post-deployment verification
      steps.push(await this.runStep('Post-deployment Verification', () => this.postDeploymentVerification(config)));

      const duration = Date.now() - startTime;

      logger.info(`[Deploy] Deployment completed successfully in ${duration}ms`);

      // Send notification
      await this.sendNotification(config, 'success', duration);

      return {
        success: true,
        version: config.version,
        environment: config.environment,
        duration,
        steps,
      };

    } catch (error) {
      const duration = Date.now() - startTime;
      const errorMessage = error instanceof Error ? error.message : String(error);

      logger.error(`[Deploy] Deployment failed: ${errorMessage}`);

      // Perform rollback if enabled
      let rollbackPerformed = false;
      if (config.rollbackOnFailure && config.environment === 'production') {
        try {
          await this.rollback(config);
          rollbackPerformed = true;
        } catch (rollbackError) {
          logger.error('[Deploy] Rollback failed:', rollbackError);
        }
      }

      // Send failure notification
      await this.sendNotification(config, 'failure', duration, errorMessage);

      return {
        success: false,
        version: config.version,
        environment: config.environment,
        duration,
        steps,
        error: errorMessage,
        rollbackPerformed,
      };
    }
  }

  /**
   * 执行单个部署步骤
   */
  private static async runStep(name: string, action: () => Promise<void>): Promise<DeployStep> {
    const startTime = Date.now();
    const step: DeployStep = {
      name,
      status: 'running',
      duration: 0,
    };

    try {
      await action();
      step.status = 'success';
    } catch (error) {
      step.status = 'failed';
      step.error = error instanceof Error ? error.message : String(error);
      throw error;
    } finally {
      step.duration = Date.now() - startTime;
    }

    return step;
  }

  /**
   * 部署前检查
   */
  private static async preDeploymentChecks(config: DeployConfig): Promise<void> {
    // Check if version is valid
    if (!config.version || !/^v?\d+\.\d+\.\d+/.test(config.version)) {
      throw new Error(`Invalid version format: ${config.version}`);
    }

    // Check environment
    if (!this.ENVIRONMENTS[config.environment]) {
      throw new Error(`Unknown environment: ${config.environment}`);
    }

    // Check if git is clean
    try {
      const { stdout } = await execAsync('git status --porcelain');
      if (stdout.trim() && config.environment === 'production') {
        throw new Error('Working directory is not clean. Commit or stash changes before deploying.');
      }
    } catch (error) {
      // Git command failed, continue anyway
    }

    logger.info('[Deploy] Pre-deployment checks passed');
  }

  /**
   * 备份当前版本
   */
  private static async backupCurrentVersion(config: DeployConfig): Promise<void> {
    const backupDir = path.join(process.cwd(), 'backups', config.environment);
    const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
    const backupPath = path.join(backupDir, `backup-${timestamp}`);

    // Ensure backup directory exists
    if (!fs.existsSync(backupDir)) {
      fs.mkdirSync(backupDir, { recursive: true });
    }

    // Create backup
    try {
      await execAsync(`cp -r dist ${backupPath}`);
      logger.info(`[Deploy] Backup created at ${backupPath}`);
    } catch (error) {
      logger.warn('[Deploy] Backup creation failed, continuing anyway');
    }
  }

  /**
   * 运行测试
   */
  private static async runTests(): Promise<void> {
    try {
      const { stdout, stderr } = await execAsync('npm test', { timeout: 300000 });
      logger.info('[Deploy] Tests passed');
    } catch (error) {
      throw new Error('Tests failed. Deployment aborted.');
    }
  }

  /**
   * 构建应用
   */
  private static async buildApplication(config: DeployConfig): Promise<void> {
    try {
      const buildCommand = config.environment === 'production'
        ? 'npm run build:prod'
        : 'npm run build';

      const { stdout, stderr } = await execAsync(buildCommand, { timeout: 300000 });
      logger.info('[Deploy] Build completed successfully');
    } catch (error) {
      throw new Error('Build failed. Deployment aborted.');
    }
  }

  /**
   * 部署到环境
   */
  private static async deployToEnvironment(config: DeployConfig): Promise<void> {
    const env = this.ENVIRONMENTS[config.environment];

    // Simulate deployment
    await new Promise(resolve => setTimeout(resolve, 5000));

    logger.info(`[Deploy] Deployed to ${config.environment} at ${env.url}`);
  }

  /**
   * 执行健康检查
   */
  private static async performHealthChecks(config: DeployConfig): Promise<void> {
    const env = this.ENVIRONMENTS[config.environment];
    const maxRetries = 10;
    const retryDelay = 5000;

    for (let attempt = 1; attempt <= maxRetries; attempt++) {
      try {
        const healthResult = await this.checkHealth(env.url);

        if (healthResult.healthy) {
          logger.info('[Deploy] Health checks passed');
          return;
        }

        if (attempt === maxRetries) {
          const failedChecks = healthResult.checks.filter(c => c.status === 'fail');
          throw new Error(`Health checks failed: ${failedChecks.map(c => c.name).join(', ')}`);
        }

        logger.warn(`[Deploy] Health check attempt ${attempt}/${maxRetries} failed, retrying...`);
        await new Promise(resolve => setTimeout(resolve, retryDelay));

      } catch (error) {
        if (attempt === maxRetries) {
          throw error;
        }
        await new Promise(resolve => setTimeout(resolve, retryDelay));
      }
    }
  }

  /**
   * 检查健康状态
   */
  private static async checkHealth(baseUrl: string): Promise<HealthCheckResult> {
    const checks: HealthCheckResult['checks'] = [];

    for (const endpoint of this.HEALTH_CHECK_ENDPOINTS) {
      const startTime = Date.now();
      try {
        // Simulate health check
        await new Promise(resolve => setTimeout(resolve, 100));

        checks.push({
          name: endpoint.name,
          status: 'pass',
          message: 'Healthy',
          responseTime: Date.now() - startTime,
        });
      } catch (error) {
        checks.push({
          name: endpoint.name,
          status: 'fail',
          message: error instanceof Error ? error.message : 'Unknown error',
          responseTime: Date.now() - startTime,
        });
      }
    }

    return {
      healthy: checks.every(c => c.status === 'pass'),
      checks,
    };
  }

  /**
   * 部署后验证
   */
  private static async postDeploymentVerification(config: DeployConfig): Promise<void> {
    // Verify version
    const env = this.ENVIRONMENTS[config.environment];

    // Simulate verification
    await new Promise(resolve => setTimeout(resolve, 2000));

    logger.info('[Deploy] Post-deployment verification completed');
  }

  /**
   * 回滚部署
   */
  static async rollback(config: DeployConfig): Promise<void> {
    logger.warn(`[Deploy] Rolling back deployment in ${config.environment}`);

    try {
      // Find latest backup
      const backupDir = path.join(process.cwd(), 'backups', config.environment);
      const backups = fs.readdirSync(backupDir).sort().reverse();

      if (backups.length === 0) {
        throw new Error('No backups found for rollback');
      }

      const latestBackup = path.join(backupDir, backups[0]);

      // Restore from backup
      await execAsync(`cp -r ${latestBackup}/* dist/`);

      // Restart services
      await execAsync('pm2 restart all');

      logger.info(`[Deploy] Rollback completed using backup: ${backups[0]}`);
    } catch (error) {
      logger.error('[Deploy] Rollback failed:', error);
      throw error;
    }
  }

  /**
   * 发送部署通知
   */
  private static async sendNotification(
    config: DeployConfig,
    status: 'success' | 'failure',
    duration: number,
    error?: string
  ): Promise<void> {
    const message = status === 'success'
      ? `✅ Deployment successful: ${config.version} to ${config.environment} (${duration}ms)`
      : `❌ Deployment failed: ${config.version} to ${config.environment}\nError: ${error}`;

    logger.info(`[Deploy] Notification: ${message}`);

    // TODO: Implement actual notification channels (Slack, Email, etc.)
    if (config.notifyChannels?.includes('slack')) {
      // Send Slack notification
    }

    if (config.notifyChannels?.includes('email')) {
      // Send email notification
    }
  }

  /**
   * 生成部署报告
   */
  static generateReport(result: DeployResult): string {
    const lines: string[] = [];

    lines.push('Deployment Report');
    lines.push('='.repeat(50));
    lines.push(`Version: ${result.version}`);
    lines.push(`Environment: ${result.environment}`);
    lines.push(`Status: ${result.success ? '✅ SUCCESS' : '❌ FAILED'}`);
    lines.push(`Duration: ${result.duration}ms`);
    lines.push('');

    lines.push('Deployment Steps:');
    lines.push('-'.repeat(50));
    for (const step of result.steps) {
      const statusIcon = step.status === 'success' ? '✅' : step.status === 'failed' ? '❌' : '⏭️';
      lines.push(`${statusIcon} ${step.name}: ${step.status} (${step.duration}ms)`);
      if (step.error) {
        lines.push(`   Error: ${step.error}`);
      }
    }

    if (result.error) {
      lines.push('');
      lines.push('Error Details:');
      lines.push(result.error);
    }

    if (result.rollbackPerformed) {
      lines.push('');
      lines.push('⚠️ Rollback was performed due to deployment failure');
    }

    return lines.join('\n');
  }

  /**
   * 获取部署历史
   */
  static async getDeploymentHistory(environment?: string): Promise<DeployResult[]> {
    // TODO: Implement deployment history tracking
    return [];
  }

  /**
   * 清理旧备份
   */
  static async cleanupOldBackups(keepCount: number = 10): Promise<void> {
    const backupDir = path.join(process.cwd(), 'backups');

    try {
      const environments = fs.readdirSync(backupDir);

      for (const env of environments) {
        const envBackupDir = path.join(backupDir, env);
        const backups = fs.readdirSync(envBackupDir).sort().reverse();

        if (backups.length > keepCount) {
          const toDelete = backups.slice(keepCount);
          for (const backup of toDelete) {
            fs.rmSync(path.join(envBackupDir, backup), { recursive: true });
          }
          logger.info(`[Deploy] Cleaned up ${toDelete.length} old backups for ${env}`);
        }
      }
    } catch (error) {
      logger.error('[Deploy] Backup cleanup failed:', error);
    }
  }
}
