import { v } from "convex/values";
import { mutation, query, internalMutation } from "./_generated/server";
import { internal } from "./_generated/api";
import { Id } from "./_generated/dataModel";

/**
 * Helper para obter usuário autenticado
 */
async function getUsuarioAutenticado(ctx: any) {
  const usuariosOnline = await ctx.db.query("usuarios").collect();
  const usuarioOnline = usuariosOnline.find(
    (u: any) => u.statusPresenca === "online"
  );
  return usuarioOnline || null;
}

/**
 * Salvar métricas do sistema
 */
export const salvarMetricas = mutation({
  args: {
    cpuUsage: v.optional(v.number()),
    memoryUsage: v.optional(v.number()),
    networkLatency: v.optional(v.number()),
    storageUsed: v.optional(v.number()),
    usuariosOnline: v.optional(v.number()),
    mensagensPorMinuto: v.optional(v.number()),
    tempoRespostaMedio: v.optional(v.number()),
    errosCount: v.optional(v.number()),
  },
  returns: v.object({
    success: v.boolean(),
    metricId: v.optional(v.id("systemMetrics")),
  }),
  handler: async (ctx, args) => {
    const timestamp = Date.now();

    // Salvar métricas
    const metricId = await ctx.db.insert("systemMetrics", {
      timestamp,
      cpuUsage: args.cpuUsage,
      memoryUsage: args.memoryUsage,
      networkLatency: args.networkLatency,
      storageUsed: args.storageUsed,
      usuariosOnline: args.usuariosOnline,
      mensagensPorMinuto: args.mensagensPorMinuto,
      tempoRespostaMedio: args.tempoRespostaMedio,
      errosCount: args.errosCount,
    });

    // Verificar alertas após salvar métricas
    await ctx.scheduler.runAfter(0, internal.monitoramento.verificarAlertasInternal, {
      metricId,
    });

    // Limpar métricas antigas (mais de 30 dias)
    const dataLimite = Date.now() - 30 * 24 * 60 * 60 * 1000;
    const metricasAntigas = await ctx.db
      .query("systemMetrics")
      .withIndex("by_timestamp", (q) => q.lt("timestamp", dataLimite))
      .collect();

    for (const metrica of metricasAntigas) {
      await ctx.db.delete(metrica._id);
    }

    return {
      success: true,
      metricId,
    };
  },
});

/**
 * Configurar ou atualizar alerta
 */
export const configurarAlerta = mutation({
  args: {
    alertId: v.optional(v.id("alertConfigurations")),
    metricName: v.string(),
    threshold: v.number(),
    operator: v.union(
      v.literal(">"),
      v.literal("<"),
      v.literal(">="),
      v.literal("<="),
      v.literal("==")
    ),
    enabled: v.boolean(),
    notifyByEmail: v.boolean(),
    notifyByChat: v.boolean(),
  },
  returns: v.object({
    success: v.boolean(),
    alertId: v.id("alertConfigurations"),
  }),
  handler: async (ctx, args) => {
    const usuario = await getUsuarioAutenticado(ctx);
    if (!usuario) {
      throw new Error("Não autenticado");
    }

    let alertId: Id<"alertConfigurations">;

    if (args.alertId) {
      // Atualizar alerta existente
      await ctx.db.patch(args.alertId, {
        metricName: args.metricName,
        threshold: args.threshold,
        operator: args.operator,
        enabled: args.enabled,
        notifyByEmail: args.notifyByEmail,
        notifyByChat: args.notifyByChat,
        lastModified: Date.now(),
      });
      alertId = args.alertId;
    } else {
      // Criar novo alerta
      alertId = await ctx.db.insert("alertConfigurations", {
        metricName: args.metricName,
        threshold: args.threshold,
        operator: args.operator,
        enabled: args.enabled,
        notifyByEmail: args.notifyByEmail,
        notifyByChat: args.notifyByChat,
        createdBy: usuario._id,
        lastModified: Date.now(),
      });
    }

    return {
      success: true,
      alertId,
    };
  },
});

/**
 * Listar todas as configurações de alerta
 */
export const listarAlertas = query({
  args: {},
  returns: v.array(
    v.object({
      _id: v.id("alertConfigurations"),
      metricName: v.string(),
      threshold: v.number(),
      operator: v.union(
        v.literal(">"),
        v.literal("<"),
        v.literal(">="),
        v.literal("<="),
        v.literal("==")
      ),
      enabled: v.boolean(),
      notifyByEmail: v.boolean(),
      notifyByChat: v.boolean(),
      createdBy: v.id("usuarios"),
      lastModified: v.number(),
    })
  ),
  handler: async (ctx) => {
    const alertas = await ctx.db.query("alertConfigurations").collect();
    return alertas;
  },
});

/**
 * Obter métricas com filtros
 */
export const obterMetricas = query({
  args: {
    dataInicio: v.optional(v.number()),
    dataFim: v.optional(v.number()),
    metricName: v.optional(v.string()),
    limit: v.optional(v.number()),
  },
  returns: v.array(
    v.object({
      _id: v.id("systemMetrics"),
      timestamp: v.number(),
      cpuUsage: v.optional(v.number()),
      memoryUsage: v.optional(v.number()),
      networkLatency: v.optional(v.number()),
      storageUsed: v.optional(v.number()),
      usuariosOnline: v.optional(v.number()),
      mensagensPorMinuto: v.optional(v.number()),
      tempoRespostaMedio: v.optional(v.number()),
      errosCount: v.optional(v.number()),
    })
  ),
  handler: async (ctx, args) => {
    let query = ctx.db.query("systemMetrics");

    // Filtrar por data se fornecido
    if (args.dataInicio !== undefined || args.dataFim !== undefined) {
      query = query.withIndex("by_timestamp", (q) => {
        if (args.dataInicio !== undefined && args.dataFim !== undefined) {
          return q.gte("timestamp", args.dataInicio).lte("timestamp", args.dataFim);
        } else if (args.dataInicio !== undefined) {
          return q.gte("timestamp", args.dataInicio);
        } else {
          return q.lte("timestamp", args.dataFim!);
        }
      });
    }

    let metricas = await query.order("desc").collect();

    // Limitar resultados
    if (args.limit !== undefined && args.limit > 0) {
      metricas = metricas.slice(0, args.limit);
    }

    return metricas;
  },
});

/**
 * Obter métricas mais recentes (última hora)
 */
export const obterMetricasRecentes = query({
  args: {},
  returns: v.array(
    v.object({
      _id: v.id("systemMetrics"),
      timestamp: v.number(),
      cpuUsage: v.optional(v.number()),
      memoryUsage: v.optional(v.number()),
      networkLatency: v.optional(v.number()),
      storageUsed: v.optional(v.number()),
      usuariosOnline: v.optional(v.number()),
      mensagensPorMinuto: v.optional(v.number()),
      tempoRespostaMedio: v.optional(v.number()),
      errosCount: v.optional(v.number()),
    })
  ),
  handler: async (ctx) => {
    const umaHoraAtras = Date.now() - 60 * 60 * 1000;
    
    const metricas = await ctx.db
      .query("systemMetrics")
      .withIndex("by_timestamp", (q) => q.gte("timestamp", umaHoraAtras))
      .order("desc")
      .take(100);

    return metricas;
  },
});

/**
 * Obter última métrica salva
 */
export const obterUltimaMetrica = query({
  args: {},
  returns: v.union(
    v.object({
      _id: v.id("systemMetrics"),
      timestamp: v.number(),
      cpuUsage: v.optional(v.number()),
      memoryUsage: v.optional(v.number()),
      networkLatency: v.optional(v.number()),
      storageUsed: v.optional(v.number()),
      usuariosOnline: v.optional(v.number()),
      mensagensPorMinuto: v.optional(v.number()),
      tempoRespostaMedio: v.optional(v.number()),
      errosCount: v.optional(v.number()),
    }),
    v.null()
  ),
  handler: async (ctx) => {
    const metrica = await ctx.db
      .query("systemMetrics")
      .order("desc")
      .first();

    return metrica || null;
  },
});

/**
 * Verificar alertas (internal)
 */
export const verificarAlertasInternal = internalMutation({
  args: {
    metricId: v.id("systemMetrics"),
  },
  returns: v.null(),
  handler: async (ctx, args) => {
    const metrica = await ctx.db.get(args.metricId);
    if (!metrica) return null;

    // Buscar configurações de alerta ativas
    const alertasAtivos = await ctx.db
      .query("alertConfigurations")
      .withIndex("by_enabled", (q) => q.eq("enabled", true))
      .collect();

    for (const alerta of alertasAtivos) {
      // Obter valor da métrica correspondente
      const metricValue = (metrica as any)[alerta.metricName];
      
      if (metricValue === undefined) continue;

      // Verificar se o alerta deve ser disparado
      let shouldTrigger = false;
      switch (alerta.operator) {
        case ">":
          shouldTrigger = metricValue > alerta.threshold;
          break;
        case "<":
          shouldTrigger = metricValue < alerta.threshold;
          break;
        case ">=":
          shouldTrigger = metricValue >= alerta.threshold;
          break;
        case "<=":
          shouldTrigger = metricValue <= alerta.threshold;
          break;
        case "==":
          shouldTrigger = metricValue === alerta.threshold;
          break;
      }

      if (shouldTrigger) {
        // Verificar se já existe um alerta triggered recente (últimos 5 minutos)
        const cincoMinutosAtras = Date.now() - 5 * 60 * 1000;
        const alertaRecente = await ctx.db
          .query("alertHistory")
          .withIndex("by_config", (q) => 
            q.eq("configId", alerta._id).gte("timestamp", cincoMinutosAtras)
          )
          .filter((q) => q.eq(q.field("status"), "triggered"))
          .first();

        // Se já existe alerta recente, não disparar novamente
        if (alertaRecente) continue;

        // Registrar alerta no histórico
        await ctx.db.insert("alertHistory", {
          configId: alerta._id,
          metricName: alerta.metricName,
          metricValue,
          threshold: alerta.threshold,
          timestamp: Date.now(),
          status: "triggered",
          notificationsSent: {
            email: alerta.notifyByEmail,
            chat: alerta.notifyByChat,
          },
        });

        // Criar notificação no chat se configurado
        if (alerta.notifyByChat) {
          // Buscar usuários TI para notificar
          const usuarios = await ctx.db.query("usuarios").collect();
          const usuariosTI = usuarios.filter(
            (u: any) => u.role?.nome === "ti" || u.role?.nivel === 0
          );

          for (const usuario of usuariosTI) {
            await ctx.db.insert("notificacoes", {
              usuarioId: usuario._id,
              tipo: "nova_mensagem",
              titulo: `⚠️ Alerta de Sistema: ${alerta.metricName}`,
              descricao: `Métrica ${alerta.metricName} está em ${metricValue.toFixed(2)}% (limite: ${alerta.threshold}%)`,
              lida: false,
              criadaEm: Date.now(),
            });
          }
        }

        // TODO: Enviar email se configurado (integração com sistema de email)
        // if (alerta.notifyByEmail) {
        //   await enviarEmailAlerta(alerta, metricValue);
        // }
      }
    }

    return null;
  },
});

/**
 * Gerar relatório de métricas
 */
export const gerarRelatorio = query({
  args: {
    dataInicio: v.number(),
    dataFim: v.number(),
    metricNames: v.optional(v.array(v.string())),
  },
  returns: v.object({
    periodo: v.object({
      inicio: v.number(),
      fim: v.number(),
    }),
    metricas: v.array(
      v.object({
        _id: v.id("systemMetrics"),
        timestamp: v.number(),
        cpuUsage: v.optional(v.number()),
        memoryUsage: v.optional(v.number()),
        networkLatency: v.optional(v.number()),
        storageUsed: v.optional(v.number()),
        usuariosOnline: v.optional(v.number()),
        mensagensPorMinuto: v.optional(v.number()),
        tempoRespostaMedio: v.optional(v.number()),
        errosCount: v.optional(v.number()),
      })
    ),
    estatisticas: v.object({
      cpuUsage: v.optional(v.object({
        min: v.number(),
        max: v.number(),
        avg: v.number(),
      })),
      memoryUsage: v.optional(v.object({
        min: v.number(),
        max: v.number(),
        avg: v.number(),
      })),
      networkLatency: v.optional(v.object({
        min: v.number(),
        max: v.number(),
        avg: v.number(),
      })),
      storageUsed: v.optional(v.object({
        min: v.number(),
        max: v.number(),
        avg: v.number(),
      })),
      usuariosOnline: v.optional(v.object({
        min: v.number(),
        max: v.number(),
        avg: v.number(),
      })),
      mensagensPorMinuto: v.optional(v.object({
        min: v.number(),
        max: v.number(),
        avg: v.number(),
      })),
      tempoRespostaMedio: v.optional(v.object({
        min: v.number(),
        max: v.number(),
        avg: v.number(),
      })),
      errosCount: v.optional(v.object({
        min: v.number(),
        max: v.number(),
        avg: v.number(),
      })),
    }),
  }),
  handler: async (ctx, args) => {
    // Buscar métricas no período
    const metricas = await ctx.db
      .query("systemMetrics")
      .withIndex("by_timestamp", (q) =>
        q.gte("timestamp", args.dataInicio).lte("timestamp", args.dataFim)
      )
      .collect();

    // Calcular estatísticas
    const calcularEstatisticas = (
      valores: number[]
    ): { min: number; max: number; avg: number } | undefined => {
      if (valores.length === 0) return undefined;
      return {
        min: Math.min(...valores),
        max: Math.max(...valores),
        avg: valores.reduce((a, b) => a + b, 0) / valores.length,
      };
    };

    const estatisticas = {
      cpuUsage: calcularEstatisticas(
        metricas.map((m) => m.cpuUsage).filter((v) => v !== undefined) as number[]
      ),
      memoryUsage: calcularEstatisticas(
        metricas.map((m) => m.memoryUsage).filter((v) => v !== undefined) as number[]
      ),
      networkLatency: calcularEstatisticas(
        metricas.map((m) => m.networkLatency).filter((v) => v !== undefined) as number[]
      ),
      storageUsed: calcularEstatisticas(
        metricas.map((m) => m.storageUsed).filter((v) => v !== undefined) as number[]
      ),
      usuariosOnline: calcularEstatisticas(
        metricas.map((m) => m.usuariosOnline).filter((v) => v !== undefined) as number[]
      ),
      mensagensPorMinuto: calcularEstatisticas(
        metricas.map((m) => m.mensagensPorMinuto).filter((v) => v !== undefined) as number[]
      ),
      tempoRespostaMedio: calcularEstatisticas(
        metricas.map((m) => m.tempoRespostaMedio).filter((v) => v !== undefined) as number[]
      ),
      errosCount: calcularEstatisticas(
        metricas.map((m) => m.errosCount).filter((v) => v !== undefined) as number[]
      ),
    };

    return {
      periodo: {
        inicio: args.dataInicio,
        fim: args.dataFim,
      },
      metricas,
      estatisticas,
    };
  },
});

/**
 * Deletar configuração de alerta
 */
export const deletarAlerta = mutation({
  args: {
    alertId: v.id("alertConfigurations"),
  },
  returns: v.object({
    success: v.boolean(),
  }),
  handler: async (ctx, args) => {
    await ctx.db.delete(args.alertId);
    return { success: true };
  },
});

/**
 * Obter histórico de alertas
 */
export const obterHistoricoAlertas = query({
  args: {
    limit: v.optional(v.number()),
  },
  returns: v.array(
    v.object({
      _id: v.id("alertHistory"),
      configId: v.id("alertConfigurations"),
      metricName: v.string(),
      metricValue: v.number(),
      threshold: v.number(),
      timestamp: v.number(),
      status: v.union(v.literal("triggered"), v.literal("resolved")),
      notificationsSent: v.object({
        email: v.boolean(),
        chat: v.boolean(),
      }),
    })
  ),
  handler: async (ctx, args) => {
    const limit = args.limit || 50;
    
    const historico = await ctx.db
      .query("alertHistory")
      .order("desc")
      .take(limit);

    return historico;
  },
});
