import { v } from "convex/values";
import { mutation, query, internalMutation, internalQuery, action } from "./_generated/server";
import { internal, api } from "./_generated/api";
import { renderizarTemplate } from "./templatesMensagens";
import type { Doc, Id } from "./_generated/dataModel";

// ========== INTERNAL QUERIES ==========

/**
 * Obter email por ID (internal query)
 */
export const getEmailById = internalQuery({
  args: {
    emailId: v.id("notificacoesEmail"),
  },
  handler: async (ctx, args) => {
    return await ctx.db.get(args.emailId);
  },
});

/**
 * Obter configuração SMTP ativa (internal query)
 */
export const getActiveEmailConfig = internalQuery({
  args: {},
  handler: async (ctx) => {
    const config = await ctx.db
      .query("configuracaoEmail")
      .withIndex("by_ativo", (q) => q.eq("ativo", true))
      .first();

    return config;
  },
});

/**
 * Listar emails pendentes (internal query)
 */
export const listarEmailsPendentes = internalQuery({
  args: {
    limite: v.optional(v.number()),
  },
  handler: async (ctx, args) => {
    const emails = await ctx.db
      .query("notificacoesEmail")
      .withIndex("by_status", (q) => q.eq("status", "pendente"))
      .order("asc") // Mais antigos primeiro
      .take(args.limite || 10);

    return emails;
  },
});

// ========== INTERNAL MUTATIONS ==========

/**
 * Marcar email como enviando (internal mutation)
 */
export const markEmailEnviando = internalMutation({
  args: {
    emailId: v.id("notificacoesEmail"),
  },
  handler: async (ctx, args) => {
    const email = await ctx.db.get(args.emailId);
    if (!email) return;

    await ctx.db.patch(args.emailId, {
      status: "enviando",
      ultimaTentativa: Date.now(),
      tentativas: email.tentativas + 1,
    });
  },
});

/**
 * Marcar email como enviado (internal mutation)
 */
export const markEmailEnviado = internalMutation({
  args: {
    emailId: v.id("notificacoesEmail"),
  },
  handler: async (ctx, args) => {
    await ctx.db.patch(args.emailId, {
      status: "enviado",
      enviadoEm: Date.now(),
    });
  },
});

/**
 * Marcar email como falha (internal mutation)
 */
export const markEmailFalha = internalMutation({
  args: {
    emailId: v.id("notificacoesEmail"),
    erro: v.string(),
  },
  handler: async (ctx, args) => {
    await ctx.db.patch(args.emailId, {
      status: "falha",
      erroDetalhes: args.erro,
      ultimaTentativa: Date.now(),
    });
  },
});

// ========== PUBLIC MUTATIONS ==========

/**
 * Enfileirar email para envio assíncrono
 */
export const enfileirarEmail = mutation({
  args: {
    destinatario: v.string(),
    destinatarioId: v.optional(v.id("usuarios")),
    assunto: v.string(),
    corpo: v.string(),
    templateId: v.optional(v.id("templatesMensagens")),
    enviadoPor: v.id("usuarios"), // Obrigatório conforme schema
    agendadaPara: v.optional(v.number()), // timestamp opcional para agendamento
  },
  handler: async (ctx, args) => {
    // Validar agendamento se fornecido
    if (args.agendadaPara !== undefined && args.agendadaPara <= Date.now()) {
      throw new Error("Data de agendamento deve ser futura");
    }

    const emailId = await ctx.db.insert("notificacoesEmail", {
      destinatario: args.destinatario,
      destinatarioId: args.destinatarioId,
      assunto: args.assunto,
      corpo: args.corpo,
      templateId: args.templateId,
      status: "pendente",
      tentativas: 0,
      criadoEm: Date.now(),
      enviadoPor: args.enviadoPor,
      agendadaPara: args.agendadaPara,
    });

    // O cron job processará emails automaticamente:
    // - Emails sem agendamento serão processados imediatamente (próxima execução do cron)
    // - Emails agendados serão processados quando a hora chegar

    return emailId;
  },
});

/**
 * Enviar email usando template
 */
export const enviarEmailComTemplate = action({
  args: {
    destinatario: v.string(),
    destinatarioId: v.optional(v.id("usuarios")),
    templateCodigo: v.string(),
    variaveis: v.optional(v.record(v.string(), v.string())),
    enviadoPor: v.id("usuarios"), // Obrigatório conforme schema
    agendadaPara: v.optional(v.number()), // timestamp opcional para agendamento
  },
  handler: async (ctx, args): Promise<Id<"notificacoesEmail">> => {
    // Buscar template
    const template: Doc<"templatesMensagens"> | null = await ctx.runQuery(
      api.templatesMensagens.obterTemplatePorCodigo,
      {
        codigo: args.templateCodigo,
      }
    );

    if (!template) {
      throw new Error(`Template não encontrado: ${args.templateCodigo}`);
    }

    // Renderizar template com variáveis
    const variaveisTemplate = args.variaveis || {};
    const tituloRenderizado = renderizarTemplate(template.titulo, variaveisTemplate);
    const corpoRenderizado = renderizarTemplate(template.corpo, variaveisTemplate);

    // Enfileirar email via mutation
    const emailId: Id<"notificacoesEmail"> = await ctx.runMutation(api.email.enfileirarEmail, {
      destinatario: args.destinatario,
      destinatarioId: args.destinatarioId,
      assunto: tituloRenderizado,
      corpo: corpoRenderizado,
      templateId: template._id, // template._id sempre existe se template não é null
      enviadoPor: args.enviadoPor,
      agendadaPara: args.agendadaPara,
    });

    if (!emailId) {
      throw new Error("Erro ao enfileirar email: ID não retornado");
    }

    return emailId;
  },
});

// ========== INTERNAL MUTATION (CRON) ==========

/**
 * Processar fila de emails pendentes (chamado pelo cron)
 */
export const processarFilaEmails = internalMutation({
  args: {},
  handler: async (ctx) => {
    const agora = Date.now();
    
    // Buscar emails pendentes que devem ser processados agora
    // (sem agendamento OU com agendamento que já passou)
    const emailsParaProcessar = await ctx.db
      .query("notificacoesEmail")
      .filter((q) => {
        const statusPendente = q.eq(q.field("status"), "pendente");
        const semAgendamento = q.eq(q.field("agendadaPara"), undefined);
        const agendamentoJaPassou = q.and(
          q.neq(q.field("agendadaPara"), undefined),
          q.lte(q.field("agendadaPara"), agora)
        );
        
        return q.and(
          statusPendente,
          q.or(semAgendamento, agendamentoJaPassou)
        );
      })
      .order("asc") // Mais antigos primeiro
      .take(10);

    if (emailsParaProcessar.length === 0) {
      return { processados: 0 };
    }

    // Agendar envio de cada email via action
    for (const email of emailsParaProcessar) {
      // Agendar envio assíncrono (não bloqueia o cron)
      ctx.scheduler.runAfter(0, api.actions.email.enviar, {
        emailId: email._id,
      }).catch((error: unknown) => {
        const errorMessage = error instanceof Error ? error.message : String(error);
        console.error(`Erro ao agendar envio de email ${email._id}:`, errorMessage);
      });
    }

    return { processados: emailsParaProcessar.length };
  }
});

// ========== QUERIES ==========

/**
 * Listar emails da fila (para monitoramento)
 */
export const listarFilaEmails = query({
  args: {
    limite: v.optional(v.number()),
    status: v.optional(v.union(
      v.literal("pendente"),
      v.literal("enviando"),
      v.literal("enviado"),
      v.literal("falha")
    )),
    _refresh: v.optional(v.number()), // Parâmetro ignorado, usado apenas para forçar refresh no frontend
  },
  handler: async (ctx, args) => {
    let emails;

    // Filtrar por status se fornecido
    if (args.status) {
      emails = await ctx.db
        .query("notificacoesEmail")
        .withIndex("by_status", (q) => q.eq("status", args.status!))
        .order("desc")
        .take(args.limite || 50);
    } else {
      // Sem filtro, buscar todos e ordenar por data de criação
      const todosEmails = await ctx.db.query("notificacoesEmail").collect();
      todosEmails.sort((a, b) => b.criadoEm - a.criadoEm);
      emails = todosEmails.slice(0, args.limite || 50);
    }

    return emails;
  },
});

/**
 * Obter estatísticas da fila de emails (para debug e monitoramento)
 */
export const obterEstatisticasFilaEmails = query({
  args: {
    _refresh: v.optional(v.number()), // Parâmetro ignorado, usado apenas para forçar refresh no frontend
  },
  returns: v.object({
    pendentes: v.number(),
    enviando: v.number(),
    enviados: v.number(),
    falhas: v.number(),
    total: v.number(),
  }),
  handler: async (ctx) => {
    const todosEmails = await ctx.db.query("notificacoesEmail").collect();
    
    const estatisticas = {
      pendentes: 0,
      enviando: 0,
      enviados: 0,
      falhas: 0,
      total: todosEmails.length,
    };

    for (const email of todosEmails) {
      switch (email.status) {
        case "pendente":
          estatisticas.pendentes++;
          break;
        case "enviando":
          estatisticas.enviando++;
          break;
        case "enviado":
          estatisticas.enviados++;
          break;
        case "falha":
          estatisticas.falhas++;
          break;
      }
    }

    return estatisticas;
  },
});

// ========== PUBLIC MUTATIONS (MANUAL) ==========

/**
 * Processar fila de emails manualmente (para uso em interface)
 */
export const processarFilaEmailsManual = action({
  args: {
    limite: v.optional(v.number()),
  },
  returns: v.object({
    sucesso: v.boolean(),
    processados: v.number(),
    falhas: v.number(),
    erro: v.optional(v.string()),
  }),
  handler: async (ctx, args) => {
    try {
      // Buscar emails pendentes
      const emailsPendentes = await ctx.runQuery(internal.email.listarEmailsPendentes, {
        limite: args.limite || 10,
      });

      if (emailsPendentes.length === 0) {
        return { sucesso: true, processados: 0, falhas: 0 };
      }

      let processados = 0;
      let falhas = 0;

      // Processar cada email
      for (const email of emailsPendentes) {
        try {
          // Agendar envio via action
          await ctx.scheduler.runAfter(0, api.actions.email.enviar, {
            emailId: email._id,
          });
          processados++;
        } catch (error: unknown) {
          const errorMessage = error instanceof Error ? error.message : String(error);
          console.error(`Erro ao agendar envio de email ${email._id}:`, errorMessage);
          falhas++;
        }
      }

      return { sucesso: true, processados, falhas };
    } catch (error: unknown) {
      const errorMessage = error instanceof Error ? error.message : String(error);
      return {
        sucesso: false,
        processados: 0,
        falhas: 0,
        erro: errorMessage,
      };
    }
  },
});
