/**
 * Notification Manager - Multi-provider notification delivery
 * Supports Discord, Telegram, ntfy, and Email notifications
 */
const EventEmitter = require('events');
const fs = require('fs');
const path = require('path');
const nodemailer = require('nodemailer');

const DEFAULT_CONFIG = {
  enabled: true,
  providers: {
    discord: { enabled: false, webhookUrl: '' },
    telegram: { enabled: false, botToken: '', chatId: '' },
    ntfy: { enabled: false, topic: '', serverUrl: 'https://ntfy.sh' },
    email: { enabled: false, host: '', port: 587, to: '', from: '', username: '', password: '' }
  },
  events: {
    'container-down': true,
    'container-up': false,
    'alert': true,
    'backup-complete': true,
    'backup-failed': true,
    'update-available': true
  }
};

class NotificationManager extends EventEmitter {
  constructor(ctx) {
    super();
    this.ctx = ctx;
    this.NOTIFICATIONS_FILE = ctx.NOTIFICATIONS_FILE;
    this.log = ctx.log || console;
    this.config = { ...DEFAULT_CONFIG };
    this.lastSent = null;
    this.history = [];
    this.maxHistory = 100;
    this.healthDaemonInterval = null;
    this.healthState = new Map();

    this._loadConfig();
  }

  /**
   * Load config from file
   */
  _loadConfig() {
    try {
      if (fs.existsSync(this.NOTIFICATIONS_FILE)) {
        const data = JSON.parse(fs.readFileSync(this.NOTIFICATIONS_FILE, 'utf8'));
        this.config = this._mergeConfig(DEFAULT_CONFIG, data);
      }
    } catch (error) {
      this.log.error('notification', 'Failed to load config', { error: error.message });
    }
  }

  /**
   * Merge loaded config with defaults
   */
  _mergeConfig(defaults, loaded) {
    const result = { ...defaults };
    for (const key of Object.keys(defaults)) {
      if (loaded && typeof defaults[key] === 'object' && !Array.isArray(defaults[key])) {
        result[key] = { ...defaults[key], ...loaded[key] };
      } else if (loaded && loaded[key] !== undefined) {
        result[key] = loaded[key];
      }
    }
    return result;
  }

  /**
   * Get current config (for API)
   */
  getConfig() {
    return this.config;
  }

  /**
   * Save config to file
   */
  async saveConfig() {
    try {
      const dir = path.dirname(this.NOTIFICATIONS_FILE);
      if (!fs.existsSync(dir)) {
        fs.mkdirSync(dir, { recursive: true });
      }
      fs.writeFileSync(this.NOTIFICATIONS_FILE, JSON.stringify(this.config, null, 2));
      return true;
    } catch (error) {
      this.log.error('notification', 'Failed to save config', { error: error.message });
      throw error;
    }
  }

  /**
   * Get notification history
   */
  getHistory() {
    return this.history.slice();
  }

  /**
   * Clear notification history
   */
  clearHistory() {
    this.history = [];
  }

  /**
   * Add entry to history
   */
  _addToHistory(entry) {
    this.history.unshift({
      ...entry,
      timestamp: new Date().toISOString()
    });
    if (this.history.length > this.maxHistory) {
      this.history = this.history.slice(0, this.maxHistory);
    }
    this.lastSent = new Date().toISOString();
  }

  /**
   * Send notification via all enabled providers
   */
  async send(event, data, type = 'info') {
    if (!this.config.enabled) {
      return { success: false, error: 'Notifications disabled' };
    }

    // Check if event is enabled
    if (event && this.config.events && !this.config.events[event]) {
      return { success: false, error: `Event ${event} not enabled` };
    }

    const results = [];
    const providers = this.config.providers;

    // Discord
    if (providers.discord?.enabled && providers.discord?.webhookUrl) {
      try {
        const result = await this.sendDiscord(this._formatText(data, event), this._formatEmbed(data, event, type));
        results.push({ provider: 'discord', ...result });
      } catch (error) {
        results.push({ provider: 'discord', success: false, error: error.message });
      }
    }

    // Telegram
    if (providers.telegram?.enabled && providers.telegram?.botToken && providers.telegram?.chatId) {
      try {
        const result = await this.sendTelegram(this._formatText(data, event));
        results.push({ provider: 'telegram', ...result });
      } catch (error) {
        results.push({ provider: 'telegram', success: false, error: error.message });
      }
    }

    // ntfy
    if (providers.ntfy?.enabled && providers.ntfy?.topic) {
      try {
        const result = await this.sendNtfy(this._formatText(data, event), this._formatTitle(event));
        results.push({ provider: 'ntfy', ...result });
      } catch (error) {
        results.push({ provider: 'ntfy', success: false, error: error.message });
      }
    }

    // Email
    if (providers.email?.enabled && providers.email?.host && providers.email?.to) {
      try {
        const result = await this.sendEmail(
          this._formatTitle(event),
          this._formatText(data, event)
        );
        results.push({ provider: 'email', ...result });
      } catch (error) {
        results.push({ provider: 'email', success: false, error: error.message });
      }
    }

    const allSucceeded = results.every(r => r.success);
    this._addToHistory({
      title: this._formatTitle(event),
      type,
      event,
      results
    });

    return { success: allSucceeded, results };
  }

  /**
   * Send Discord webhook notification
   */
  async sendDiscord(text, embed) {
    const { webhookUrl } = this.config.providers.discord;
    if (!webhookUrl) {
      throw new Error('Discord webhook not configured');
    }

    const payload = {
      content: text,
      embeds: embed ? [embed] : []
    };

    const response = await this.ctx.fetchT(webhookUrl, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload)
    });

    if (!response.ok) {
      throw new Error(`Discord API error: ${response.status}`);
    }

    return { success: true };
  }

  /**
   * Send Telegram message
   */
  async sendTelegram(text) {
    const { botToken, chatId } = this.config.providers.telegram;
    if (!botToken || !chatId) {
      throw new Error('Telegram not configured');
    }

    const url = `https://api.telegram.org/bot${botToken}/sendMessage`;
    const payload = {
      chat_id: chatId,
      text,
      parse_mode: 'Markdown'
    };

    const response = await this.ctx.fetchT(url, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload)
    });

    const data = await response.json();
    if (!data.ok) {
      throw new Error(`Telegram error: ${data.description}`);
    }

    return { success: true };
  }

  /**
   * Send ntfy notification
   */
  async sendNtfy(text, title) {
    const { serverUrl, topic } = this.config.providers.ntfy;
    if (!topic) {
      throw new Error('ntfy topic not configured');
    }

    const url = `${serverUrl.replace(/\/$/, '')}/${topic}`;

    const response = await this.ctx.fetchT(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'text/plain',
        'Title': title || 'DashCaddy',
        'Priority': '3'
      },
      body: text
    });

    if (!response.ok) {
      throw new Error(`ntfy error: ${response.status}`);
    }

    return { success: true };
  }

  /**
   * Send email notification
   */
  async sendEmail(subject, body) {
    const { host, port, to, from, username, password, secure } = this.config.providers.email;
    if (!host || !to) {
      throw new Error('Email not configured');
    }

    // Create transporter
    const transporter = nodemailer.createTransport({
      host,
      port: parseInt(port) || 587,
      secure: !!secure,
      auth: username ? {
        user: username,
        pass: password
      } : undefined
    });

    // Send mail
    await transporter.sendMail({
      from: from || username,
      to,
      subject,
      text: body,
      html: `<pre style="font-family: monospace;">${body}</pre>`
    });

    return { success: true };
  }

  /**
   * Send resource alert
   */
  async sendAlert(alert) {
    const text = this._formatAlertText(alert);
    const embed = {
      title: `⚠️ Resource Alert: ${alert.containerName}`,
      color: this._getAlertColor(alert.alerts),
      fields: alert.alerts.map(a => ({
        name: a.type.toUpperCase(),
        value: a.message,
        inline: true
      })),
      footer: {
        text: 'DashCaddy Resource Monitor'
      },
      timestamp: alert.timestamp
    };

    return this.send('alert', { ...alert, text, embed }, 'warning');
  }

  /**
   * Send backup complete notification
   */
  async sendBackupComplete(backup) {
    const event = backup.status === 'success' ? 'backup-complete' : 'backup-failed';
    const type = backup.status === 'success' ? 'success' : 'error';
    
    const text = backup.status === 'success'
      ? `✅ Backup "${backup.name}" completed successfully`
      : `❌ Backup "${backup.name}" failed: ${backup.error}`;

    return this.send(event, { ...backup, text }, type);
  }

  /**
   * Send service event notification (container up/down, deploy success/fail)
   */
  async sendServiceEvent(event, service) {
    const eventMap = {
      'container-up': { type: 'success', text: `✅ Container "${service.containerName || service.name}" is now UP` },
      'container-down': { type: 'error', text: `🔴 Container "${service.containerName || service.name}" is DOWN` },
      'deploy-success': { type: 'success', text: `✅ "${service.name}" deployed successfully` },
      'deploy-failed': { type: 'error', text: `❌ "${service.name}" deployment failed` },
      'auto-restart': { type: 'warning', text: `🔄 Container "${service.containerName || service.name}" auto-restarted` }
    };

    const info = eventMap[event] || { type: 'info', text: `Service event: ${event}` };
    return this.send(event, { ...service, text: info.text }, info.type);
  }

  // ===== Helper Methods =====

  _formatTitle(event) {
    const titles = {
      'container-down': 'Container Down',
      'container-up': 'Container Recovered',
      'alert': 'Resource Alert',
      'backup-complete': 'Backup Complete',
      'backup-failed': 'Backup Failed',
      'update-available': 'Update Available',
      'test': 'Test Notification',
      'auto-restart': 'Auto-Restart',
      'deploy-success': 'Deployment Success',
      'deploy-failed': 'Deployment Failed'
    };
    return titles[event] || 'DashCaddy Notification';
  }

  _formatText(data, event) {
    if (typeof data === 'string') return data;
    return data.text || data.message || this._formatTitle(event);
  }

  _formatEmbed(data, event, type) {
    if (typeof data === 'string') return null;
    if (data.embed) return data.embed;

    return {
      title: this._formatTitle(event),
      description: data.text || data.message || '',
      color: this._getTypeColor(type),
      timestamp: new Date().toISOString()
    };
  }

  _formatAlertText(alert) {
    const lines = [
      `**${alert.containerName}**`,
      '',
      ...alert.alerts.map(a => `• ${a.message}`)
    ];
    return lines.join('\n');
  }

  _getAlertColor(alerts) {
    if (alerts.some(a => a.severity === 'critical')) return 15158332; // Red
    if (alerts.some(a => a.severity === 'warning')) return 16776960;  // Yellow
    return 3447003; // Blue
  }

  _getTypeColor(type) {
    const colors = {
      success: 3066993,  // Green
      error: 15158332,   // Red
      warning: 16776960, // Yellow
      info: 3447003      // Blue
    };
    return colors[type] || colors.info;
  }

  // ===== Health Check Daemon =====

  startHealthDaemon() {
    if (this.healthDaemonInterval) return;

    const interval = (this.config.healthCheck?.intervalMinutes || 5) * 60 * 1000;
    this.healthDaemonInterval = setInterval(() => {
      this.checkHealth().catch(err => {
        this.log.error('notification', 'Health check failed', { error: err.message });
      });
    }, interval);

    this.log.info('notification', 'Health daemon started', { intervalMinutes: this.config.healthCheck?.intervalMinutes });
  }

  stopHealthDaemon() {
    if (this.healthDaemonInterval) {
      clearInterval(this.healthDaemonInterval);
      this.healthDaemonInterval = null;
      this.log.info('notification', 'Health daemon stopped');
    }
  }

  async checkHealth() {
    if (!this.config.healthCheck?.enabled || !this.ctx.docker) {
      return { checked: false };
    }

    try {
      const containers = await this.ctx.docker.listContainers({ all: true });
      const previousState = new Map(this.healthState);

      for (const container of containers) {
        const name = container.Names[0]?.replace(/^\//, '') || container.Id.substring(0, 12);
        const wasDown = previousState.get(container.Id) === false;
        const isDown = container.State !== 'running';

        this.healthState.set(container.Id, isDown);

        if (wasDown && !isDown) {
          // Container recovered
          await this.sendServiceEvent('container-up', {
            containerId: container.Id,
            containerName: name,
            state: container.State
          });
        } else if (!wasDown && isDown) {
          // Container went down
          await this.sendServiceEvent('container-down', {
            containerId: container.Id,
            containerName: name,
            state: container.State
          });
        }
      }

      // Update last check time
      this.config.healthCheck = this.config.healthCheck || {};
      this.config.healthCheck.lastCheck = new Date().toISOString();
      await this.saveConfig();

      return {
        checked: true,
        containersMonitored: containers.length,
        lastCheck: this.config.healthCheck.lastCheck
      };
    } catch (error) {
      this.log.error('notification', 'Health check error', { error: error.message });
      throw error;
    }
  }

  getHealthState() {
    return new Map(this.healthState);
  }
}

module.exports = NotificationManager;