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

// Query: Listar todas as solicitações (para RH)
export const listarTodas = query({
  args: {},
  handler: async (ctx) => {
    const solicitacoes = await ctx.db.query("solicitacoesAusencias").collect();

    const solicitacoesComDetalhes = await Promise.all(
      solicitacoes.map(async (s) => {
        const funcionario = await ctx.db.get(s.funcionarioId);

        // Buscar time do funcionário
        const membroTime = await ctx.db
          .query("timesMembros")
          .withIndex("by_funcionario", (q) =>
            q.eq("funcionarioId", s.funcionarioId)
          )
          .filter((q) => q.eq(q.field("ativo"), true))
          .first();

        let time = null;
        if (membroTime) {
          time = await ctx.db.get(membroTime.timeId);
        }

        return {
          ...s,
          funcionario,
          time,
        };
      })
    );

    return solicitacoesComDetalhes.sort(
      (a, b) => b.criadoEm - a.criadoEm
    );
  },
});

// Query: Listar solicitações do funcionário
export const listarMinhasSolicitacoes = query({
  args: { funcionarioId: v.id("funcionarios") },
  handler: async (ctx, args) => {
    const solicitacoes = await ctx.db
      .query("solicitacoesAusencias")
      .withIndex("by_funcionario", (q) =>
        q.eq("funcionarioId", args.funcionarioId)
      )
      .order("desc")
      .collect();

    // Enriquecer com dados do funcionário e time
    const solicitacoesComDetalhes = await Promise.all(
      solicitacoes.map(async (s) => {
        const funcionario = await ctx.db.get(s.funcionarioId);
        
        // Buscar time do funcionário
        const membroTime = await ctx.db
          .query("timesMembros")
          .withIndex("by_funcionario", (q) =>
            q.eq("funcionarioId", s.funcionarioId)
          )
          .filter((q) => q.eq(q.field("ativo"), true))
          .first();

        let time = null;
        if (membroTime) {
          time = await ctx.db.get(membroTime.timeId);
        }

        return {
          ...s,
          funcionario,
          time,
        };
      })
    );

    return solicitacoesComDetalhes;
  },
});

// Query: Listar solicitações dos subordinados (para gestores)
export const listarSolicitacoesSubordinados = query({
  args: { gestorId: v.id("usuarios") },
  handler: async (ctx, args) => {
    // Buscar times onde o usuário é gestor
    const timesGestor = await ctx.db
      .query("times")
      .withIndex("by_gestor", (q) => q.eq("gestorId", args.gestorId))
      .filter((q) => q.eq(q.field("ativo"), true))
      .collect();

    const solicitacoes: Array<Doc<"solicitacoesAusencias"> & {
      funcionario: Doc<"funcionarios"> | null;
      time: Doc<"times"> | null;
    }> = [];

    for (const time of timesGestor) {
      // Buscar membros do time
      const membros = await ctx.db
        .query("timesMembros")
        .withIndex("by_time_and_ativo", (q) =>
          q.eq("timeId", time._id).eq("ativo", true)
        )
        .collect();

      // Buscar solicitações de cada membro
      for (const membro of membros) {
        const solic = await ctx.db
          .query("solicitacoesAusencias")
          .withIndex("by_funcionario", (q) =>
            q.eq("funcionarioId", membro.funcionarioId)
          )
          .collect();

        // Adicionar info do funcionário
        for (const s of solic) {
          const funcionario = await ctx.db.get(s.funcionarioId);
          solicitacoes.push({
            ...s,
            funcionario,
            time,
          });
        }
      }
    }

    return solicitacoes.sort((a, b) => b.criadoEm - a.criadoEm);
  },
});

// Query: Obter detalhes completos de uma solicitação
export const obterDetalhes = query({
  args: { solicitacaoId: v.id("solicitacoesAusencias") },
  handler: async (ctx, args) => {
    const solicitacao = await ctx.db.get(args.solicitacaoId);
    if (!solicitacao) return null;

    const funcionario = await ctx.db.get(solicitacao.funcionarioId);
    let gestor = null;
    if (solicitacao.gestorId) {
      gestor = await ctx.db.get(solicitacao.gestorId);
    }

    // Buscar time do funcionário
    const membroTime = await ctx.db
      .query("timesMembros")
      .withIndex("by_funcionario", (q) =>
        q.eq("funcionarioId", solicitacao.funcionarioId)
      )
      .filter((q) => q.eq(q.field("ativo"), true))
      .first();

    let time = null;
    if (membroTime) {
      time = await ctx.db.get(membroTime.timeId);
    }

    return {
      ...solicitacao,
      funcionario,
      gestor,
      time,
    };
  },
});

// Query: Obter notificações não lidas
export const obterNotificacoesNaoLidas = query({
  args: { usuarioId: v.id("usuarios") },
  handler: async (ctx, args) => {
    const notificacoes = await ctx.db
      .query("notificacoesAusencias")
      .withIndex("by_destinatario_and_lida", (q) =>
        q.eq("destinatarioId", args.usuarioId).eq("lida", false)
      )
      .order("desc")
      .collect();

    return notificacoes;
  },
});

// Query: Contar solicitações pendentes para gestor
export const contarPendentesGestor = query({
  args: { gestorId: v.id("usuarios") },
  handler: async (ctx, args) => {
    // Buscar times onde o usuário é gestor
    const timesGestor = await ctx.db
      .query("times")
      .withIndex("by_gestor", (q) => q.eq("gestorId", args.gestorId))
      .filter((q) => q.eq(q.field("ativo"), true))
      .collect();

    let totalPendentes = 0;

    for (const time of timesGestor) {
      // Buscar membros do time
      const membros = await ctx.db
        .query("timesMembros")
        .withIndex("by_time_and_ativo", (q) =>
          q.eq("timeId", time._id).eq("ativo", true)
        )
        .collect();

      // Contar solicitações pendentes de cada membro
      for (const membro of membros) {
        const pendentes = await ctx.db
          .query("solicitacoesAusencias")
          .withIndex("by_funcionario_and_status", (q) =>
            q
              .eq("funcionarioId", membro.funcionarioId)
              .eq("status", "aguardando_aprovacao")
          )
          .collect();
        totalPendentes += pendentes.length;
      }
    }

    return totalPendentes;
  },
});

// Helper: Verificar se há sobreposição de datas
function verificarSobreposicao(
  inicio1: string,
  fim1: string,
  inicio2: string,
  fim2: string
): boolean {
  const d1Inicio = new Date(inicio1);
  const d1Fim = new Date(fim1);
  const d2Inicio = new Date(inicio2);
  const d2Fim = new Date(fim2);

  return d1Inicio <= d2Fim && d2Inicio <= d1Fim;
}

// Helper: Encontrar gestor do funcionário
async function encontrarGestorDoFuncionario(
  ctx: QueryCtx | MutationCtx,
  funcionarioId: Id<"funcionarios">
): Promise<Id<"usuarios"> | null> {
  const membroTime = await ctx.db
    .query("timesMembros")
    .withIndex("by_funcionario", (q) => q.eq("funcionarioId", funcionarioId))
    .filter((q) => q.eq(q.field("ativo"), true))
    .first();

  if (!membroTime) return null;

  const time = await ctx.db.get(membroTime.timeId);
  if (!time) return null;

  return time.gestorId;
}

// Mutation: Criar solicitação de ausência
export const criarSolicitacao = mutation({
  args: {
    funcionarioId: v.id("funcionarios"),
    dataInicio: v.string(),
    dataFim: v.string(),
    motivo: v.string(),
  },
  returns: v.id("solicitacoesAusencias"),
  handler: async (ctx, args) => {
    // Validações
    if (args.motivo.trim().length < 10) {
      throw new Error("O motivo deve ter no mínimo 10 caracteres");
    }

    const dataInicio = new Date(args.dataInicio);
    const dataFim = new Date(args.dataFim);
    const hoje = new Date();
    hoje.setHours(0, 0, 0, 0);

    if (dataInicio < hoje) {
      throw new Error("A data de início não pode ser no passado");
    }

    if (dataFim < dataInicio) {
      throw new Error("A data de fim deve ser maior ou igual à data de início");
    }

    const funcionario = await ctx.db.get(args.funcionarioId);
    if (!funcionario) {
      throw new Error("Funcionário não encontrado");
    }

    // Verificar sobreposição com outras solicitações aprovadas ou pendentes
    const solicitacoesExistentes = await ctx.db
      .query("solicitacoesAusencias")
      .withIndex("by_funcionario", (q) =>
        q.eq("funcionarioId", args.funcionarioId)
      )
      .collect();

    for (const solic of solicitacoesExistentes) {
      if (
        solic.status === "aprovado" ||
        solic.status === "aguardando_aprovacao"
      ) {
        if (
          verificarSobreposicao(
            args.dataInicio,
            args.dataFim,
            solic.dataInicio,
            solic.dataFim
          )
        ) {
          throw new Error(
            "Já existe uma solicitação aprovada ou pendente para este período"
          );
        }
      }
    }

    // Criar solicitação
    const solicitacaoId = await ctx.db.insert("solicitacoesAusencias", {
      funcionarioId: args.funcionarioId,
      dataInicio: args.dataInicio,
      dataFim: args.dataFim,
      motivo: args.motivo.trim(),
      status: "aguardando_aprovacao",
      criadoEm: Date.now(),
    });

    // Encontrar gestor do funcionário
    const gestorId = await encontrarGestorDoFuncionario(
      ctx,
      args.funcionarioId
    );

    if (gestorId) {
      // Criar notificação in-app para gestor
      await ctx.db.insert("notificacoesAusencias", {
        destinatarioId: gestorId,
        solicitacaoAusenciaId: solicitacaoId,
        tipo: "nova_solicitacao",
        lida: false,
        mensagem: `${funcionario.nome} solicitou uma ausência de ${new Date(args.dataInicio).toLocaleDateString("pt-BR")} até ${new Date(args.dataFim).toLocaleDateString("pt-BR")}`,
      });

      // Buscar usuário do gestor para enviar email e chat
      const gestorUsuario = await ctx.db.get(gestorId);
      const funcionarioUsuario = await ctx.db
        .query("usuarios")
        .withIndex("by_funcionarioId", (q) =>
          q.eq("funcionarioId", args.funcionarioId)
        )
        .first();

      if (gestorUsuario && funcionarioUsuario) {
        // Enviar email ao gestor
        await ctx.runMutation(api.email.enfileirarEmail, {
          destinatario: gestorUsuario.email,
          destinatarioId: gestorId,
          assunto: `Nova Solicitação de Ausência - ${funcionario.nome}`,
          corpo: `<p>Olá ${gestorUsuario.nome},</p>
            <p>O funcionário <strong>${funcionario.nome}</strong> solicitou uma ausência:</p>
            <ul>
              <li><strong>Período:</strong> ${new Date(args.dataInicio).toLocaleDateString("pt-BR")} até ${new Date(args.dataFim).toLocaleDateString("pt-BR")}</li>
              <li><strong>Motivo:</strong> ${args.motivo}</li>
            </ul>
            <p>Por favor, acesse o sistema para aprovar ou reprovar esta solicitação.</p>`,
          enviadoPor: funcionarioUsuario._id,
        });

        // Criar ou obter conversa entre gestor e funcionário
        const conversasExistentes = await ctx.db
          .query("conversas")
          .filter((q) => q.eq(q.field("tipo"), "individual"))
          .collect();

        let conversaId: Id<"conversas"> | null = null;
        for (const conversa of conversasExistentes) {
          if (
            conversa.participantes.length === 2 &&
            conversa.participantes.includes(gestorId) &&
            conversa.participantes.includes(funcionarioUsuario._id)
          ) {
            conversaId = conversa._id;
            break;
          }
        }

        if (!conversaId) {
          conversaId = await ctx.db.insert("conversas", {
            tipo: "individual",
            participantes: [gestorId, funcionarioUsuario._id],
            criadoPor: funcionarioUsuario._id,
            criadoEm: Date.now(),
          });
        }

        // Criar mensagem de chat
        await ctx.db.insert("mensagens", {
          conversaId,
          remetenteId: funcionarioUsuario._id,
          tipo: "texto",
          conteudo: `Solicitei uma ausência de ${new Date(args.dataInicio).toLocaleDateString("pt-BR")} até ${new Date(args.dataFim).toLocaleDateString("pt-BR")}. Motivo: ${args.motivo}`,
          enviadaEm: Date.now(),
        });
      }
    }

    return solicitacaoId;
  },
});

// Mutation: Aprovar ausência
export const aprovar = mutation({
  args: {
    solicitacaoId: v.id("solicitacoesAusencias"),
    gestorId: v.id("usuarios"),
  },
  returns: v.null(),
  handler: async (ctx, args) => {
    const solicitacao = await ctx.db.get(args.solicitacaoId);
    if (!solicitacao) {
      throw new Error("Solicitação não encontrada");
    }

    if (solicitacao.status !== "aguardando_aprovacao") {
      throw new Error("Esta solicitação já foi processada");
    }

    // Verificar se o gestor tem permissão (é gestor do time do funcionário)
    const gestorIdDoFuncionario = await encontrarGestorDoFuncionario(
      ctx,
      solicitacao.funcionarioId
    );

    if (gestorIdDoFuncionario !== args.gestorId) {
      throw new Error("Você não tem permissão para aprovar esta solicitação");
    }

    const funcionario = await ctx.db.get(solicitacao.funcionarioId);
    if (!funcionario) {
      throw new Error("Funcionário não encontrado");
    }

    // Atualizar solicitação
    await ctx.db.patch(args.solicitacaoId, {
      status: "aprovado",
      gestorId: args.gestorId,
      dataAprovacao: Date.now(),
    });

    // Buscar usuário do funcionário
    const funcionarioUsuario = await ctx.db
      .query("usuarios")
      .withIndex("by_funcionarioId", (q) =>
        q.eq("funcionarioId", solicitacao.funcionarioId)
      )
      .first();

    if (funcionarioUsuario) {
      // Criar notificação in-app para funcionário
      await ctx.db.insert("notificacoesAusencias", {
        destinatarioId: funcionarioUsuario._id,
        solicitacaoAusenciaId: args.solicitacaoId,
        tipo: "aprovado",
        lida: false,
        mensagem: `Sua solicitação de ausência de ${new Date(solicitacao.dataInicio).toLocaleDateString("pt-BR")} até ${new Date(solicitacao.dataFim).toLocaleDateString("pt-BR")} foi aprovada!`,
      });

      const gestorUsuario = await ctx.db.get(args.gestorId);

      if (gestorUsuario) {
        // Enviar email ao funcionário
        await ctx.runMutation(api.email.enfileirarEmail, {
          destinatario: funcionarioUsuario.email,
          destinatarioId: funcionarioUsuario._id,
          assunto: "Solicitação de Ausência Aprovada",
          corpo: `<p>Olá ${funcionarioUsuario.nome},</p>
            <p>Sua solicitação de ausência foi <strong>aprovada</strong> pelo gestor ${gestorUsuario.nome}:</p>
            <ul>
              <li><strong>Período:</strong> ${new Date(solicitacao.dataInicio).toLocaleDateString("pt-BR")} até ${new Date(solicitacao.dataFim).toLocaleDateString("pt-BR")}</li>
              <li><strong>Motivo:</strong> ${solicitacao.motivo}</li>
            </ul>`,
          enviadoPor: args.gestorId,
        });

        // Criar ou obter conversa
        const conversasExistentes = await ctx.db
          .query("conversas")
          .filter((q) => q.eq(q.field("tipo"), "individual"))
          .collect();

        let conversaId: Id<"conversas"> | null = null;
        for (const conversa of conversasExistentes) {
          if (
            conversa.participantes.length === 2 &&
            conversa.participantes.includes(args.gestorId) &&
            conversa.participantes.includes(funcionarioUsuario._id)
          ) {
            conversaId = conversa._id;
            break;
          }
        }

        if (!conversaId) {
          conversaId = await ctx.db.insert("conversas", {
            tipo: "individual",
            participantes: [args.gestorId, funcionarioUsuario._id],
            criadoPor: args.gestorId,
            criadoEm: Date.now(),
          });
        }

        // Criar mensagem de chat
        await ctx.db.insert("mensagens", {
          conversaId,
          remetenteId: args.gestorId,
          tipo: "texto",
          conteudo: `Aprovei sua solicitação de ausência de ${new Date(solicitacao.dataInicio).toLocaleDateString("pt-BR")} até ${new Date(solicitacao.dataFim).toLocaleDateString("pt-BR")}.`,
          enviadaEm: Date.now(),
        });
      }
    }

    return null;
  },
});

// Mutation: Reprovar ausência
export const reprovar = mutation({
  args: {
    solicitacaoId: v.id("solicitacoesAusencias"),
    gestorId: v.id("usuarios"),
    motivoReprovacao: v.string(),
  },
  returns: v.null(),
  handler: async (ctx, args) => {
    const solicitacao = await ctx.db.get(args.solicitacaoId);
    if (!solicitacao) {
      throw new Error("Solicitação não encontrada");
    }

    if (solicitacao.status !== "aguardando_aprovacao") {
      throw new Error("Esta solicitação já foi processada");
    }

    // Verificar se o gestor tem permissão
    const gestorIdDoFuncionario = await encontrarGestorDoFuncionario(
      ctx,
      solicitacao.funcionarioId
    );

    if (gestorIdDoFuncionario !== args.gestorId) {
      throw new Error("Você não tem permissão para reprovar esta solicitação");
    }

    const funcionario = await ctx.db.get(solicitacao.funcionarioId);
    if (!funcionario) {
      throw new Error("Funcionário não encontrado");
    }

    // Atualizar solicitação
    await ctx.db.patch(args.solicitacaoId, {
      status: "reprovado",
      gestorId: args.gestorId,
      dataReprovacao: Date.now(),
      motivoReprovacao: args.motivoReprovacao.trim(),
    });

    // Buscar usuário do funcionário
    const funcionarioUsuario = await ctx.db
      .query("usuarios")
      .withIndex("by_funcionarioId", (q) =>
        q.eq("funcionarioId", solicitacao.funcionarioId)
      )
      .first();

    if (funcionarioUsuario) {
      // Criar notificação in-app para funcionário
      await ctx.db.insert("notificacoesAusencias", {
        destinatarioId: funcionarioUsuario._id,
        solicitacaoAusenciaId: args.solicitacaoId,
        tipo: "reprovado",
        lida: false,
        mensagem: `Sua solicitação de ausência de ${new Date(solicitacao.dataInicio).toLocaleDateString("pt-BR")} até ${new Date(solicitacao.dataFim).toLocaleDateString("pt-BR")} foi reprovada. Motivo: ${args.motivoReprovacao}`,
      });

      const gestorUsuario = await ctx.db.get(args.gestorId);

      if (gestorUsuario) {
        // Enviar email ao funcionário
        await ctx.runMutation(api.email.enfileirarEmail, {
          destinatario: funcionarioUsuario.email,
          destinatarioId: funcionarioUsuario._id,
          assunto: "Solicitação de Ausência Reprovada",
          corpo: `<p>Olá ${funcionarioUsuario.nome},</p>
            <p>Sua solicitação de ausência foi <strong>reprovada</strong> pelo gestor ${gestorUsuario.nome}:</p>
            <ul>
              <li><strong>Período:</strong> ${new Date(solicitacao.dataInicio).toLocaleDateString("pt-BR")} até ${new Date(solicitacao.dataFim).toLocaleDateString("pt-BR")}</li>
              <li><strong>Motivo:</strong> ${solicitacao.motivo}</li>
              <li><strong>Motivo da Reprovação:</strong> ${args.motivoReprovacao}</li>
            </ul>`,
          enviadoPor: args.gestorId,
        });

        // Criar ou obter conversa
        const conversasExistentes = await ctx.db
          .query("conversas")
          .filter((q) => q.eq(q.field("tipo"), "individual"))
          .collect();

        let conversaId: Id<"conversas"> | null = null;
        for (const conversa of conversasExistentes) {
          if (
            conversa.participantes.length === 2 &&
            conversa.participantes.includes(args.gestorId) &&
            conversa.participantes.includes(funcionarioUsuario._id)
          ) {
            conversaId = conversa._id;
            break;
          }
        }

        if (!conversaId) {
          conversaId = await ctx.db.insert("conversas", {
            tipo: "individual",
            participantes: [args.gestorId, funcionarioUsuario._id],
            criadoPor: args.gestorId,
            criadoEm: Date.now(),
          });
        }

        // Criar mensagem de chat
        await ctx.db.insert("mensagens", {
          conversaId,
          remetenteId: args.gestorId,
          tipo: "texto",
          conteudo: `Reprovei sua solicitação de ausência de ${new Date(solicitacao.dataInicio).toLocaleDateString("pt-BR")} até ${new Date(solicitacao.dataFim).toLocaleDateString("pt-BR")}. Motivo: ${args.motivoReprovacao}`,
          enviadaEm: Date.now(),
        });
      }
    }

    return null;
  },
});

// Mutation: Marcar notificação como lida
export const marcarComoLida = mutation({
  args: {
    notificacaoId: v.id("notificacoesAusencias"),
  },
  returns: v.null(),
  handler: async (ctx, args) => {
    await ctx.db.patch(args.notificacaoId, {
      lida: true,
    });
    return null;
  },
});

