import * as kv from "./kv_store.tsx";
import { createLogger } from "./logger.tsx";
import { buscarColaboradorPorId, cessarColaborador } from "./colaboradores.tsx";
import { buscarEmpresaPorId } from "./empresas.tsx";
import { criarComunicacaoINSS } from "./comunicacao-inss.tsx";

const logger = createLogger('CessacaoContrato');

// ========== TIPOS ==========
//
// Confirmado por leitura directa do texto oficial da Lei n.º 13/2023
// (Boletim da Republica, extraido via pdftotext). Ha VARIOS regimes
// distintos consoante quem toma a iniciativa e porque — nao e "um" calculo
// de rescisao, sao vários, cada um com a sua base legal:
//
//   - Art. 136 (aprox.): caducidade do contrato a termo — NAO confere
//     direito a indemnizacao.
//   - Art. 139: rescisao com justa causa por iniciativa do TRABALHADOR —
//     45 dias de salario por ano de servico (rateado por fraccao <12
//     meses) se contrato indeterminado; remuneracoes ate ao fim do prazo
//     se contrato a termo certo (este ultimo caso exige saber a data de
//     fim prevista do contrato, que ainda nao existe no cadastro do
//     colaborador — fica para calculo manual).
//   - Art. 140: denuncia do contrato pelo TRABALHADOR (demissao sem justa
//     causa) — sem indemnizacao, so exige aviso previo: 30 dias (termo
//     certo) ou 15/30 dias conforme antiguidade (indeterminado).
//   - Art. 141: rescisao por iniciativa do EMPREGADOR por motivos
//     estruturais/tecnologicos/mercado — indemnizacao escalonada por
//     faixa salarial relativa ao "salario minimo do sector de
//     actividade" (30/15/5 dias por ano). Este salario minimo NAO e um
//     valor unico nacional nem consta desta lei — e proprio de cada
//     empresa (Empresa.salarioMinimoSetorMzn, ver empresas.tsx), ao
//     contrario do INSS/IRPS que sao os mesmos para qualquer empresa.
//   - Despedimento disciplinar: por remissao do texto, segue a formula do
//     Art. 139 quando nao ha reintegracao (este sistema so calcula a
//     indemnizacao monetaria, nao suporta o cenario de reintegracao).
//   - Caducidade, mutuo acordo, reforma, falecimento: sem formula legal de
//     indemnizacao fixa (salvo acordo das partes) — fica 0 por omissao,
//     editavel manualmente se houver acordo.
export type MotivoCessacao =
  | "caducidade_termo"
  | "denuncia_trabalhador"
  | "justa_causa_trabalhador"
  | "rescisao_empregador_motivos_estruturais"
  | "despedimento_disciplinar"
  | "mutuo_acordo"
  | "reforma"
  | "falecimento";

export type EstadoCessacao = "rascunho" | "confirmado";

export interface CessacaoContrato {
  id: string;
  empresaId: string;
  colaboradorId: string;
  motivo: MotivoCessacao;
  dataComunicacao?: string; // YYYY-MM-DD, quando a decisao foi comunicada
  dataCessacaoEfetiva: string; // YYYY-MM-DD, ultimo dia de vinculo

  // Aviso previo (Art. 139 n.1, Art. 140, Art. 142 n.2) — null quando a
  // lei nao fixa um prazo para o caso (ex.: antiguidade < 6 meses em
  // denuncia por tempo indeterminado, ver comentario na funcao de calculo)
  avisoPrevioDiasExigidos: number | null;
  avisoPrevioCumprido: boolean;

  // Indemnizacao — null quando precisa de calculo manual (parametro em
  // falta, ou caso nao coberto automaticamente — ver comentario acima)
  indemnizacaoDiasPorAno: number | null;
  indemnizacaoValor: number | null;
  indemnizacaoRequerCalculoManual: boolean;
  indemnizacaoObservacao?: string;

  // Saldo de ferias nao gozadas — SEMPRE manual: a acumulacao de ferias
  // depende da mesma taxa legal que continua bloqueada a espera de
  // confirmacao do contabilista (ver parametros-fiscais-rh.tsx), por isso
  // nao ha aqui nenhum calculo automatico de saldo, so o registo do que
  // foi acordado/apurado manualmente.
  saldoFeriasDiasManual: number;
  saldoFeriasValor: number;

  // Salario proporcional do mes de cessacao — dias de calendario
  // efectivamente trabalhados no mes / dias do mes * salario base
  salarioProporcionalDias: number;
  salarioProporcionalValor: number;

  totalALiquidar: number;
  observacoes?: string;
  estado: EstadoCessacao;
  processadoPor: string;
  dataCriacao: string;
  dataAtualizacao?: string;
}

const CESSACAO_PREFIX = "cessacao_contrato:";

function arredondar(valor: number): number {
  return Math.round(valor * 100) / 100;
}

function diferencaEmAnosEMeses(dataInicio: string, dataFim: string): { anos: number; mesesRestantes: number; totalMeses: number } {
  const inicio = new Date(`${dataInicio}T00:00:00`);
  const fim = new Date(`${dataFim}T00:00:00`);
  let totalMeses = (fim.getFullYear() - inicio.getFullYear()) * 12 + (fim.getMonth() - inicio.getMonth());
  if (fim.getDate() < inicio.getDate()) totalMeses -= 1;
  totalMeses = Math.max(0, totalMeses);
  return { anos: Math.floor(totalMeses / 12), mesesRestantes: totalMeses % 12, totalMeses };
}

// ========== CALCULO DE AVISO PREVIO ==========
//
// Art. 140 n.3: para denuncia por tempo indeterminado, a lei so fixa
// prazos para antiguidade > 6 meses. Para antiguidade <= 6 meses a lei
// nao especifica um prazo proprio aqui (o periodo probatorio, Art. 12,
// tem o seu proprio regime de 7 dias, mas isso e um cenario diferente) —
// devolve null nesse caso em vez de adivinhar um numero.
function calcularAvisoPrevioDias(
  motivo: MotivoCessacao,
  tipoContratoIndeterminado: boolean,
  antiguidadeMeses: number,
): number | null {
  switch (motivo) {
    case "denuncia_trabalhador":
      if (!tipoContratoIndeterminado) return 30; // Art. 140 n.2 (contrato a termo certo)
      if (antiguidadeMeses > 36) return 30; // Art. 140 n.3 b)
      if (antiguidadeMeses > 6) return 15; // Art. 140 n.3 a)
      return null; // lei nao fixa prazo proprio para <= 6 meses de servico
    case "rescisao_empregador_motivos_estruturais":
      return 30; // Art. 142 n.2 — comunicacao previa ao orgao sindical/Ministerio
    case "justa_causa_trabalhador":
      return 7; // Art. 139 n.1 — comunicacao previa de pelo menos 7 dias
    default:
      return null;
  }
}

// ========== CALCULO DE INDEMNIZACAO ==========

interface ResultadoIndemnizacao {
  diasPorAno: number | null;
  valor: number | null;
  requerCalculoManual: boolean;
  observacao?: string;
}

async function calcularIndemnizacao(
  motivo: MotivoCessacao,
  salarioBase: number,
  antiguidadeAnos: number,
  antiguidadeMesesRestantes: number,
  tipoContratoIndeterminado: boolean,
  salarioMinimoSetorMzn: number | null,
): Promise<ResultadoIndemnizacao> {
  const anosProporcionais = antiguidadeAnos + antiguidadeMesesRestantes / 12;

  switch (motivo) {
    case "justa_causa_trabalhador":
    case "despedimento_disciplinar": {
      // Art. 139 n.2 (indeterminado): 45 dias/ano, rateado por fraccao <12
      // meses. Art. 139 n.3 (termo certo): remuneracoes ate ao fim do
      // prazo contratual — exige a data de fim prevista do contrato, que
      // ainda nao existe no cadastro do colaborador.
      if (!tipoContratoIndeterminado) {
        return {
          diasPorAno: null,
          valor: null,
          requerCalculoManual: true,
          observacao: "Contrato a termo certo: indemnização corresponde às remunerações até ao fim do prazo contratual (Art. 139 n.3) — calcular manualmente, a data de fim do contrato ainda não é um campo do cadastro.",
        };
      }
      const valor = arredondar((salarioBase / 30) * 45 * anosProporcionais);
      return { diasPorAno: 45, valor, requerCalculoManual: false };
    }

    case "rescisao_empregador_motivos_estruturais": {
      if (salarioMinimoSetorMzn === null || salarioMinimoSetorMzn === undefined) {
        return {
          diasPorAno: null,
          valor: null,
          requerCalculoManual: true,
          observacao: "Campo \"Salário mínimo do sector\" ainda não preenchido (ver Configuração da Empresa) — necessário para determinar a faixa de indemnização (Art. 141 n.3).",
        };
      }

      const razao = salarioBase / salarioMinimoSetorMzn;
      let diasPorAno: number;
      if (razao <= 7) {
        diasPorAno = 30; // Art. 141 n.3 a)
      } else if (razao <= 18) {
        diasPorAno = 15; // Art. 141 n.3 b)
      } else {
        diasPorAno = 5; // Art. 141 n.3 c)
      }

      const valor = arredondar((salarioBase / 30) * diasPorAno * anosProporcionais);
      return { diasPorAno, valor, requerCalculoManual: false };
    }

    case "caducidade_termo":
      return { diasPorAno: 0, valor: 0, requerCalculoManual: false, observacao: "Caducidade do contrato a termo não confere direito a indemnização (Art. 136)." };

    case "denuncia_trabalhador":
      return { diasPorAno: 0, valor: 0, requerCalculoManual: false, observacao: "Denúncia do contrato pelo trabalhador não confere direito a indemnização (Art. 140)." };

    case "mutuo_acordo":
    case "reforma":
    case "falecimento":
      return { diasPorAno: null, valor: null, requerCalculoManual: true, observacao: "Sem fórmula legal fixa — valor a acordar entre as partes ou conforme regime aplicável." };

    default:
      return { diasPorAno: null, valor: null, requerCalculoManual: true };
  }
}

// ========== FUNCOES DE GESTAO ==========

export async function criarCessacaoContrato(
  dados: {
    empresaId: string;
    colaboradorId: string;
    motivo: MotivoCessacao;
    dataComunicacao?: string;
    dataCessacaoEfetiva: string;
    avisoPrevioCumprido: boolean;
    saldoFeriasDiasManual: number;
    observacoes?: string;
  },
  processadoPor: string,
): Promise<{ success: boolean; data?: CessacaoContrato; error?: string }> {
  try {
    if (!dados.empresaId || !dados.colaboradorId) {
      return { success: false, error: "Empresa e colaborador são obrigatórios" };
    }

    if (!dados.dataCessacaoEfetiva || !/^\d{4}-\d{2}-\d{2}$/.test(dados.dataCessacaoEfetiva)) {
      return { success: false, error: "Data de cessação efectiva inválida" };
    }

    const colaborador = await buscarColaboradorPorId(dados.colaboradorId);
    if (!colaborador || colaborador.empresaId !== dados.empresaId) {
      return { success: false, error: "Colaborador não encontrado nesta empresa" };
    }

    if (colaborador.estado === "cessado") {
      return { success: false, error: "Este colaborador já se encontra cessado" };
    }

    const empresa = await buscarEmpresaPorId(dados.empresaId);

    const tipoContratoIndeterminado = colaborador.tipoContrato === "efetivo";
    const { anos, mesesRestantes, totalMeses } = diferencaEmAnosEMeses(colaborador.dataAdmissao, dados.dataCessacaoEfetiva);

    const avisoPrevioDiasExigidos = calcularAvisoPrevioDias(dados.motivo, tipoContratoIndeterminado, totalMeses);

    const indemnizacao = await calcularIndemnizacao(
      dados.motivo,
      colaborador.salarioBase,
      anos,
      mesesRestantes,
      tipoContratoIndeterminado,
      empresa?.salarioMinimoSetorMzn ?? null,
    );

    const diaCessacao = Number(dados.dataCessacaoEfetiva.slice(8, 10));
    const [anoCessacao, mesCessacao] = dados.dataCessacaoEfetiva.slice(0, 7).split("-").map(Number);
    const diasNoMes = new Date(anoCessacao, mesCessacao, 0).getDate();
    const salarioProporcionalDias = diaCessacao;
    const salarioProporcionalValor = arredondar((colaborador.salarioBase / diasNoMes) * salarioProporcionalDias);

    const saldoFeriasValor = arredondar((colaborador.salarioBase / 30) * (dados.saldoFeriasDiasManual || 0));

    const totalALiquidar = arredondar(
      (indemnizacao.valor || 0) + saldoFeriasValor + salarioProporcionalValor,
    );

    const id = `cessacao_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;

    const cessacao: CessacaoContrato = {
      id,
      empresaId: dados.empresaId,
      colaboradorId: dados.colaboradorId,
      motivo: dados.motivo,
      dataComunicacao: dados.dataComunicacao,
      dataCessacaoEfetiva: dados.dataCessacaoEfetiva,
      avisoPrevioDiasExigidos,
      avisoPrevioCumprido: dados.avisoPrevioCumprido,
      indemnizacaoDiasPorAno: indemnizacao.diasPorAno,
      indemnizacaoValor: indemnizacao.valor,
      indemnizacaoRequerCalculoManual: indemnizacao.requerCalculoManual,
      indemnizacaoObservacao: indemnizacao.observacao,
      saldoFeriasDiasManual: dados.saldoFeriasDiasManual || 0,
      saldoFeriasValor,
      salarioProporcionalDias,
      salarioProporcionalValor,
      totalALiquidar,
      observacoes: dados.observacoes,
      estado: "rascunho",
      processadoPor,
      dataCriacao: new Date().toISOString(),
    };

    await kv.set(`${CESSACAO_PREFIX}${id}`, cessacao);

    logger.info(`Cessação de contrato criada (rascunho): ${colaborador.nome} - ${dados.motivo}`);

    return { success: true, data: cessacao };
  } catch (error) {
    logger.error("Erro ao criar cessação de contrato:", error);
    return { success: false, error: error.message };
  }
}

export async function buscarCessacaoPorId(id: string): Promise<CessacaoContrato | null> {
  try {
    const cessacao = await kv.get(`${CESSACAO_PREFIX}${id}`);
    return cessacao as CessacaoContrato | null;
  } catch (error) {
    logger.error("Erro ao buscar cessação:", error);
    return null;
  }
}

export async function listarCessacoes(
  empresaId: string,
  filtros?: { colaboradorId?: string; estado?: EstadoCessacao },
): Promise<CessacaoContrato[]> {
  try {
    const todas = await kv.getByPrefix<CessacaoContrato>(CESSACAO_PREFIX);

    let cessacoes = todas.filter((item) => item && item.empresaId === empresaId);

    if (filtros?.colaboradorId) {
      cessacoes = cessacoes.filter((item) => item.colaboradorId === filtros.colaboradorId);
    }

    if (filtros?.estado) {
      cessacoes = cessacoes.filter((item) => item.estado === filtros.estado);
    }

    cessacoes.sort((a, b) => b.dataCessacaoEfetiva.localeCompare(a.dataCessacaoEfetiva));

    return cessacoes;
  } catch (error) {
    logger.error("Erro ao listar cessações:", error);
    return [];
  }
}

// Confirmar a cessacao marca o colaborador como cessado (reaproveita
// cessarColaborador ja existente em colaboradores.tsx) — so acontece aqui,
// nunca na criacao do rascunho, para permitir rever/corrigir os calculos
// antes de afectar o cadastro do colaborador.
export async function confirmarCessacaoContrato(
  id: string,
): Promise<{ success: boolean; data?: CessacaoContrato; error?: string }> {
  try {
    const cessacao = await buscarCessacaoPorId(id);
    if (!cessacao) {
      return { success: false, error: "Cessação de contrato não encontrada" };
    }

    if (cessacao.estado === "confirmado") {
      return { success: false, error: "Esta cessação já foi confirmada" };
    }

    const resultadoColaborador = await cessarColaborador(cessacao.colaboradorId, cessacao.dataCessacaoEfetiva);
    if (!resultadoColaborador.success) {
      return { success: false, error: resultadoColaborador.error };
    }

    const cessacaoAtualizada: CessacaoContrato = {
      ...cessacao,
      estado: "confirmado",
      dataAtualizacao: new Date().toISOString(),
    };

    await kv.set(`${CESSACAO_PREFIX}${id}`, cessacaoAtualizada);

    await criarComunicacaoINSS({
      empresaId: cessacao.empresaId,
      colaboradorId: cessacao.colaboradorId,
      colaboradorNome: resultadoColaborador.data?.nome || cessacao.colaboradorId,
      tipo: "cessacao",
      dataEvento: cessacao.dataCessacaoEfetiva,
      detalhes: {
        motivo: cessacao.motivo,
        dataAdmissao: resultadoColaborador.data?.dataAdmissao,
      },
    });

    logger.info(`Cessação de contrato confirmada: ${id}`);

    return { success: true, data: cessacaoAtualizada };
  } catch (error) {
    logger.error("Erro ao confirmar cessação:", error);
    return { success: false, error: error.message };
  }
}

export async function deletarCessacaoContrato(id: string): Promise<{ success: boolean; error?: string }> {
  try {
    const cessacao = await buscarCessacaoPorId(id);
    if (!cessacao) {
      return { success: false, error: "Cessação de contrato não encontrada" };
    }

    if (cessacao.estado === "confirmado") {
      return { success: false, error: "Não é possível eliminar uma cessação já confirmada. Reactive o colaborador manualmente se foi um erro." };
    }

    await kv.del(`${CESSACAO_PREFIX}${id}`);

    logger.info(`Cessação de contrato (rascunho) deletada: ${id}`);

    return { success: true };
  } catch (error) {
    logger.error("Erro ao deletar cessação:", error);
    return { success: false, error: error.message };
  }
}
