/**
 * EvolutionInstance Model
 * Manages Evolution API instances with tenant isolation
 * 
 * @module models/EvolutionInstance
 */

const { pool } = require('../config/database');
const { logger } = require('../config/logger');

class EvolutionInstance {
  /**
   * Create new instance
   * @param {number} tenantId - Tenant ID
   * @param {Object} data - Instance data
   * @returns {Promise<Object>} Created instance
   */
  static async create(tenantId, data) {
    try {
      if (tenantId === undefined || tenantId === null) {
        throw new Error('tenantId is required');
      }

      logger.info('Creating Evolution instance', { tenantId, data });

      const [result] = await pool.query(
        `INSERT INTO evolution_instances 
        (tenant_id, instance_name, instance_type, phone_number, status, evolution_instance_id, 
         evolution_api_key, webhook_url, webhook_events, connection_data) 
        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
        [
          tenantId,
          data.instance_name,
          data.instance_type || 'whatsapp_web',
          data.phone_number || null,
          data.status || 'disconnected',
          data.evolution_instance_id || null,
          data.evolution_api_key || null,
          data.webhook_url || null,
          data.webhook_events ? JSON.stringify(data.webhook_events) : null,
          data.connection_data ? JSON.stringify(data.connection_data) : null
        ]
      );

      logger.info('Evolution instance created', { tenantId, insertId: result.insertId });

      return await this.getById(result.insertId);
    } catch (error) {
      logger.error('Error creating Evolution instance', { 
        tenantId, 
        error: error.message,
        stack: error.stack,
        data 
      });
      throw error;
    }
  }

  /**
   * Get instances by tenant ID
   * @param {number} tenantId - Tenant ID
   * @returns {Promise<Array>} Array of instances
   */
  static async getByTenantId(tenantId) {
    try {
      if (tenantId === undefined || tenantId === null) {
        logger.error('getByTenantId called with invalid tenantId', { tenantId });
        return [];
      }
      
      const query = 'SELECT * FROM evolution_instances WHERE tenant_id = ? AND is_active = TRUE ORDER BY created_at DESC';
      logger.info('Executing SELECT query', { query, params: [tenantId] });
      const [rows] = await pool.query(query, [tenantId]);
      
      // Parse JSON fields
      return rows.map(row => ({
        ...row,
        webhook_events: row.webhook_events ? JSON.parse(row.webhook_events) : null,
        connection_data: row.connection_data ? JSON.parse(row.connection_data) : null
      }));
    } catch (error) {
      logger.error('Error getting Evolution instances', { tenantId, error: error.message, stack: error.stack });
      throw error;
    }
  }

  /**
   * Get instance by ID
   * @param {number} id - Instance ID
   * @returns {Promise<Object|null>} Instance data or null
   */
  static async getById(id) {
    try {
      const [rows] = await pool.query(
        `SELECT * FROM evolution_instances WHERE id = ? LIMIT 1`,
        [id]
      );
      
      if (rows.length === 0) return null;
      
      const row = rows[0];
      return {
        ...row,
        webhook_events: row.webhook_events ? JSON.parse(row.webhook_events) : null,
        connection_data: row.connection_data ? JSON.parse(row.connection_data) : null
      };
    } catch (error) {
      logger.error('Error getting instance by ID', { id, error: error.message });
      throw error;
    }
  }

  /**
   * Get instance by Evolution instance ID
   * @param {string} evolutionInstanceId - Evolution API instance ID
   * @returns {Promise<Object|null>} Instance data or null
   */
  static async getByEvolutionId(evolutionInstanceId) {
    try {
      const [rows] = await pool.query(
        `SELECT * FROM evolution_instances WHERE evolution_instance_id = ? LIMIT 1`,
        [evolutionInstanceId]
      );
      
      if (rows.length === 0) return null;
      
      const row = rows[0];
      return {
        ...row,
        webhook_events: row.webhook_events ? JSON.parse(row.webhook_events) : null,
        connection_data: row.connection_data ? JSON.parse(row.connection_data) : null
      };
    } catch (error) {
      logger.error('Error getting instance by Evolution ID', { evolutionInstanceId, error: error.message });
      throw error;
    }
  }

  /**
   * Update instance
   * @param {number} id - Instance ID
   * @param {Object} data - Instance data to update
   * @returns {Promise<Object>} Updated instance
   */
  static async update(id, data) {
    try {
      if (!data || typeof data !== 'object') {
        throw new Error('data must be an object');
      }

      logger.info('EvolutionInstance.update called', { id, data });

      const updates = [];
      const values = [];

      if (data.instance_name !== undefined) {
        updates.push('instance_name = ?');
        values.push(data.instance_name);
      }
      if (data.instance_type !== undefined) {
        updates.push('instance_type = ?');
        values.push(data.instance_type);
      }
      if (data.phone_number !== undefined) {
        updates.push('phone_number = ?');
        values.push(data.phone_number);
      }
      if (data.status !== undefined) {
        updates.push('status = ?');
        values.push(data.status);
      }
      if (data.qr_code !== undefined) {
        updates.push('qr_code = ?');
        values.push(data.qr_code);
      }
      if (data.evolution_instance_id !== undefined) {
        updates.push('evolution_instance_id = ?');
        values.push(data.evolution_instance_id);
      }
      if (data.evolution_api_key !== undefined) {
        updates.push('evolution_api_key = ?');
        values.push(data.evolution_api_key);
      }
      if (data.webhook_url !== undefined) {
        updates.push('webhook_url = ?');
        values.push(data.webhook_url);
      }
      if (data.webhook_events !== undefined) {
        updates.push('webhook_events = ?');
        values.push(JSON.stringify(data.webhook_events));
      }
      if (data.connection_data !== undefined) {
        updates.push('connection_data = ?');
        values.push(JSON.stringify(data.connection_data));
      }
      if (data.error_message !== undefined) {
        updates.push('error_message = ?');
        values.push(data.error_message);
      }
      if (data.last_connected_at !== undefined) {
        updates.push('last_connected_at = ?');
        values.push(data.last_connected_at);
      }
      if (data.is_active !== undefined) {
        updates.push('is_active = ?');
        values.push(data.is_active);
      }

      if (updates.length === 0) {
        logger.info('No fields to update, returning existing instance');
        return await this.getById(id);
      }

      values.push(id);

      const query = `UPDATE evolution_instances SET ${updates.join(', ')} WHERE id = ?`;
      logger.info('Executing UPDATE query', { query, values, updates, valuesCount: values.length });

      await pool.query(query, values);

      logger.info('Evolution instance updated successfully', { id });

      return await this.getById(id);
    } catch (error) {
      logger.error('Error updating Evolution instance', { 
        id, 
        error: error.message,
        stack: error.stack,
        data 
      });
      throw error;
    }
  }

  /**
   * Update instance status
   * @param {number} id - Instance ID
   * @param {string} status - New status
   * @param {Object} additionalData - Additional data to update
   * @returns {Promise<Object>} Updated instance
   */
  static async updateStatus(id, status, additionalData = {}) {
    try {
      const data = { status, ...additionalData };
      
      if (status === 'connected') {
        data.last_connected_at = new Date().toISOString().slice(0, 19).replace('T', ' ');
        data.error_message = null;
      }

      return await this.update(id, data);
    } catch (error) {
      logger.error('Error updating instance status', { id, status, error: error.message });
      throw error;
    }
  }

  /**
   * Delete instance (soft delete)
   * @param {number} id - Instance ID
   * @returns {Promise<boolean>} Success status
   */
  static async delete(id) {
    try {
      await pool.query(
        `UPDATE evolution_instances SET is_active = FALSE WHERE id = ?`,
        [id]
      );
      return true;
    } catch (error) {
      logger.error('Error deleting Evolution instance', { id, error: error.message });
      throw error;
    }
  }

  /**
   * Hard delete instance
   * @param {number} id - Instance ID
   * @returns {Promise<boolean>} Success status
   */
  static async hardDelete(id) {
    try {
      await pool.query(
        `DELETE FROM evolution_instances WHERE id = ?`,
        [id]
      );
      return true;
    } catch (error) {
      logger.error('Error hard deleting Evolution instance', { id, error: error.message });
      throw error;
    }
  }

  /**
   * Count instances by tenant
   * @param {number} tenantId - Tenant ID
   * @returns {Promise<number>} Instance count
   */
  static async countByTenant(tenantId) {
    try {
      const [rows] = await pool.query(
        `SELECT COUNT(*) as count FROM evolution_instances WHERE tenant_id = ? AND is_active = TRUE`,
        [tenantId]
      );
      return rows[0].count;
    } catch (error) {
      logger.error('Error counting instances', { tenantId, error: error.message });
      throw error;
    }
  }

  /**
   * Get all active instances
   * @returns {Promise<Array>} Array of active instances
   */
  static async getAllActive() {
    try {
      const [rows] = await pool.query(
        `SELECT * FROM evolution_instances WHERE status = 'connected' AND is_active = TRUE ORDER BY last_connected_at DESC`
      );
      
      return rows.map(row => ({
        ...row,
        webhook_events: row.webhook_events ? JSON.parse(row.webhook_events) : null,
        connection_data: row.connection_data ? JSON.parse(row.connection_data) : null
      }));
    } catch (error) {
      logger.error('Error getting active instances', { error: error.message });
      throw error;
    }
  }

  /**
   * Check if tenant has reached instance limit
   * @param {number} tenantId - Tenant ID
   * @returns {Promise<Object>} Limit check result
   */
  static async checkLimit(tenantId) {
    try {
      const [tenantRows] = await pool.query(
        `SELECT max_whatsapp_instances FROM tenants WHERE id = ?`,
        [tenantId]
      );

      if (tenantRows.length === 0) {
        throw new Error('Tenant not found');
      }

      const maxInstances = tenantRows[0].max_whatsapp_instances || 1;
      const currentCount = await this.countByTenant(tenantId);

      return {
        canCreate: currentCount < maxInstances,
        current: currentCount,
        max: maxInstances,
        remaining: Math.max(0, maxInstances - currentCount)
      };
    } catch (error) {
      logger.error('Error checking instance limit', { tenantId, error: error.message });
      throw error;
    }
  }
}

module.exports = EvolutionInstance;
