import type { Database } from 'bun:sqlite';
import { getDb } from '../db/locator';
import { toIsoSqlUTC } from '../utils/datetime';
import { Metrics } from './metrics';

export class MaintenanceService {
  private static _timer: any = null;
  private static _healthCheckTimer: any = null;
  private static _lastRestartAttempt: number = 0;
  private static _lastEvolutionState: string | null = null;
  private static _lastStateChangeTs: number = 0;
  private static readonly _processStartSec: number = Math.floor(Date.now() / 1000);

  private static get retentionDays(): number {
    const v = Number(process.env.GROUP_MEMBERS_INACTIVE_RETENTION_DAYS);
    if (Number.isFinite(v)) return v;
    return 180; // por defecto 180 días
  }

  private static get evolutionApiConfig() {
    return {
      url: process.env.EVOLUTION_API_URL,
      instance: process.env.EVOLUTION_API_INSTANCE,
      apiKey: process.env.EVOLUTION_API_KEY,
      intervalMs: Number(process.env.HEALTH_CHECK_INTERVAL_MS || '60000'), // 1 min por defecto
      restartCooldownMs: Number(process.env.HEALTH_CHECK_RESTART_COOLDOWN_MS || '900000'), // 15 min por defecto
    };
  }

  static start(): void {
    if (process.env.NODE_ENV === 'test' && process.env.FORCE_SCHEDULERS !== 'true') return;

    // --- Tareas diarias existentes ---
    if (this.retentionDays > 0) {
      const intervalMs = 24 * 60 * 60 * 1000; // diario
      this._timer = setInterval(() => {
        this.cleanupInactiveMembersOnce().catch(err => {
          console.error('❌ Error en cleanup de miembros inactivos:', err);
        });
        this.reconcileAliasUsersOnce().catch(err => {
          console.error('❌ Error en reconcile de alias de usuarios:', err);
        });
      }, intervalMs);
    }

    // --- Nuevo Health Check de Evolution API ---
    const { url, instance, apiKey, intervalMs } = this.evolutionApiConfig;
    if (url && instance && apiKey) {
      console.log('[MaintenanceService] Iniciando health check de Evolution API...');
      this._healthCheckTimer = setInterval(() => {
        this.performEvolutionHealthCheck().catch(err => {
          console.error('❌ Error en el health check de Evolution API:', err);
        });
      }, intervalMs);
    } else {
      console.warn('[MaintenanceService] Variables de entorno para el health check de Evolution API (URL, INSTANCE, API_KEY) no encontradas. Health check desactivado.');
    }
  }

  static stop(): void {
    if (this._timer) {
      clearInterval(this._timer);
      this._timer = null;
    }
    if (this._healthCheckTimer) {
      clearInterval(this._healthCheckTimer);
      this._healthCheckTimer = null;
    }
  }

  static async cleanupInactiveMembersOnce(instance?: Database, retentionDays: number = this.retentionDays): Promise<number> {
    if (retentionDays <= 0) return 0;
    const threshold = toIsoSqlUTC(new Date(Date.now() - retentionDays * 24 * 60 * 60 * 1000));
    const dbi = ((instance ?? getDb()) as Database);
    const res = dbi.prepare(`
      DELETE FROM group_members
      WHERE is_active = 0
        AND last_seen_at < ?
    `).run(threshold);
    const deleted = Number(res?.changes || 0);
    return deleted;
  }

  /**
   * Reconciliación de usuarios: fusiona IDs alias (LID u opacos) hacia el número real
   * en todas las tablas relevantes, basándose en user_aliases.
   * Devuelve el número de alias procesados.
   */
  static async reconcileAliasUsersOnce(instance?: Database): Promise<number> {
    try {
      const dbi = ((instance ?? getDb()) as Database);
      const rows = dbi.prepare(`SELECT alias, user_id FROM user_aliases WHERE alias != user_id`).all() as any[];
      let merged = 0;

      for (const r of rows) {
        const alias = String(r.alias);
        const real = String(r.user_id);

        dbi.transaction(() => {
          // Asegurar existencia del usuario real
          try {
            dbi.prepare(`INSERT OR IGNORE INTO users (id) VALUES (?)`)
              .run(real);
          } catch {}

          const updates = [
            `UPDATE tasks SET created_by = ? WHERE created_by = ?`,
            `UPDATE task_assignments SET user_id = ? WHERE user_id = ?`,
            `UPDATE task_assignments SET assigned_by = ? WHERE assigned_by = ?`,
            `UPDATE user_preferences SET user_id = ? WHERE user_id = ?`,
            `UPDATE web_tokens SET user_id = ? WHERE user_id = ?`,
            `UPDATE group_members SET user_id = ? WHERE user_id = ?`
          ];

          for (const sql of updates) {
            try {
              dbi.prepare(sql).run(real, alias);
            } catch {
              // Ignorar si la tabla no existe en este despliegue
            }
          }

          // Intentar eliminar el usuario alias si ya no tiene referencias
          try {
            dbi.prepare(`DELETE FROM users WHERE id = ?`).run(alias);
          } catch {}
        })();

        merged++;
      }

      return merged;
    } catch {
      // Si no existe la tabla user_aliases o hay error de DB, no hacemos nada
      return 0;
    }
  }

  /**
   * Verifica el estado de la instancia de Evolution API y la reinicia si es necesario.
   */
  private static async performEvolutionHealthCheck(): Promise<void> {
    const { url, instance, apiKey, restartCooldownMs } = this.evolutionApiConfig;

    const stateUrl = `${url}/instance/connectionState/${instance}`;
    const restartUrl = `${url}/instance/restart/${instance}`;
    const headers: HeadersInit = { apikey: String(apiKey || '') };

    const recordState = (newState: string) => {
      const prev = this._lastEvolutionState;
      const nowSec = Math.floor(Date.now() / 1000);

      // Actualizar métricas de estado (1 para el actual; 0 para el anterior si cambió)
      try {
        Metrics.set('evolution_instance_state', 1, { instance: String(instance || ''), state: newState });
        if (prev && prev !== newState) {
          Metrics.set('evolution_instance_state', 0, { instance: String(instance || ''), state: prev });
        }
      } catch {}

      // Logging solo en primer muestreo o cuando cambie
      if (!prev) {
        console.log(`[HealthCheck] Estado inicial de la instancia '${instance}': ${newState}`);
        this._lastStateChangeTs = this._processStartSec;
        try { Metrics.set('evolution_instance_last_state_change_ts', this._processStartSec, { instance: String(instance || '') }); } catch {}
      } else if (prev !== newState) {
        console.log(`[HealthCheck] Cambio de estado en instancia '${instance}': ${prev} → ${newState}`);
        this._lastStateChangeTs = nowSec;
        try {
          Metrics.set('evolution_instance_last_state_change_ts', nowSec, { instance: String(instance || '') });
          Metrics.inc('evolution_instance_state_changes_total', 1, { instance: String(instance || '') });
        } catch {}
      }

      this._lastEvolutionState = newState;
      try {
        Metrics.set('evolution_instance_state_age_seconds', Math.max(0, nowSec - this._lastStateChangeTs), { instance: String(instance || '') });
      } catch {}
    };

    try {
      const response = await fetch(stateUrl, { method: 'GET', headers });

      if (!response.ok) {
        console.error(`[HealthCheck] Error al consultar estado de Evolution API: ${response.status} ${response.statusText}`);
        try { Metrics.inc('evolution_health_check_errors_total', 1, { instance: String(instance || '') }); } catch {}
        // Registrar estado como 'unreachable' (sin intentar reinicio aquí)
        recordState('unreachable');
        return;
      }

      const data = await response.json();
      const currentState = String(data?.instance?.state ?? 'unknown');

      // Registrar estado y métricas (sin spam de logs si no cambia)
      recordState(currentState);

      // Intentar reinicio si no está 'open' y ha pasado el cooldown
      if (currentState !== 'open') {
        const now = Date.now();
        if (now - this._lastRestartAttempt > restartCooldownMs) {
          console.warn(`[HealthCheck] La instancia no está 'open' (estado: ${currentState}). Intentando reiniciar...`);
          try {
            try { Metrics.inc('evolution_instance_restart_attempts_total', 1, { instance: String(instance || '') }); } catch {}
            const restartResponse = await fetch(restartUrl, { method: 'PUT', headers });
            if (restartResponse.ok) {
              console.log(`[HealthCheck] Petición de reinicio enviada exitosamente para '${instance}'.`);
              try { Metrics.inc('evolution_instance_restart_success_total', 1, { instance: String(instance || '') }); } catch {}
              this._lastRestartAttempt = now;
            } else {
              console.error(`[HealthCheck] Fallo al reiniciar la instancia. Status: ${restartResponse.status} ${restartResponse.statusText}`);
            }
          } catch (restartError) {
            console.error('[HealthCheck] Error de red al intentar reiniciar la instancia:', restartError);
          }
        } else {
          // Reducir ruido: no loguear en cada intervalo si seguimos en el mismo estado
        }
      }
    } catch (error) {
      console.error('[HealthCheck] Error de red o inesperado al verificar el estado de la Evolution API:', error);
      try { Metrics.inc('evolution_health_check_errors_total', 1, { instance: String(instance || '') }); } catch {}
      recordState('unreachable');
    }
  }
}
