import * as kv from "./kv_store.tsx";
import { createLogger } from "./logger.tsx";
import { buscarColaboradorPorId } from "./colaboradores.tsx";

const logger = createLogger('BancoHoras');

// Banco de Horas — ledger de creditos/debitos em MINUTOS por colaborador.
// Duas origens de credito, ambas geradas automaticamente por
// folha-pagamento.tsx (a partir do apuramento de calculo-horas-trabalho.tsx)
// apos um processamento salarial bem sucedido — nunca escrito directamente
// pelo router:
//   - "hora_extra_util"/"hora_extra_excepcional": quando existe uma
//     autorizacao-hora-extra.tsx APROVADA com tipoCompensacao='tempo' para
//     o dia trabalhado — em vez de pagar a hora extra, credita-se o tempo
//     (ja majorado pela mesma percentagem que seria aplicada ao pagamento).
//   - "descanso_compensatorio": Art. 98 n.4 da Lei n.º 13/2023 — direito a
//     um dia completo de descanso compensatorio por trabalho em dia de
//     descanso/feriado (meio-dia se a prestacao nao ultrapassar 5h),
//     INDEPENDENTE da escolha de pagamento vs. banco de horas (e um
//     direito, nao uma alternativa de compensacao).
// Debito: gozo de folga compensatoria, registado manualmente pelo RH.
export type TipoMovimentoBancoHoras = "credito" | "debito";
export type OrigemMovimentoBancoHoras =
  | "hora_extra_util"
  | "hora_extra_excepcional"
  | "descanso_compensatorio"
  | "gozo_folga"
  | "ajuste_manual";

export interface MovimentoBancoHoras {
  id: string;
  empresaId: string;
  colaboradorId: string;
  data: string; // YYYY-MM-DD
  tipo: TipoMovimentoBancoHoras;
  origem: OrigemMovimentoBancoHoras;
  minutos: number; // sempre positivo; o sinal e dado por `tipo`
  autorizacaoId?: string;
  folhaPagamentoId?: string; // se veio de um processamento salarial — permite reverter ao eliminar a folha
  observacoes?: string;
  registadoPor: string;
  dataCriacao: string;
}

const MOVIMENTO_PREFIX = "movimento_banco_horas:";

export async function registarMovimentoBancoHoras(
  dados: Omit<MovimentoBancoHoras, "id" | "dataCriacao">,
): Promise<{ success: boolean; data?: MovimentoBancoHoras; error?: string }> {
  try {
    if (!dados.empresaId || !dados.colaboradorId) {
      return { success: false, error: "Empresa e colaborador são obrigatórios" };
    }
    if (!dados.minutos || dados.minutos <= 0) {
      return { success: false, error: "O movimento tem de ter uma quantidade de minutos positiva" };
    }
    if (!dados.registadoPor?.trim()) {
      return { success: false, error: "É obrigatório identificar quem registou o movimento" };
    }

    const id = `movimento_banco_horas_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
    const movimento: MovimentoBancoHoras = {
      id,
      ...dados,
      dataCriacao: new Date().toISOString(),
    };

    await kv.set(`${MOVIMENTO_PREFIX}${id}`, movimento);
    logger.info(`Movimento de banco de horas registado: ${dados.colaboradorId} - ${dados.tipo} - ${dados.minutos} min (${dados.origem})`);

    return { success: true, data: movimento };
  } catch (error) {
    logger.error("Erro ao registar movimento de banco de horas:", error);
    return { success: false, error: error.message };
  }
}

export async function listarMovimentosBancoHoras(
  empresaId: string,
  filtros?: { colaboradorId?: string; dataInicio?: string; dataFim?: string },
): Promise<MovimentoBancoHoras[]> {
  try {
    const todos = await kv.getByPrefix<MovimentoBancoHoras>(MOVIMENTO_PREFIX);
    let movimentos = todos.filter((item) => item && item.empresaId === empresaId);

    if (filtros?.colaboradorId) {
      movimentos = movimentos.filter((item) => item.colaboradorId === filtros.colaboradorId);
    }
    if (filtros?.dataInicio) {
      movimentos = movimentos.filter((item) => item.data >= filtros.dataInicio!);
    }
    if (filtros?.dataFim) {
      movimentos = movimentos.filter((item) => item.data <= filtros.dataFim!);
    }

    movimentos.sort((a, b) => b.data.localeCompare(a.data));
    return movimentos;
  } catch (error) {
    logger.error("Erro ao listar movimentos de banco de horas:", error);
    return [];
  }
}

export async function calcularSaldoBancoHoras(empresaId: string, colaboradorId: string): Promise<number> {
  const movimentos = await listarMovimentosBancoHoras(empresaId, { colaboradorId });
  return movimentos.reduce((saldo, m) => saldo + (m.tipo === "credito" ? m.minutos : -m.minutos), 0);
}

// Reverte (elimina) os creditos gerados por uma folha de pagamento, quando
// essa folha e eliminada por ter sido processada incorrectamente — ver
// folha-pagamento.tsx.
export async function removerMovimentosDaFolha(folhaPagamentoId: string): Promise<void> {
  try {
    const todos = await kv.getByPrefix<MovimentoBancoHoras>(MOVIMENTO_PREFIX);
    const daFolha = todos.filter((item) => item && item.folhaPagamentoId === folhaPagamentoId);
    for (const movimento of daFolha) {
      await kv.del(`${MOVIMENTO_PREFIX}${movimento.id}`);
    }
    if (daFolha.length > 0) {
      logger.info(`${daFolha.length} movimento(s) de banco de horas revertido(s) da folha ${folhaPagamentoId}`);
    }
  } catch (error) {
    logger.error("Erro ao reverter movimentos de banco de horas da folha:", error);
  }
}

// Gozo de folga compensatoria — debito manual, validado contra o saldo
// disponivel (nao permite ficar negativo).
export async function registarGozoBancoHoras(
  empresaId: string,
  colaboradorId: string,
  data: string,
  minutos: number,
  registadoPor: string,
  observacoes?: string,
): Promise<{ success: boolean; data?: MovimentoBancoHoras; error?: string }> {
  try {
    const colaborador = await buscarColaboradorPorId(colaboradorId);
    if (!colaborador || colaborador.empresaId !== empresaId) {
      return { success: false, error: "Colaborador não encontrado nesta empresa" };
    }

    const saldoActual = await calcularSaldoBancoHoras(empresaId, colaboradorId);
    if (minutos > saldoActual) {
      return { success: false, error: `Saldo insuficiente no banco de horas (disponível: ${(saldoActual / 60).toFixed(1)}h, solicitado: ${(minutos / 60).toFixed(1)}h)` };
    }

    return await registarMovimentoBancoHoras({
      empresaId,
      colaboradorId,
      data,
      tipo: "debito",
      origem: "gozo_folga",
      minutos,
      observacoes,
      registadoPor,
    });
  } catch (error) {
    logger.error("Erro ao registar gozo de banco de horas:", error);
    return { success: false, error: error.message };
  }
}
