import * as kv from "./kv_store.tsx";
import { createLogger } from "./logger.tsx";

const logger = createLogger('FeriadosRH');

// Calendario de feriados — GLOBAL (nao por empresa), versionado por ano,
// tal como parametros-fiscais-rh.tsx: os 9 feriados nacionais (Art. 105
// n.2 da Lei n.º 13/2023) sao iguais para todas as empresas moçambicanas.
// Feriados locais/provinciais e tolerancias de ponto (Art. 106 — concedida
// pelo Ministro do Trabalho, anunciada com pelo menos 2 dias de
// antecedencia) sao a excepcao e ficam marcados com `provincia`.
//
// Confirmado por leitura directa da Lei n.º 13/2023 (Boletim da Republica):
//   - Art. 105 n.2: os 9 feriados nacionais fixos.
//   - Art. 105 n.4, alineas a) a m): sectores de continuidade isentos da
//     suspensao em feriados/tolerancia de ponto (lista oficial completa,
//     mais precisa que a versao simplificada por vezes citada).
//   - Art. 105 n.6: quando o feriado cai em domingo, a suspensao "fica
//     diferida para o dia seguinte" (segunda-feira) — excepto para os
//     sectores de continuidade do n.4.
//   - Art. 106 n.3: a mesma excepcao dos sectores de continuidade aplica-se
//     a tolerancia de ponto.
export type TipoFeriado = "nacional" | "local" | "tolerancia_ponto";

export const SECTORES_CONTINUIDADE_ART_105_N4 = [
  { alinea: "a", descricao: "Serviços médicos, hospitalares e de fornecimento de medicamentos" },
  { alinea: "b", descricao: "Abastecimento de água, energia e de combustíveis" },
  { alinea: "c", descricao: "Correios e telecomunicações" },
  { alinea: "d", descricao: "Serviços funerários" },
  { alinea: "e", descricao: "Carga e descarga de animais e géneros alimentares deterioráveis" },
  { alinea: "f", descricao: "Controlo do espaço aéreo e meteorológico" },
  { alinea: "g", descricao: "Bombeiros" },
  { alinea: "h", descricao: "Serviços de salubridade" },
  { alinea: "i", descricao: "Segurança privada" },
  { alinea: "j", descricao: "Indústria de produção em grande escala, em regime de laboração contínua" },
  { alinea: "k", descricao: "Serviços de transporte" },
  { alinea: "l", descricao: "Serviços hoteleiros e de restauração" },
  { alinea: "m", descricao: "Serviços de manuseamento portuário e cais de portagem" },
];

export interface Feriado {
  id: string;
  ano: number;
  data: string; // YYYY-MM-DD
  descricao: string;
  tipo: TipoFeriado;
  provincia?: string; // so relevante para tipo='local'
  fonteLegal?: string;
  ehTransferido?: boolean; // Art. 105 n.6 — feriado que caiu em domingo, observado na segunda-feira seguinte
  dataOriginal?: string; // se ehTransferido, a data real do feriado (o domingo)
  sistema: boolean; // feriados nacionais semeados automaticamente — nao eliminaveis
  dataCriacao: string;
  dataAtualizacao?: string;
}

const FERIADO_PREFIX = "feriado_rh:";

const FERIADOS_NACIONAIS_FIXOS = [
  { mes: 1, dia: 1, descricao: "Ano Novo" },
  { mes: 2, dia: 3, descricao: "Dia dos Heróis Moçambicanos" },
  { mes: 4, dia: 7, descricao: "Dia da Mulher Moçambicana" },
  { mes: 5, dia: 1, descricao: "Dia Internacional do Trabalhador" },
  { mes: 6, dia: 25, descricao: "Dia da Independência Nacional" },
  { mes: 9, dia: 7, descricao: "Dia dos Acordos de Lusaka" },
  { mes: 9, dia: 25, descricao: "Dia das Forças Armadas" },
  { mes: 10, dia: 4, descricao: "Dia da Paz e Reconciliação Nacional" },
  { mes: 12, dia: 25, descricao: "Dia da Família" },
];

function formatarDataISO(ano: number, mes: number, dia: number): string {
  return `${ano}-${String(mes).padStart(2, "0")}-${String(dia).padStart(2, "0")}`;
}

// Dia da semana em UTC para evitar deslizamento por fuso horário (0=domingo).
function diaDaSemana(dataIso: string): number {
  return new Date(`${dataIso}T00:00:00Z`).getUTCDay();
}

function adicionarDias(dataIso: string, dias: number): string {
  const data = new Date(`${dataIso}T00:00:00Z`);
  data.setUTCDate(data.getUTCDate() + dias);
  return data.toISOString().slice(0, 10);
}

// Semeia os 9 feriados nacionais do ano (Art. 105 n.2) e, para cada um que
// caia num domingo, gera tambem o registo do feriado transferido para
// segunda-feira (Art. 105 n.6). Idempotente.
export async function garantirFeriadosNacionais(ano: number): Promise<void> {
  const existentes = await kv.getByPrefix<Feriado>(FERIADO_PREFIX);
  const jaTem = existentes.some((item) => item && item.ano === ano && item.sistema);
  if (jaTem) return;

  for (const feriado of FERIADOS_NACIONAIS_FIXOS) {
    const data = formatarDataISO(ano, feriado.mes, feriado.dia);
    const id = `feriado_${ano}_${feriado.mes}_${feriado.dia}`;

    await kv.set(`${FERIADO_PREFIX}${id}`, {
      id,
      ano,
      data,
      descricao: feriado.descricao,
      tipo: "nacional",
      fonteLegal: "Art. 105 n.2 da Lei n.º 13/2023",
      sistema: true,
      dataCriacao: new Date().toISOString(),
    } as Feriado);

    if (diaDaSemana(data) === 0) {
      const dataTransferida = adicionarDias(data, 1);
      const idTransferido = `feriado_${ano}_${feriado.mes}_${feriado.dia}_transferido`;

      await kv.set(`${FERIADO_PREFIX}${idTransferido}`, {
        id: idTransferido,
        ano,
        data: dataTransferida,
        descricao: `${feriado.descricao} (transferido de domingo)`,
        tipo: "nacional",
        fonteLegal: "Art. 105 n.6 da Lei n.º 13/2023",
        ehTransferido: true,
        dataOriginal: data,
        sistema: true,
        dataCriacao: new Date().toISOString(),
      } as Feriado);
    }
  }

  logger.info(`Feriados nacionais semeados para o ano ${ano}`);
}

export async function listarFeriados(ano: number, filtros?: { provincia?: string }): Promise<Feriado[]> {
  try {
    await garantirFeriadosNacionais(ano);

    const todos = await kv.getByPrefix<Feriado>(FERIADO_PREFIX);
    let feriados = todos.filter((item) => item && item.ano === ano);

    if (filtros?.provincia) {
      feriados = feriados.filter((item) => !item.provincia || item.provincia === filtros.provincia);
    }

    feriados.sort((a, b) => a.data.localeCompare(b.data));
    return feriados;
  } catch (error) {
    logger.error("Erro ao listar feriados:", error);
    return [];
  }
}

export async function buscarFeriadoPorId(id: string): Promise<Feriado | null> {
  try {
    return (await kv.get(`${FERIADO_PREFIX}${id}`)) as Feriado | null;
  } catch (error) {
    logger.error("Erro ao buscar feriado:", error);
    return null;
  }
}

// Verifica se uma data e feriado (nacional, local aplicavel à provincia
// indicada, ou tolerancia de ponto) — usado pelo motor de calculo de horas
// (Grupo B) para classificar presencas.
export async function ehFeriado(data: string, provincia?: string): Promise<Feriado | null> {
  const ano = Number(data.slice(0, 4));
  const feriados = await listarFeriados(ano, { provincia });
  return feriados.find((item) => item.data === data) || null;
}

export async function criarFeriadoLocal(
  dados: { ano: number; data: string; descricao: string; tipo: "local" | "tolerancia_ponto"; provincia?: string },
): Promise<{ success: boolean; data?: Feriado; error?: string }> {
  try {
    if (!dados.data || !dados.descricao?.trim()) {
      return { success: false, error: "Data e descrição são obrigatórias" };
    }

    if (dados.tipo === "local" && !dados.provincia?.trim()) {
      return { success: false, error: "Indique a província para um feriado local" };
    }

    const id = `feriado_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
    const feriado: Feriado = {
      id,
      ano: dados.ano,
      data: dados.data,
      descricao: dados.descricao.trim(),
      tipo: dados.tipo,
      provincia: dados.provincia?.trim(),
      fonteLegal: dados.tipo === "tolerancia_ponto" ? "Art. 106 da Lei n.º 13/2023" : undefined,
      sistema: false,
      dataCriacao: new Date().toISOString(),
    };

    await kv.set(`${FERIADO_PREFIX}${id}`, feriado);
    logger.info(`Feriado criado: ${feriado.descricao} (${feriado.data})`);

    return { success: true, data: feriado };
  } catch (error) {
    logger.error("Erro ao criar feriado:", error);
    return { success: false, error: error.message };
  }
}

export async function deletarFeriado(id: string): Promise<{ success: boolean; error?: string }> {
  try {
    const feriado = await buscarFeriadoPorId(id);
    if (!feriado) {
      return { success: false, error: "Feriado não encontrado" };
    }

    if (feriado.sistema) {
      return { success: false, error: "Feriados nacionais não podem ser eliminados" };
    }

    await kv.del(`${FERIADO_PREFIX}${id}`);
    logger.info(`Feriado eliminado: ${id}`);

    return { success: true };
  } catch (error) {
    logger.error("Erro ao eliminar feriado:", error);
    return { success: false, error: error.message };
  }
}
