import { ConfigManager } from '../../config/config-manager.js';
import { 
  DeprecationNotice, 
  DeprecationPlan, 
  AffectedObject, 
  UpgradeStep,
  DeprecationConfig,
  MigrationResult,
  DeprecationRegistry
} from './types.js';
import { promises as fs } from 'fs';
import path from 'path';

export class DeprecationManager {
  private configManager: ConfigManager;
  private registryPath: string;
  private config: DeprecationConfig;

  constructor(configManager: ConfigManager) {
    this.configManager = configManager;
    this.registryPath = '';
    this.config = {
      requireApproval: true,
      archiveDeprecated: true,
      autoMigrate: false,
      notificationLevel: 'detailed'
    };
  }

  async init(): Promise<void> {
    const storageManager = this.configManager.getStorageManager();
    this.registryPath = await storageManager.getModuleDataPath('deprecation', 'deprecation-registry.json');
    
    await this.ensureRegistry();
  }

  private async ensureRegistry(): Promise<void> {
    try {
      await fs.access(this.registryPath);
    } catch {
      const registry: DeprecationRegistry = {
        notices: [],
        lastUpdated: new Date().toISOString(),
        version: '1.0.0'
      };
      // Ensure directory exists
      const dir = path.dirname(this.registryPath);
      await fs.mkdir(dir, { recursive: true });
      await fs.writeFile(this.registryPath, JSON.stringify(registry, null, 2));
    }
  }

  async markAsDeprecated(notice: Omit<DeprecationNotice, 'id' | 'deprecatedAt'>): Promise<DeprecationNotice> {
    const registry = await this.loadRegistry();
    
    const fullNotice: DeprecationNotice = {
      ...notice,
      id: this.generateId(),
      deprecatedAt: new Date().toISOString()
    };
    
    registry.notices.push(fullNotice);
    registry.lastUpdated = new Date().toISOString();
    
    await this.saveRegistry(registry);
    return fullNotice;
  }

  async createDeprecationPlan(objectIds: string[]): Promise<DeprecationPlan> {
    const registry = await this.loadRegistry();
    const relevantNotices = registry.notices.filter(n => objectIds.includes(n.objectId));
    
    const affectedObjects = await this.analyzeAffectedObjects(relevantNotices);
    const upgradeSteps = this.generateUpgradeSteps(relevantNotices, affectedObjects);
    const estimatedImpact = this.calculateImpact(affectedObjects);
    
    return {
      notices: relevantNotices,
      affectedObjects,
      estimatedImpact,
      requiresUserApproval: this.config.requireApproval,
      upgradeSteps
    };
  }

  async executeUpgrade(plan: DeprecationPlan, stepIds?: number[]): Promise<MigrationResult> {
    const result: MigrationResult = {
      success: false,
      migratedObjects: [],
      failedObjects: [],
      errors: [],
      rollbackAvailable: false
    };

    if (plan.requiresUserApproval && !this.config.autoMigrate) {
      result.errors.push('User approval required for this upgrade');
      return result;
    }

    const stepsToExecute = stepIds 
      ? plan.upgradeSteps.filter(s => stepIds.includes(s.order))
      : plan.upgradeSteps;

    const backupLocation = await this.createBackup(plan);
    result.backupLocation = backupLocation;
    result.rollbackAvailable = true;

    for (const step of stepsToExecute) {
      try {
        if (step.automated) {
          await this.executeAutomatedStep(step, plan);
          result.migratedObjects.push(`Step ${step.order}: ${step.description}`);
        } else {
          result.errors.push(`Step ${step.order} requires manual execution: ${step.description}`);
        }
      } catch (error) {
        result.failedObjects.push(`Step ${step.order}`);
        result.errors.push(`Step ${step.order} failed: ${(error as Error).message}`);
        break;
      }
    }

    result.success = result.failedObjects.length === 0;
    return result;
  }

  async checkDeprecations(objectType?: string): Promise<DeprecationNotice[]> {
    const registry = await this.loadRegistry();
    
    if (objectType) {
      return registry.notices.filter(n => n.objectType === objectType);
    }
    
    return registry.notices;
  }

  async generateUpgradeGuide(noticeId: string): Promise<string> {
    const registry = await this.loadRegistry();
    const notice = registry.notices.find(n => n.id === noticeId);
    
    if (!notice) {
      throw new Error('Deprecation notice not found');
    }

    return `# Upgrade Guide for ${notice.objectType} (${notice.objectId})

## Deprecation Details
- **Deprecated At**: ${new Date(notice.deprecatedAt).toLocaleDateString()}
- **Deprecated By**: ${notice.deprecatedBy}
- **Reason**: ${notice.reason}

## Upgrade Instructions
${notice.upgradeInstructions}

${notice.alternativeId ? `## Alternative Object
- **ID**: ${notice.alternativeId}
- **Path**: ${notice.alternativePath || 'N/A'}` : ''}

## Automated Upgrade
To automatically upgrade this object, run:
\`\`\`bash
atlas upgrade --notice-id ${notice.id}
\`\`\`

## Manual Upgrade Steps
${this.generateManualSteps(notice)}

## Rollback
If you need to rollback after upgrade:
\`\`\`bash
atlas rollback --notice-id ${notice.id}
\`\`\`
`;
  }

  private async analyzeAffectedObjects(notices: DeprecationNotice[]): Promise<AffectedObject[]> {
    const affected: AffectedObject[] = [];
    
    for (const notice of notices) {
      affected.push({
        type: notice.objectType,
        id: notice.objectId,
        name: `${notice.objectType}-${notice.objectId}`,
        dependencies: await this.findDependencies(notice),
        currentLocation: notice.objectPath || 'unknown',
        proposedAction: notice.alternativeId ? 'migrate' : 'archive'
      });
    }
    
    return affected;
  }

  private generateUpgradeSteps(notices: DeprecationNotice[], affected: AffectedObject[]): UpgradeStep[] {
    const steps: UpgradeStep[] = [];
    let order = 1;

    steps.push({
      order: order++,
      description: 'Create backup of current state',
      automated: true,
      reversible: true,
      estimatedDuration: '30s'
    });

    for (const obj of affected) {
      if (obj.proposedAction === 'migrate') {
        steps.push({
          order: order++,
          description: `Migrate ${obj.type} ${obj.id} to new structure`,
          automated: true,
          reversible: true,
          estimatedDuration: '1m'
        });
      } else if (obj.proposedAction === 'archive') {
        steps.push({
          order: order++,
          description: `Archive ${obj.type} ${obj.id}`,
          automated: true,
          reversible: true,
          estimatedDuration: '30s'
        });
      }
    }

    steps.push({
      order: order++,
      description: 'Update references and dependencies',
      automated: true,
      reversible: false,
      estimatedDuration: '2m'
    });

    return steps;
  }

  private calculateImpact(affected: AffectedObject[]): 'low' | 'medium' | 'high' {
    const totalDependencies = affected.reduce((sum, obj) => sum + obj.dependencies.length, 0);
    
    if (totalDependencies > 10 || affected.length > 5) return 'high';
    if (totalDependencies > 5 || affected.length > 2) return 'medium';
    return 'low';
  }

  private async createBackup(plan: DeprecationPlan): Promise<string> {
    const storageManager = this.configManager.getStorageManager();
    const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
    const backupFile = await storageManager.getModuleDataPath('deprecation', `backups/backup-${timestamp}.json`);
    
    const backup = {
      plan,
      timestamp: new Date().toISOString(),
      originalData: {} // Would include actual data in real implementation
    };
    
    await fs.mkdir(path.dirname(backupFile), { recursive: true });
    await fs.writeFile(backupFile, JSON.stringify(backup, null, 2));
    
    return backupFile;
  }

  private async executeAutomatedStep(step: UpgradeStep, plan: DeprecationPlan): Promise<void> {
    if (step.command) {
      // Would execute command in real implementation
      console.log(`Executing: ${step.command}`);
    }
    
    // Simulate step execution
    await new Promise(resolve => setTimeout(resolve, 1000));
  }

  private async findDependencies(notice: DeprecationNotice): Promise<string[]> {
    // Would analyze actual dependencies in real implementation
    return [];
  }

  private generateManualSteps(notice: DeprecationNotice): string {
    const steps = [
      '1. Export the current object data',
      '2. Create a new object with updated structure',
      '3. Migrate data to new object',
      '4. Update all references',
      '5. Test the migration',
      '6. Archive the old object'
    ];
    
    return steps.join('\n');
  }

  private generateId(): string {
    return `dep_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
  }

  private async loadRegistry(): Promise<DeprecationRegistry> {
    const content = await fs.readFile(this.registryPath, 'utf-8');
    return JSON.parse(content);
  }

  private async saveRegistry(registry: DeprecationRegistry): Promise<void> {
    await fs.writeFile(this.registryPath, JSON.stringify(registry, null, 2));
  }

  updateConfig(config: Partial<DeprecationConfig>): void {
    this.config = { ...this.config, ...config };
  }
}