/**
 * Update Management Module
 * Checks for Docker image updates, manages update scheduling,
 * and provides rollback capabilities
 */

const Docker = require('dockerode');
const EventEmitter = require('events');
const fs = require('fs');
const path = require('path');
const https = require('https');

const docker = new Docker();

const UPDATE_CONFIG_FILE = process.env.UPDATE_CONFIG_FILE || path.join(__dirname, 'update-config.json');
const UPDATE_HISTORY_FILE = process.env.UPDATE_HISTORY_FILE || path.join(__dirname, 'update-history.json');
const CHECK_INTERVAL = parseInt(process.env.UPDATE_CHECK_INTERVAL || '3600000', 10); // 1 hour

class UpdateManager extends EventEmitter {
  constructor() {
    super();
    this.config = this.loadConfig();
    this.history = this.loadHistory();
    this.availableUpdates = new Map();
    this.checking = false;
    this.checkInterval = null;
  }

  /**
   * Start update checking and auto-update scheduler
   */
  start() {
    if (this.checking) return;

    console.log('[UpdateManager] Starting update checks');
    this.checking = true;

    // Initial check
    this.checkForUpdates();

    // Schedule periodic checks
    this.checkInterval = setInterval(() => this.checkForUpdates(), CHECK_INTERVAL);

    // Start auto-update scheduler (checks every hour)
    this.startAutoUpdateScheduler();
  }

  /**
   * Stop update checking
   */
  stop() {
    if (!this.checking) return;

    console.log('[UpdateManager] Stopping update checks');
    this.checking = false;

    if (this.checkInterval) {
      clearInterval(this.checkInterval);
      this.checkInterval = null;
    }
    if (this.autoUpdateInterval) {
      clearInterval(this.autoUpdateInterval);
      this.autoUpdateInterval = null;
    }
  }

  /**
   * Trigger bundled workflows for an event
   */
  triggerWorkflows(eventType, eventData) {
    if (!this.workflowEngine) {
      console.log('[UpdateManager] Workflow engine not set, skipping workflow trigger');
      return;
    }
    
    try {
      this.workflowEngine.triggerForEvent(eventType, eventData)
        .then(results => {
          if (results && results.length > 0) {
            console.log(`[UpdateManager] Triggered ${results.length} workflow(s) for ${eventType}`);
          }
        })
        .catch(err => {
          console.error('[UpdateManager] Workflow trigger error:', err.message);
        });
    } catch (error) {
      console.error('[UpdateManager] Error triggering workflows:', error.message);
    }
  }

  /**
   * Set the workflow engine for triggering workflows
   */
  setWorkflowEngine(workflowEngine) {
    this.workflowEngine = workflowEngine;
    console.log('[UpdateManager] Workflow engine configured');
  }

  /**
   * Check for updates for all containers
   */
  async checkForUpdates() {
    try {
      const containers = await docker.listContainers({ all: true });
      
      for (const containerInfo of containers) {
        try {
          const container = docker.getContainer(containerInfo.Id);
          const inspect = await container.inspect();
          
          const imageName = inspect.Config.Image;
          const currentDigest = inspect.Image;
          
          // Check if update available
          const latestDigest = await this.getLatestImageDigest(imageName);
          
          if (latestDigest && latestDigest !== currentDigest) {
            this.availableUpdates.set(containerInfo.Id, {
              containerId: containerInfo.Id,
              containerName: containerInfo.Names[0].replace(/^\//, ''),
              imageName,
              currentDigest: currentDigest.substring(0, 12),
              latestDigest: latestDigest.substring(0, 12),
              currentTag: this.extractTag(imageName),
              detectedAt: new Date().toISOString()
            });
            
            this.emit('update-available', this.availableUpdates.get(containerInfo.Id));
          } else {
            this.availableUpdates.delete(containerInfo.Id);
          }
        } catch (error) {
          console.error(`[UpdateManager] Error checking ${containerInfo.Names[0]}:`, error.message);
        }
      }
      
      console.log(`[UpdateManager] Found ${this.availableUpdates.size} updates available`);
    } catch (error) {
      console.error('[UpdateManager] Error checking for updates:', error.message);
    }
  }

  /**
   * Get latest image digest from registry
   */
  async getLatestImageDigest(imageName) {
    try {
      // Parse image name
      const [repository, tag] = imageName.split(':');
      const imageTag = tag || 'latest';
      
      // For Docker Hub images
      if (!repository.includes('/') || repository.split('/').length === 2) {
        return await this.getDockerHubDigest(repository, imageTag);
      }
      
      // For other registries (would need authentication)
      console.warn(`[UpdateManager] Custom registry not yet supported: ${repository}`);
      return null;
    } catch (error) {
      console.error(`[UpdateManager] Error getting digest for ${imageName}:`, error.message);
      return null;
    }
  }

  /**
   * Get image digest from Docker Hub
   */
  async getDockerHubDigest(repository, tag) {
    return new Promise((resolve, reject) => {
      // Normalize repository name
      const repo = repository.includes('/') ? repository : `library/${repository}`;
      
      const options = {
        hostname: 'registry-1.docker.io',
        path: `/v2/${repo}/manifests/${tag}`,
        method: 'GET',
        headers: {
          'Accept': 'application/vnd.docker.distribution.manifest.v2+json'
        }
      };

      const req = https.request(options, (res) => {
        if (res.statusCode === 401) {
          // Need to authenticate
          const authHeader = res.headers['www-authenticate'];
          const authUrl = this.parseAuthHeader(authHeader);
          
          if (authUrl) {
            this.authenticateAndGetDigest(authUrl, options).then(resolve).catch(reject);
          } else {
            reject(new Error('Authentication required but no auth URL found'));
          }
          return;
        }

        const digest = res.headers['docker-content-digest'];
        resolve(digest || null);
      });

      req.on('error', reject);
      req.end();
    });
  }

  /**
   * Parse authentication header
   */
  parseAuthHeader(header) {
    if (!header) return null;
    
    const match = header.match(/Bearer realm="([^"]+)"/);
    if (!match) return null;
    
    const url = new URL(match[1]);
    const params = header.match(/service="([^"]+)"/);
    if (params) url.searchParams.set('service', params[1]);
    
    const scope = header.match(/scope="([^"]+)"/);
    if (scope) url.searchParams.set('scope', scope[1]);
    
    return url.toString();
  }

  /**
   * Authenticate and get digest
   */
  async authenticateAndGetDigest(authUrl, originalOptions) {
    return new Promise((resolve, reject) => {
      https.get(authUrl, (res) => {
        let data = '';
        res.on('data', chunk => data += chunk);
        res.on('end', () => {
          try {
            const auth = JSON.parse(data);
            const token = auth.token || auth.access_token;
            
            if (!token) {
              reject(new Error('No token in auth response'));
              return;
            }

            // Retry original request with token
            const options = {
              ...originalOptions,
              headers: {
                ...originalOptions.headers,
                'Authorization': `Bearer ${token}`
              }
            };

            const req = https.request(options, (res) => {
              const digest = res.headers['docker-content-digest'];
              resolve(digest || null);
            });

            req.on('error', reject);
            req.end();
          } catch (error) {
            reject(error);
          }
        });
      }).on('error', reject);
    });
  }

  /**
   * Extract tag from image name
   */
  extractTag(imageName) {
    const parts = imageName.split(':');
    return parts.length > 1 ? parts[parts.length - 1] : 'latest';
  }

  /**
   * Update a container
   */
  async updateContainer(containerId, options = {}) {
    const startTime = Date.now();

    console.log(`[UpdateManager] Starting update for container ${containerId}`);
    this.emit('update-start', { containerId, timestamp: new Date().toISOString() });

    try {
      const container = docker.getContainer(containerId);
      const inspect = await container.inspect();

      const imageName = inspect.Config.Image;
      const containerName = inspect.Name.replace(/^\//, '');
      const oldImageId = inspect.Image;

      // Get old image digest for rollback
      let oldImageDigest = null;
      try {
        const oldImage = docker.getImage(oldImageId);
        const oldImageInspect = await oldImage.inspect();
        oldImageDigest = oldImageInspect.RepoDigests?.[0] || oldImageId;
        console.log(`[UpdateManager] Stored old image digest: ${oldImageDigest.substring(0, 40)}...`);
      } catch (error) {
        console.warn(`[UpdateManager] Could not get old image digest: ${error.message}`);
      }

      // Create backup of current state
      const backup = {
        containerId,
        containerName,
        imageName,
        imageId: oldImageId,
        imageDigest: oldImageDigest,
        config: inspect.Config,
        hostConfig: inspect.HostConfig,
        networkSettings: inspect.NetworkSettings,
        timestamp: new Date().toISOString()
      };

      // Emit pre-update event for bundled workflows (e.g., backup-before-update)
      this.emit('pre-update', { containerId, containerName, imageName, backup });
      
      // Also trigger workflows for pre-update event directly
      this.triggerWorkflows('pre-update', { containerId, containerName, appId: containerName, imageName });

      // Pull latest image
      console.log(`[UpdateManager] Pulling latest image: ${imageName}`);
      await this.pullImage(imageName);

      // Stop container
      console.log(`[UpdateManager] Stopping container: ${containerName}`);
      await container.stop();

      // Remove old container
      console.log(`[UpdateManager] Removing old container: ${containerName}`);
      await container.remove();

      // Create new container with same configuration
      console.log(`[UpdateManager] Creating new container: ${containerName}`);
      const newContainer = await docker.createContainer({
        name: containerName,
        Image: imageName,
        ...backup.config,
        HostConfig: backup.hostConfig
      });

      // Start new container
      console.log(`[UpdateManager] Starting new container: ${containerName}`);
      await newContainer.start();

      // Extended verification with health checks and port accessibility
      console.log(`[UpdateManager] Performing extended verification...`);
      await this.verifyContainerExtended(newContainer, inspect, options.verifyTimeout || 60000);

      // Get new image ID
      const newInspect = await newContainer.inspect();
      const newImageId = newInspect.Image;

      // Remove old image only after successful verification
      if (oldImageId !== newImageId) {
        try {
          console.log(`[UpdateManager] Removing old image: ${oldImageId.substring(0, 12)}`);
          const oldImage = docker.getImage(oldImageId);
          await oldImage.remove({ force: false });
          console.log(`[UpdateManager] Old image removed successfully`);
        } catch (error) {
          console.warn(`[UpdateManager] Could not remove old image (may be in use): ${error.message}`);
        }
      }

      const duration = Date.now() - startTime;

      const historyEntry = {
        containerId: newContainer.id,
        containerName,
        imageName,
        oldImageId: oldImageId.substring(0, 12),
        newImageId: newImageId.substring(0, 12),
        timestamp: new Date().toISOString(),
        duration,
        status: 'success',
        backup
      };

      this.addToHistory(historyEntry);
      this.availableUpdates.delete(containerId);

      this.emit('update-complete', historyEntry);
      console.log(`[UpdateManager] Update completed in ${duration}ms`);

      return historyEntry;
    } catch (error) {
      const duration = Date.now() - startTime;

      const historyEntry = {
        containerId,
        timestamp: new Date().toISOString(),
        duration,
        status: 'failed',
        error: error.message
      };

      this.addToHistory(historyEntry);
      this.emit('update-failed', historyEntry);

      // Attempt rollback
      if (options.autoRollback !== false) {
        console.log(`[UpdateManager] Attempting rollback for ${containerId}`);
        try {
          await this.rollbackUpdate(containerId);
        } catch (rollbackError) {
          console.error(`[UpdateManager] Rollback failed:`, rollbackError.message);
        }
      }

      throw error;
    }
  }

  /**
   * Pull Docker image
   */
  async pullImage(imageName) {
    return new Promise((resolve, reject) => {
      docker.pull(imageName, (err, stream) => {
        if (err) {
          reject(err);
          return;
        }

        docker.modem.followProgress(stream, (err, output) => {
          if (err) {
            reject(err);
          } else {
            resolve(output);
          }
        });
      });
    });
  }

  /**
   * Verify container is running and healthy
   */
  async verifyContainer(container, timeout = 30000) {
    const startTime = Date.now();

    while (Date.now() - startTime < timeout) {
      try {
        const inspect = await container.inspect();

        if (inspect.State.Running) {
          // Check health if health check is configured
          if (inspect.State.Health) {
            if (inspect.State.Health.Status === 'healthy') {
              return true;
            }
          } else {
            // No health check, just verify it's running
            return true;
          }
        }

        // Wait before checking again
        await new Promise(resolve => setTimeout(resolve, 1000));
      } catch (error) {
        throw new Error(`Container verification failed: ${error.message}`);
      }
    }

    throw new Error('Container verification timeout');
  }

  /**
   * Extended container verification with health checks and port accessibility
   * @param {object} container - Docker container object
   * @param {object} oldInspect - Old container inspect data for port comparison
   * @param {number} timeout - Verification timeout in milliseconds (default: 60000)
   */
  async verifyContainerExtended(container, oldInspect, timeout = 60000) {
    const startTime = Date.now();
    const maxAttempts = Math.floor(timeout / 2000); // Check every 2 seconds
    let lastError = null;

    console.log(`[UpdateManager] Extended verification with ${maxAttempts} attempts over ${timeout/1000}s`);

    for (let attempt = 0; attempt < maxAttempts; attempt++) {
      try {
        const inspect = await container.inspect();

        // Step 1: Verify container is running
        if (!inspect.State.Running) {
          lastError = 'Container is not running';
          throw new Error(lastError);
        }

        // Step 2: Check Docker health check if available
        if (inspect.State.Health) {
          if (inspect.State.Health.Status === 'healthy') {
            console.log(`[UpdateManager] Container health check: healthy`);
            return true;
          } else if (inspect.State.Health.Status === 'unhealthy') {
            lastError = 'Container health check failed (unhealthy)';
            throw new Error(lastError);
          }
          // Status is 'starting' - continue waiting
          console.log(`[UpdateManager] Health check status: ${inspect.State.Health.Status} (attempt ${attempt + 1}/${maxAttempts})`);
        } else {
          // Step 3: No Docker health check - verify HTTP port accessibility
          const ports = this.extractPorts(inspect);

          if (ports.length > 0) {
            // Try to access the first HTTP port
            const primaryPort = ports[0];
            const testUrl = `http://localhost:${primaryPort.hostPort}`;

            try {
              const response = await fetch(testUrl, {
                signal: AbortSignal.timeout(3000),
                redirect: 'manual'
              });

              // Accept 2xx, 3xx, 4xx as "accessible" (server is responding)
              if (response.status >= 200 && response.status < 500) {
                console.log(`[UpdateManager] Port ${primaryPort.hostPort} is accessible (HTTP ${response.status})`);

                // Wait a bit more to ensure stability
                if (attempt >= 2) {
                  console.log(`[UpdateManager] Container verified successfully`);
                  return true;
                }
              }
            } catch (fetchError) {
              lastError = `Port ${primaryPort.hostPort} not accessible: ${fetchError.message}`;
              console.log(`[UpdateManager] ${lastError} (attempt ${attempt + 1}/${maxAttempts})`);
            }
          } else {
            // No ports exposed - just verify it's running for a few cycles
            if (attempt >= 5) {
              console.log(`[UpdateManager] Container running without exposed ports (verified)`);
              return true;
            }
          }
        }

        // Wait before next attempt
        if (attempt < maxAttempts - 1) {
          await new Promise(resolve => setTimeout(resolve, 2000));
        }
      } catch (error) {
        lastError = error.message;
        console.log(`[UpdateManager] Verification attempt ${attempt + 1} failed: ${lastError}`);

        if (attempt < maxAttempts - 1) {
          await new Promise(resolve => setTimeout(resolve, 2000));
        }
      }
    }

    // Verification failed
    const duration = Date.now() - startTime;
    throw new Error(`Extended verification failed after ${duration}ms: ${lastError || 'timeout'}`);
  }

  /**
   * Extract port mappings from container inspect data
   * @param {object} inspect - Container inspect data
   * @returns {Array} Array of port mappings
   */
  extractPorts(inspect) {
    const ports = [];

    if (inspect.NetworkSettings && inspect.NetworkSettings.Ports) {
      for (const [containerPort, bindings] of Object.entries(inspect.NetworkSettings.Ports)) {
        if (bindings && bindings.length > 0) {
          for (const binding of bindings) {
            if (binding.HostPort) {
              ports.push({
                containerPort: containerPort.split('/')[0],
                hostPort: binding.HostPort,
                protocol: containerPort.split('/')[1] || 'tcp'
              });
            }
          }
        }
      }
    }

    return ports;
  }

  /**
   * Rollback to previous version
   */
  async rollbackUpdate(containerId) {
    console.log(`[UpdateManager] Rolling back container ${containerId}`);
    
    // Find last successful update in history
    const lastUpdate = this.history
      .filter(h => h.containerId === containerId && h.status === 'success' && h.backup)
      .pop();

    if (!lastUpdate || !lastUpdate.backup) {
      throw new Error('No backup found for rollback');
    }

    const backup = lastUpdate.backup;

    try {
      // Stop and remove current container
      try {
        const container = docker.getContainer(containerId);
        await container.stop();
        await container.remove();
      } catch (error) {
        // Container might not exist, continue
      }

      // Recreate container from backup
      const newContainer = await docker.createContainer({
        name: backup.containerName,
        Image: backup.imageName,
        ...backup.config,
        HostConfig: backup.hostConfig
      });

      await newContainer.start();

      console.log(`[UpdateManager] Rollback completed for ${backup.containerName}`);
      this.emit('rollback-complete', { containerId, containerName: backup.containerName });
      
      return true;
    } catch (error) {
      console.error(`[UpdateManager] Rollback failed:`, error.message);
      throw error;
    }
  }

  /**
   * Schedule update for maintenance window
   */
  scheduleUpdate(containerId, scheduledTime) {
    const delay = new Date(scheduledTime).getTime() - Date.now();
    
    if (delay < 0) {
      throw new Error('Scheduled time must be in the future');
    }

    setTimeout(() => {
      this.updateContainer(containerId).catch(error => {
        console.error(`[UpdateManager] Scheduled update failed:`, error.message);
      });
    }, delay);

    console.log(`[UpdateManager] Update scheduled for ${containerId} at ${scheduledTime}`);
  }

  /**
   * Get available updates
   */
  getAvailableUpdates() {
    return Array.from(this.availableUpdates.values());
  }

  /**
   * Get update history
   */
  getHistory(limit = 50) {
    return this.history.slice(-limit).reverse();
  }

  /**
   * Get changelog and release information from Docker Hub
   * @param {string} imageName - Docker image name (e.g., "nginx:latest" or "linuxserver/plex")
   * @returns {Object} Changelog information including tags, description, and URLs
   */
  async getChangelog(imageName) {
    try {
      // Parse image name
      const [fullRepo, tag] = imageName.split(':');
      const imageTag = tag || 'latest';

      // Normalize repository name for Docker Hub API
      let repo = fullRepo;
      let namespace = 'library';

      if (fullRepo.includes('/')) {
        const parts = fullRepo.split('/');
        namespace = parts[0];
        repo = parts.slice(1).join('/');
      }

      const repoPath = namespace === 'library' ? repo : `${namespace}/${repo}`;

      // Fetch repository info from Docker Hub API
      const repoInfo = await this.fetchDockerHubRepo(repoPath, namespace === 'library');

      // Fetch available tags
      const tags = await this.fetchDockerHubTags(repoPath, namespace === 'library');

      // Build the Docker Hub URL
      const hubUrl = namespace === 'library'
        ? `https://hub.docker.com/_/${repo}`
        : `https://hub.docker.com/r/${namespace}/${repo}`;

      return {
        imageName,
        currentTag: imageTag,
        repository: {
          name: repoPath,
          description: repoInfo?.description || 'No description available',
          shortDescription: repoInfo?.description?.substring(0, 200) || '',
          starCount: repoInfo?.star_count || 0,
          pullCount: repoInfo?.pull_count || 0,
          lastUpdated: repoInfo?.last_updated || null
        },
        tags: tags.slice(0, 10).map(t => ({
          name: t.name,
          lastPushed: t.last_pushed || t.tag_last_pushed,
          digest: t.digest?.substring(0, 12) || 'unknown',
          size: t.full_size || t.size || 0
        })),
        urls: {
          dockerHub: hubUrl,
          tags: `${hubUrl}/tags`,
          dockerfile: repoInfo?.dockerfile_url || null
        },
        changelog: this.formatChangelog(repoInfo, tags, imageTag)
      };
    } catch (error) {
      console.error(`[UpdateManager] Error fetching changelog for ${imageName}:`, error.message);

      // Return basic info even on error
      const [fullRepo] = imageName.split(':');
      const repoPath = fullRepo.includes('/') ? fullRepo : `library/${fullRepo}`;

      return {
        imageName,
        error: error.message,
        urls: {
          dockerHub: `https://hub.docker.com/r/${repoPath.replace('library/', '_/')}`,
        },
        changelog: 'Unable to fetch changelog. Visit Docker Hub for details.'
      };
    }
  }

  /**
   * Fetch repository info from Docker Hub
   */
  async fetchDockerHubRepo(repoPath, isLibrary) {
    return new Promise((resolve, reject) => {
      const apiPath = isLibrary
        ? `/v2/repositories/library/${repoPath}`
        : `/v2/repositories/${repoPath}`;

      const options = {
        hostname: 'hub.docker.com',
        path: apiPath,
        method: 'GET',
        headers: {
          'Accept': 'application/json',
          'User-Agent': 'DashCaddy/1.0'
        }
      };

      const req = https.request(options, (res) => {
        let data = '';
        res.on('data', chunk => data += chunk);
        res.on('end', () => {
          try {
            if (res.statusCode === 200) {
              resolve(JSON.parse(data));
            } else {
              resolve(null);
            }
          } catch (e) {
            resolve(null);
          }
        });
      });

      req.on('error', () => resolve(null));
      req.setTimeout(10000, () => {
        req.destroy();
        resolve(null);
      });
      req.end();
    });
  }

  /**
   * Fetch available tags from Docker Hub
   */
  async fetchDockerHubTags(repoPath, isLibrary) {
    return new Promise((resolve, reject) => {
      const apiPath = isLibrary
        ? `/v2/repositories/library/${repoPath}/tags?page_size=20&ordering=last_updated`
        : `/v2/repositories/${repoPath}/tags?page_size=20&ordering=last_updated`;

      const options = {
        hostname: 'hub.docker.com',
        path: apiPath,
        method: 'GET',
        headers: {
          'Accept': 'application/json',
          'User-Agent': 'DashCaddy/1.0'
        }
      };

      const req = https.request(options, (res) => {
        let data = '';
        res.on('data', chunk => data += chunk);
        res.on('end', () => {
          try {
            if (res.statusCode === 200) {
              const parsed = JSON.parse(data);
              resolve(parsed.results || []);
            } else {
              resolve([]);
            }
          } catch (e) {
            resolve([]);
          }
        });
      });

      req.on('error', () => resolve([]));
      req.setTimeout(10000, () => {
        req.destroy();
        resolve([]);
      });
      req.end();
    });
  }

  /**
   * Format changelog from repo info and tags
   */
  formatChangelog(repoInfo, tags, currentTag) {
    const lines = [];

    if (repoInfo?.description) {
      lines.push(`**${repoInfo.description.split('\n')[0]}**`);
      lines.push('');
    }

    // Find current and latest tags
    const latestTag = tags.find(t => t.name === 'latest');
    const currentTagInfo = tags.find(t => t.name === currentTag);

    if (latestTag?.last_pushed || latestTag?.tag_last_pushed) {
      const lastUpdated = new Date(latestTag.last_pushed || latestTag.tag_last_pushed);
      lines.push(`Latest update: ${lastUpdated.toLocaleDateString()}`);
    }

    if (tags.length > 0) {
      lines.push('');
      lines.push('Recent tags:');
      tags.slice(0, 5).forEach(t => {
        const date = t.last_pushed || t.tag_last_pushed;
        const dateStr = date ? new Date(date).toLocaleDateString() : 'unknown';
        lines.push(`  - ${t.name} (${dateStr})`);
      });
    }

    if (repoInfo?.pull_count) {
      lines.push('');
      lines.push(`Total pulls: ${repoInfo.pull_count.toLocaleString()}`);
    }

    return lines.join('\n') || 'No changelog available';
  }

  /**
   * Start the auto-update scheduler — runs hourly, applies updates in maintenance windows
   */
  startAutoUpdateScheduler() {
    const AUTO_CHECK_INTERVAL = 60 * 60 * 1000; // 1 hour

    // Delay first run by 10 minutes to let containers start
    setTimeout(() => this.runAutoUpdates(), 10 * 60 * 1000);
    this.autoUpdateInterval = setInterval(() => this.runAutoUpdates(), AUTO_CHECK_INTERVAL);

    const count = Object.values(this.config.autoUpdate || {}).filter(c => c.enabled).length;
    if (count > 0) {
      console.log(`[UpdateManager] Auto-update scheduler started (${count} container(s) configured)`);
    }
  }

  /**
   * Execute auto-updates for all configured containers
   */
  async runAutoUpdates() {
    const autoConfig = this.config.autoUpdate || {};
    const now = new Date();
    const hour = now.getHours();
    const dayOfWeek = now.getDay(); // 0 = Sunday
    const dayOfMonth = now.getDate();

    for (const [containerId, cfg] of Object.entries(autoConfig)) {
      if (!cfg.enabled) continue;

      // Check maintenance window (e.g., "02:00-05:00")
      if (cfg.maintenanceWindow) {
        const [startStr, endStr] = cfg.maintenanceWindow.split('-').map(s => s.trim());
        const startHour = parseInt(startStr);
        const endHour = parseInt(endStr);
        if (startHour <= endHour) {
          if (hour < startHour || hour >= endHour) continue;
        } else {
          // Wraps midnight (e.g., "22:00-04:00")
          if (hour < startHour && hour >= endHour) continue;
        }
      } else {
        // Default: only run between 2AM and 4AM
        if (hour < 2 || hour >= 4) continue;
      }

      // Check schedule
      const shouldRun =
        cfg.schedule === 'daily' ||
        (cfg.schedule === 'weekly' && dayOfWeek === 0) || // Sunday
        (cfg.schedule === 'monthly' && dayOfMonth === 1);

      if (!shouldRun) continue;

      // Check if already ran today
      const lastRun = cfg.lastAutoUpdate ? new Date(cfg.lastAutoUpdate) : null;
      if (lastRun && lastRun.toDateString() === now.toDateString()) continue;

      // Check if this container has an available update
      const update = this.availableUpdates.get(containerId);
      if (!update) continue;

      console.log(`[UpdateManager] Auto-updating ${update.containerName} (schedule: ${cfg.schedule})`);
      this.emit('auto-update-start', { containerId, containerName: update.containerName, schedule: cfg.schedule });

      try {
        const result = await this.updateContainer(containerId, { autoRollback: cfg.autoRollback !== false });
        cfg.lastAutoUpdate = now.toISOString();
        this.saveConfig();
        console.log(`[UpdateManager] Auto-update completed for ${update.containerName}`);
        this.emit('auto-update-complete', { containerId, containerName: update.containerName, result });
      } catch (error) {
        console.error(`[UpdateManager] Auto-update failed for ${update.containerName}:`, error.message);
        cfg.lastAutoUpdate = now.toISOString(); // Don't retry same day
        this.saveConfig();
        this.emit('auto-update-failed', { containerId, containerName: update.containerName, error: error.message });
      }
    }
  }

  /**
   * Get auto-update configuration for all containers
   */
  getAutoUpdateConfig() {
    return this.config.autoUpdate || {};
  }

  /**
   * Configure auto-update for a container
   */
  configureAutoUpdate(containerId, config) {
    if (!this.config.autoUpdate) {
      this.config.autoUpdate = {};
    }

    this.config.autoUpdate[containerId] = {
      enabled: config.enabled !== false,
      schedule: config.schedule || 'weekly',
      maintenanceWindow: config.maintenanceWindow,
      autoRollback: config.autoRollback !== false,
      securityOnly: config.securityOnly || false
    };

    this.saveConfig();
  }

  /**
   * Add entry to history
   */
  addToHistory(entry) {
    this.history.push(entry);
    
    // Keep only last 100 entries
    if (this.history.length > 100) {
      this.history = this.history.slice(-100);
    }
    
    this.saveHistory();
  }

  /**
   * Load configuration
   */
  loadConfig() {
    try {
      if (fs.existsSync(UPDATE_CONFIG_FILE)) {
        return JSON.parse(fs.readFileSync(UPDATE_CONFIG_FILE, 'utf8'));
      }
    } catch (error) {
      console.error('[UpdateManager] Error loading config:', error.message);
    }
    return { autoUpdate: {} };
  }

  /**
   * Save configuration
   */
  saveConfig() {
    try {
      fs.writeFileSync(UPDATE_CONFIG_FILE, JSON.stringify(this.config, null, 2));
    } catch (error) {
      console.error('[UpdateManager] Error saving config:', error.message);
    }
  }

  /**
   * Load history
   */
  loadHistory() {
    try {
      if (fs.existsSync(UPDATE_HISTORY_FILE)) {
        return JSON.parse(fs.readFileSync(UPDATE_HISTORY_FILE, 'utf8'));
      }
    } catch (error) {
      console.error('[UpdateManager] Error loading history:', error.message);
    }
    return [];
  }

  /**
   * Save history
   */
  saveHistory() {
    try {
      fs.writeFileSync(UPDATE_HISTORY_FILE, JSON.stringify(this.history, null, 2));
    } catch (error) {
      console.error('[UpdateManager] Error saving history:', error.message);
    }
  }
}

// Export singleton instance
module.exports = new UpdateManager();
