import AWS from 'aws-sdk';
import { EventEmitter } from 'events';
import { createHash } from 'crypto';
import { gzip, gunzip } from 'zlib';
import { promisify } from 'util';
import { LocalStorageClient } from './localStorageClient.js';

const gzipAsync = promisify(gzip);
const gunzipAsync = promisify(gunzip);

/**
 * DigitalOcean Spaces Client for AI-to-AI Data Storage
 * Handles compressed data storage, user analytics, and license management
 */
export class DigitalOceanClient extends EventEmitter {
  constructor() {
    super();
    
    // DigitalOcean Spaces configuration
    this.spacesEndpoint = new AWS.Endpoint('sgp1.digitaloceanspaces.com');

    // Use environment variables or demo credentials
    const accessKeyId = process.env.DO_SPACES_ACCESS_KEY || 'DEMO_ACCESS_KEY';
    const secretAccessKey = process.env.DO_SPACES_SECRET_KEY || 'DEMO_SECRET_KEY';

    this.s3 = new AWS.S3({
      endpoint: this.spacesEndpoint,
      accessKeyId,
      secretAccessKey,
      region: 'sgp1',
      s3ForcePathStyle: false,
      signatureVersion: 'v4'
    });

    this.bucketName = process.env.DO_SPACES_BUCKET || 'quizapp';
    this.dataPrefix = 'zai-ai-to-ai-data';

    // Check if we should use local storage instead
    this.useLocalStorage = accessKeyId === 'DEMO_ACCESS_KEY' || process.env.USE_LOCAL_STORAGE === 'true';

    // Initialize local storage client as fallback
    this.localClient = new LocalStorageClient();

    if (this.useLocalStorage) {
      console.log('💾 Using local storage mode');
    } else {
      console.log('🌊 DigitalOcean Spaces Client initialized');
    }
  }

  /**
   * Test connection to DigitalOcean Spaces or local storage
   */
  async testConnection() {
    if (this.useLocalStorage) {
      return await this.localClient.testConnection();
    }

    try {
      console.log('🧪 Testing DigitalOcean Spaces connection...');

      const testData = {
        timestamp: new Date().toISOString(),
        test: 'connection-test',
        message: 'ZAI AI-to-AI System Connection Test'
      };

      await this.uploadData('test/connection-test.json', testData);
      console.log('✅ DigitalOcean Spaces connection successful!');

      return true;
    } catch (error) {
      console.error('❌ DigitalOcean Spaces connection failed, falling back to local storage');
      this.useLocalStorage = true;
      return await this.localClient.testConnection();
    }
  }

  /**
   * Upload compressed data to DigitalOcean Spaces or local storage
   */
  async uploadData(key, data, options = {}) {
    if (this.useLocalStorage) {
      return await this.localClient.uploadData(key, data, options);
    }

    try {
      const jsonData = JSON.stringify(data);
      const compressedData = await gzipAsync(jsonData);

      const params = {
        Bucket: this.bucketName,
        Key: `${this.dataPrefix}/${key}`,
        Body: compressedData,
        ContentType: 'application/json',
        ContentEncoding: 'gzip',
        Metadata: {
          'original-size': jsonData.length.toString(),
          'compressed-size': compressedData.length.toString(),
          'compression-ratio': (compressedData.length / jsonData.length).toFixed(3),
          ...options.metadata
        }
      };

      const result = await this.s3.upload(params).promise();

      console.log(`📤 Data uploaded: ${key} (${jsonData.length} → ${compressedData.length} bytes, ${((1 - compressedData.length / jsonData.length) * 100).toFixed(1)}% compression)`);

      this.emit('dataUploaded', { key, originalSize: jsonData.length, compressedSize: compressedData.length });

      return result;
    } catch (error) {
      console.error(`❌ Upload failed for ${key}, falling back to local storage:`, error.message);
      this.useLocalStorage = true;
      return await this.localClient.uploadData(key, data, options);
    }
  }

  /**
   * Download and decompress data from DigitalOcean Spaces or local storage
   */
  async downloadData(key) {
    if (this.useLocalStorage) {
      return await this.localClient.downloadData(key);
    }

    try {
      const params = {
        Bucket: this.bucketName,
        Key: `${this.dataPrefix}/${key}`
      };

      const result = await this.s3.getObject(params).promise();
      const decompressedData = await gunzipAsync(result.Body);
      const data = JSON.parse(decompressedData.toString());

      console.log(`📥 Data downloaded: ${key}`);

      return data;
    } catch (error) {
      if (error.code === 'NoSuchKey') {
        return null;
      }
      console.error(`❌ Download failed for ${key}, trying local storage:`, error.message);
      this.useLocalStorage = true;
      return await this.localClient.downloadData(key);
    }
  }

  /**
   * Store AI-to-AI conversation data
   */
  async storeConversation(userId, conversationData) {
    const userHash = this.hashUserId(userId);
    const timestamp = new Date().toISOString();
    const date = timestamp.split('T')[0];
    
    const key = `conversations/${date}/${userHash}/${Date.now()}.json`;
    
    const data = {
      timestamp,
      userId: userHash,
      conversation: conversationData,
      metadata: {
        messageCount: conversationData.messages?.length || 0,
        duration: conversationData.duration || 0,
        model: conversationData.model || 'unknown',
        success: conversationData.success || false
      }
    };
    
    return await this.uploadData(key, data);
  }

  /**
   * Store user metrics and analytics
   */
  async storeUserMetrics(userId, metrics) {
    const userHash = this.hashUserId(userId);
    const timestamp = new Date().toISOString();
    const date = timestamp.split('T')[0];
    
    const key = `metrics/${date}/${userHash}.json`;
    
    // Try to get existing metrics for the day
    let existingMetrics = await this.downloadData(key);
    if (!existingMetrics) {
      existingMetrics = {
        date,
        userId: userHash,
        sessions: [],
        totalUsage: {
          conversations: 0,
          totalMessages: 0,
          totalDuration: 0,
          successRate: 0
        }
      };
    }
    
    // Add new session
    existingMetrics.sessions.push({
      timestamp,
      ...metrics
    });
    
    // Update totals
    existingMetrics.totalUsage.conversations++;
    existingMetrics.totalUsage.totalMessages += metrics.messageCount || 0;
    existingMetrics.totalUsage.totalDuration += metrics.duration || 0;
    
    const successfulSessions = existingMetrics.sessions.filter(s => s.success).length;
    existingMetrics.totalUsage.successRate = successfulSessions / existingMetrics.sessions.length;
    
    return await this.uploadData(key, existingMetrics);
  }

  /**
   * Store license information
   */
  async storeLicense(licenseData) {
    if (this.useLocalStorage) {
      return await this.localClient.storeLicense(licenseData);
    }

    const key = `licenses/active/${licenseData.licenseId}.json`;

    const data = {
      ...licenseData,
      createdAt: licenseData.createdAt || new Date().toISOString(),
      lastUpdated: new Date().toISOString()
    };

    return await this.uploadData(key, data);
  }

  /**
   * Get all licenses
   */
  async getAllLicenses() {
    if (this.useLocalStorage) {
      return await this.localClient.getAllLicenses();
    }

    try {
      const params = {
        Bucket: this.bucketName,
        Prefix: `${this.dataPrefix}/licenses/active/`,
      };

      const result = await this.s3.listObjectsV2(params).promise();
      const licenses = [];

      for (const obj of result.Contents || []) {
        const key = obj.Key.replace(`${this.dataPrefix}/`, '');
        const license = await this.downloadData(key);
        if (license) {
          licenses.push(license);
        }
      }

      return licenses.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt));
    } catch (error) {
      console.error('❌ Failed to get all licenses:', error.message);
      this.useLocalStorage = true;
      return await this.localClient.getAllLicenses();
    }
  }

  /**
   * Delete a license
   */
  async deleteLicense(licenseId) {
    if (this.useLocalStorage) {
      return await this.localClient.deleteLicense(licenseId);
    }

    try {
      const key = `${this.dataPrefix}/licenses/active/${licenseId}.json`;

      const params = {
        Bucket: this.bucketName,
        Key: key
      };

      await this.s3.deleteObject(params).promise();
      console.log(`🗑️ License deleted from DigitalOcean: ${licenseId}`);

      return { success: true, message: 'License deleted successfully' };
    } catch (error) {
      console.error(`❌ Failed to delete license ${licenseId}:`, error.message);
      this.useLocalStorage = true;
      return await this.localClient.deleteLicense(licenseId);
    }
  }

  /**
   * Get user's conversation history
   */
  async getUserConversations(userId, dateRange = null) {
    const userHash = this.hashUserId(userId);
    const conversations = [];
    
    try {
      // List objects with user prefix
      const params = {
        Bucket: this.bucketName,
        Prefix: `${this.dataPrefix}/conversations/`,
        Delimiter: '/'
      };
      
      const result = await this.s3.listObjectsV2(params).promise();
      
      // Filter and download user's conversations
      for (const obj of result.Contents || []) {
        if (obj.Key.includes(userHash)) {
          const key = obj.Key.replace(`${this.dataPrefix}/`, '');
          const conversation = await this.downloadData(key);
          if (conversation) {
            conversations.push(conversation);
          }
        }
      }
      
      return conversations.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp));
    } catch (error) {
      console.error(`❌ Failed to get conversations for user ${userHash}:`, error.message);
      return [];
    }
  }

  /**
   * Get aggregated analytics data
   */
  async getAnalytics(dateRange = null) {
    if (this.useLocalStorage) {
      return await this.localClient.getAnalytics(dateRange);
    }

    try {
      const params = {
        Bucket: this.bucketName,
        Prefix: `${this.dataPrefix}/metrics/`,
        Delimiter: '/'
      };

      const result = await this.s3.listObjectsV2(params).promise();
      const analytics = {
        totalUsers: 0,
        totalConversations: 0,
        totalMessages: 0,
        averageSuccessRate: 0,
        dailyStats: {}
      };

      const userMetrics = new Set();
      let totalSuccessRate = 0;
      let metricsCount = 0;

      for (const obj of result.Contents || []) {
        const key = obj.Key.replace(`${this.dataPrefix}/`, '');
        const metrics = await this.downloadData(key);

        if (metrics) {
          userMetrics.add(metrics.userId);
          analytics.totalConversations += metrics.totalUsage.conversations;
          analytics.totalMessages += metrics.totalUsage.totalMessages;
          totalSuccessRate += metrics.totalUsage.successRate;
          metricsCount++;

          analytics.dailyStats[metrics.date] = analytics.dailyStats[metrics.date] || {
            users: 0,
            conversations: 0,
            messages: 0
          };

          analytics.dailyStats[metrics.date].users++;
          analytics.dailyStats[metrics.date].conversations += metrics.totalUsage.conversations;
          analytics.dailyStats[metrics.date].messages += metrics.totalUsage.totalMessages;
        }
      }

      analytics.totalUsers = userMetrics.size;
      analytics.averageSuccessRate = metricsCount > 0 ? totalSuccessRate / metricsCount : 0;

      return analytics;
    } catch (error) {
      console.error('❌ Failed to get analytics, falling back to local storage:', error.message);
      this.useLocalStorage = true;
      return await this.localClient.getAnalytics(dateRange);
    }
  }

  /**
   * Hash user ID for privacy
   */
  hashUserId(userId) {
    return createHash('sha256').update(userId.toString()).digest('hex').substring(0, 16);
  }

  /**
   * Clean up old data (data retention policy)
   */
  async cleanupOldData(retentionDays = 90) {
    try {
      const cutoffDate = new Date();
      cutoffDate.setDate(cutoffDate.getDate() - retentionDays);
      
      const params = {
        Bucket: this.bucketName,
        Prefix: `${this.dataPrefix}/conversations/`,
      };
      
      const result = await this.s3.listObjectsV2(params).promise();
      const objectsToDelete = [];
      
      for (const obj of result.Contents || []) {
        if (obj.LastModified < cutoffDate) {
          objectsToDelete.push({ Key: obj.Key });
        }
      }
      
      if (objectsToDelete.length > 0) {
        const deleteParams = {
          Bucket: this.bucketName,
          Delete: {
            Objects: objectsToDelete
          }
        };
        
        await this.s3.deleteObjects(deleteParams).promise();
        console.log(`🗑️ Cleaned up ${objectsToDelete.length} old data files`);
      }
      
      return objectsToDelete.length;
    } catch (error) {
      console.error('❌ Cleanup failed:', error.message);
      throw error;
    }
  }
}
