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

// Validador para períodos
const periodoValidator = v.object({
  dataInicio: v.string(),
  dataFim: v.string(),
  diasCorridos: v.number(),
});

// Query: Listar TODAS as solicitações (para RH)
export const listarTodas = query({
  args: {},
  returns: v.array(v.any()),
  handler: async (ctx) => {
    const solicitacoes = await ctx.db.query("solicitacoesFerias").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._creationTime - a._creationTime);
  },
});

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

// Query: Listar solicitações dos subordinados (para gestores)
export const listarSolicitacoesSubordinados = query({
  args: { gestorId: v.id("usuarios") },
  returns: v.array(v.any()),
  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<any> = [];
    
    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("solicitacoesFerias")
          .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._creationTime - a._creationTime);
  },
});

// Query: Obter detalhes completos de uma solicitação
export const obterDetalhes = query({
  args: { solicitacaoId: v.id("solicitacoesFerias") },
  returns: v.union(v.any(), v.null()),
  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);
    }
    
    return {
      ...solicitacao,
      funcionario,
      gestor,
    };
  },
});

// Mutation: Criar solicitação de férias (com validação de saldo)
export const criarSolicitacao = mutation({
  args: {
    funcionarioId: v.id("funcionarios"),
    anoReferencia: v.number(),
    periodos: v.array(periodoValidator),
    observacao: v.optional(v.string()),
  },
  returns: v.id("solicitacoesFerias"),
  handler: async (ctx, args) => {
    if (args.periodos.length === 0) {
      throw new Error("É necessário adicionar pelo menos 1 período");
    }
    
    const funcionario = await ctx.db.get(args.funcionarioId);
    if (!funcionario) throw new Error("Funcionário não encontrado");
    
    // Calcular total de dias
    let totalDias = 0;
    for (const p of args.periodos) {
      totalDias += p.diasCorridos;
    }
    
    // Reservar dias no saldo (impede uso duplo)
    await ctx.runMutation(internal.saldoFerias.reservarDias, {
      funcionarioId: args.funcionarioId,
      anoReferencia: args.anoReferencia,
      totalDias,
    });
    
    // Buscar usuário que está criando (pode não ser o próprio funcionário)
    const usuario = await ctx.db
      .query("usuarios")
      .withIndex("by_funcionarioId", (q) => q.eq("funcionarioId", args.funcionarioId))
      .first();
    
    const solicitacaoId = await ctx.db.insert("solicitacoesFerias", {
      funcionarioId: args.funcionarioId,
      anoReferencia: args.anoReferencia,
      status: "aguardando_aprovacao",
      periodos: args.periodos,
      observacao: args.observacao,
      historicoAlteracoes: [{
        data: Date.now(),
        usuarioId: usuario?._id || funcionario.gestorId!,
        acao: "Solicitação criada",
      }],
    });
    
    // Notificar gestor
    if (funcionario.gestorId) {
      await ctx.db.insert("notificacoesFerias", {
        destinatarioId: funcionario.gestorId,
        solicitacaoFeriasId: solicitacaoId,
        tipo: "nova_solicitacao",
        lida: false,
        mensagem: `${funcionario.nome} solicitou férias`,
      });
    }
    
    return solicitacaoId;
  },
});

// Mutation: Aprovar férias
export const aprovar = mutation({
  args: {
    solicitacaoId: v.id("solicitacoesFerias"),
    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");
    }
    
    const funcionario = await ctx.db.get(solicitacao.funcionarioId);
    
    await ctx.db.patch(args.solicitacaoId, {
      status: "aprovado",
      gestorId: args.gestorId,
      dataAprovacao: Date.now(),
      historicoAlteracoes: [
        ...(solicitacao.historicoAlteracoes || []),
        {
          data: Date.now(),
          usuarioId: args.gestorId,
          acao: "Aprovado",
        },
      ],
    });
    
    // Atualizar saldo (de pendente para usado)
    await ctx.runMutation(internal.saldoFerias.atualizarSaldoAposAprovacao, {
      solicitacaoId: args.solicitacaoId,
    });
    
    // Notificar funcionário
    if (funcionario) {
      const usuario = await ctx.db
        .query("usuarios")
        .withIndex("by_funcionarioId", (q) => q.eq("funcionarioId", funcionario._id))
        .first();
      
      if (usuario) {
        await ctx.db.insert("notificacoesFerias", {
          destinatarioId: usuario._id,
          solicitacaoFeriasId: args.solicitacaoId,
          tipo: "aprovado",
          lida: false,
          mensagem: "Suas férias foram aprovadas!",
        });
      }
    }
    
    return null;
  },
});

// Mutation: Reprovar férias
export const reprovar = mutation({
  args: {
    solicitacaoId: v.id("solicitacoesFerias"),
    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");
    }
    
    const funcionario = await ctx.db.get(solicitacao.funcionarioId);
    
    await ctx.db.patch(args.solicitacaoId, {
      status: "reprovado",
      gestorId: args.gestorId,
      dataReprovacao: Date.now(),
      motivoReprovacao: args.motivoReprovacao,
      historicoAlteracoes: [
        ...(solicitacao.historicoAlteracoes || []),
        {
          data: Date.now(),
          usuarioId: args.gestorId,
          acao: `Reprovado: ${args.motivoReprovacao}`,
        },
      ],
    });
    
    // Liberar dias reservados de volta ao saldo
    await ctx.runMutation(internal.saldoFerias.liberarDias, {
      solicitacaoId: args.solicitacaoId,
    });
    
    // Notificar funcionário
    if (funcionario) {
      const usuario = await ctx.db
        .query("usuarios")
        .withIndex("by_funcionarioId", (q) => q.eq("funcionarioId", funcionario._id))
        .first();
      
      if (usuario) {
        await ctx.db.insert("notificacoesFerias", {
          destinatarioId: usuario._id,
          solicitacaoFeriasId: args.solicitacaoId,
          tipo: "reprovado",
          lida: false,
          mensagem: `Suas férias foram reprovadas: ${args.motivoReprovacao}`,
        });
      }
    }
    
    return null;
  },
});

// Mutation: Ajustar data e aprovar
export const ajustarEAprovar = mutation({
  args: {
    solicitacaoId: v.id("solicitacoesFerias"),
    gestorId: v.id("usuarios"),
    novosPeriodos: v.array(periodoValidator),
  },
  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");
    }
    
    if (args.novosPeriodos.length === 0) {
      throw new Error("É necessário adicionar pelo menos 1 período");
    }
    
    const funcionario = await ctx.db.get(solicitacao.funcionarioId);
    
    // Liberar dias antigos
    await ctx.runMutation(internal.saldoFerias.liberarDias, {
      solicitacaoId: args.solicitacaoId,
    });
    
    // Calcular novos dias e reservar
    let totalNovosDias = 0;
    for (const p of args.novosPeriodos) {
      totalNovosDias += p.diasCorridos;
    }
    
    await ctx.runMutation(internal.saldoFerias.reservarDias, {
      funcionarioId: solicitacao.funcionarioId,
      anoReferencia: solicitacao.anoReferencia,
      totalDias: totalNovosDias,
    });
    
    await ctx.db.patch(args.solicitacaoId, {
      status: "data_ajustada_aprovada",
      periodos: args.novosPeriodos,
      gestorId: args.gestorId,
      dataAprovacao: Date.now(),
      historicoAlteracoes: [
        ...(solicitacao.historicoAlteracoes || []),
        {
          data: Date.now(),
          usuarioId: args.gestorId,
          acao: "Data ajustada e aprovada",
          periodosAnteriores: solicitacao.periodos,
        },
      ],
    });
    
    // Atualizar saldo (marcar como usado)
    await ctx.runMutation(internal.saldoFerias.atualizarSaldoAposAprovacao, {
      solicitacaoId: args.solicitacaoId,
    });
    
    // Notificar funcionário
    if (funcionario) {
      const usuario = await ctx.db
        .query("usuarios")
        .withIndex("by_funcionarioId", (q) => q.eq("funcionarioId", funcionario._id))
        .first();
      
      if (usuario) {
        await ctx.db.insert("notificacoesFerias", {
          destinatarioId: usuario._id,
          solicitacaoFeriasId: args.solicitacaoId,
          tipo: "data_ajustada",
          lida: false,
          mensagem: "Suas férias foram aprovadas com ajuste de datas",
        });
      }
    }
    
    return null;
  },
});

// Query: Verificar status de férias automático
export const verificarStatusFerias = query({
  args: { funcionarioId: v.id("funcionarios") },
  returns: v.union(v.literal("ativo"), v.literal("em_ferias")),
  handler: async (ctx, args) => {
    const hoje = new Date();
    hoje.setHours(0, 0, 0, 0);
    
    const solicitacoesAprovadas = await ctx.db
      .query("solicitacoesFerias")
      .withIndex("by_funcionario_and_status", (q) =>
        q.eq("funcionarioId", args.funcionarioId)
          .eq("status", "aprovado")
      )
      .collect();
    
    const solicitacoesAjustadas = await ctx.db
      .query("solicitacoesFerias")
      .withIndex("by_funcionario_and_status", (q) =>
        q.eq("funcionarioId", args.funcionarioId)
          .eq("status", "data_ajustada_aprovada")
      )
      .collect();
    
    const todasSolicitacoes = [...solicitacoesAprovadas, ...solicitacoesAjustadas];
    
    for (const solicitacao of todasSolicitacoes) {
      for (const periodo of solicitacao.periodos) {
        const inicio = new Date(periodo.dataInicio);
        const fim = new Date(periodo.dataFim);
        inicio.setHours(0, 0, 0, 0);
        fim.setHours(23, 59, 59, 999);
        
        if (hoje >= inicio && hoje <= fim) {
          return "em_ferias";
        }
      }
    }
    
    return "ativo";
  },
});

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

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

// Internal Mutation: Atualizar status de todos os funcionários
export const atualizarStatusTodosFuncionarios = internalMutation({
  args: {},
  returns: v.null(),
  handler: async (ctx) => {
    const funcionarios = await ctx.db.query("funcionarios").collect();
    
    for (const func of funcionarios) {
      const hoje = new Date();
      hoje.setHours(0, 0, 0, 0);
      
      const solicitacoesAprovadas = await ctx.db
        .query("solicitacoesFerias")
        .withIndex("by_funcionario_and_status", (q) =>
          q.eq("funcionarioId", func._id)
            .eq("status", "aprovado")
        )
        .collect();
      
      const solicitacoesAjustadas = await ctx.db
        .query("solicitacoesFerias")
        .withIndex("by_funcionario_and_status", (q) =>
          q.eq("funcionarioId", func._id)
            .eq("status", "data_ajustada_aprovada")
        )
        .collect();
      
      const todasSolicitacoes = [...solicitacoesAprovadas, ...solicitacoesAjustadas];
      
      let emFerias = false;
      for (const solicitacao of todasSolicitacoes) {
        for (const periodo of solicitacao.periodos) {
          const inicio = new Date(periodo.dataInicio);
          const fim = new Date(periodo.dataFim);
          inicio.setHours(0, 0, 0, 0);
          fim.setHours(23, 59, 59, 999);
          
          if (hoje >= inicio && hoje <= fim) {
            emFerias = true;
            break;
          }
        }
        if (emFerias) break;
      }
      
      const novoStatus = emFerias ? "em_ferias" : "ativo";
      
      if (func.statusFerias !== novoStatus) {
        await ctx.db.patch(func._id, { statusFerias: novoStatus });
      }
    }
    
    return null;
  },
});

