import * as kv from "./kv_store.tsx";
import { createLogger } from "./logger.tsx";

const logger = createLogger('RubricasSalariais');

// Motor de Rubricas Salariais — substitui o calculo fixo (so INSS/IRPS) por
// uma lista configuravel de linhas de vencimento/desconto, ao estilo dos
// grandes ERPs (SAP "wage types", Sage/PHC/Primavera "rubricas"). Por
// empresa (nao versionado por ano — ajustar isto e operacao corrente, ao
// contrario dos Parametros Fiscais que sao uma revisao legal anual).
//
// Âmbito desta iteracao (deliberadamente limitado — ver nota no plano):
//   - Sem motor de formulas livres (eval) — so valor_fixo, percentual_rubrica,
//     sistema (logica interna ja existente para INSS/IRPS) e manual (valor
//     inserido no momento do processamento).
//   - Rubricas "valor_fixo" aplicam-se a TODOS os colaboradores activos por
//     igual — ainda nao ha mecanismo de "esta rubrica so se aplica a X".
//   - Horas extra / faltas / atrasos calculados automaticamente a partir de
//     Presencas ainda NAO estao ligados (precisam de Calendarios de
//     Trabalho, que tambem nao existem) — ficam como rubricas de sistema
//     com valor 0 ate essa logica ser construida.
export type TipoRubrica = "remuneratoria" | "desconto" | "encargo_patronal";
export type NaturezaRubrica = "base" | "variavel" | "subsidio" | "contribuicao" | "imposto" | "outro";
export type ModoCalculoRubrica = "valor_fixo" | "percentual_rubrica" | "sistema" | "manual";
export type PeriodicidadeRubrica = "mensal" | "trimestral" | "anual" | "eventual";

export interface RubricaSalarial {
  id: string;
  empresaId: string;
  codigo: string;
  descricao: string;
  tipo: TipoRubrica;
  natureza: NaturezaRubrica;
  modoCalculo: ModoCalculoRubrica;
  valorFixo?: number; // se modoCalculo === 'valor_fixo'
  rubricaBaseId?: string; // se modoCalculo === 'percentual_rubrica'
  percentual?: number; // se modoCalculo === 'percentual_rubrica'
  periodicidade: PeriodicidadeRubrica;
  incideInss: boolean;
  incideIrps: boolean;
  incideFerias: boolean;
  incide13Mes: boolean;
  incideIndemnizacao: boolean;
  visivelNoRecibo: boolean;
  ordemApresentacao: number;
  grupoAgregacao?: string;
  ativa: boolean;
  sistema: boolean; // rubricas pre-definidas (Salario Base, INSS, IRPS) — nao podem ser eliminadas
  dataCriacao: string;
  dataAtualizacao?: string;
}

const RUBRICA_PREFIX = "rubrica_salarial:";

// Codigos reservados para as rubricas de sistema, usados pelo motor de
// calculo (calculo-salarial.tsx) para encontrar a rubrica certa sem
// depender de descricao/ordem.
export const CODIGO_SALARIO_BASE = "SALBASE";
export const CODIGO_INSS_TRABALHADOR = "INSSTRAB";
export const CODIGO_INSS_EMPREGADOR = "INSSEMP";
export const CODIGO_IRPS = "IRPS";

// Horas extraordinarias (Art. 99), majoracao escalonada 50%/100% pela
// fronteira das 20h (Art. 125 n.1) — ver calculo-horas-trabalho.tsx.
export const CODIGO_HORA_EXTRA_UTIL = "HEXTRADU";

// Trabalho excepcional (Art. 98 — dia de descanso semanal/complementar,
// feriado ou tolerancia de ponto), majoracao fixa +100% (Art. 125 n.2).
// Antes um unico codigo agregado (HEXTRADF); desdobrado em 5 codigos para
// reporting/auditoria separados por tipo de dia, mesma majoracao legal.
export const CODIGO_HORA_EXTRA_SABADO = "HEXTRASAB";
export const CODIGO_HORA_EXTRA_DOMINGO = "HEXTRADOM";
export const CODIGO_HORA_EXTRA_FERIADO_NACIONAL = "HEXTRAFERNAC";
export const CODIGO_HORA_EXTRA_FERIADO_LOCAL = "HEXTRAFERLOC";
export const CODIGO_HORA_EXTRA_TOLERANCIA = "HEXTRATOL";

// Codigo agregado ANTIGO — mantido so para depreciar registos ja semeados
// antes deste desdobramento (ver garantirRubricasStandard).
const CODIGO_HORA_EXTRA_EXCEPCIONAL_ANTIGO = "HEXTRADF";

export const CODIGO_DESCONTO_SAIDA_ANTECIPADA = "DESCSAIDA";

function rubricasStandardParaSeed(empresaId: string): Omit<RubricaSalarial, "id" | "dataCriacao">[] {
  const agora = () => new Date().toISOString();

  return [
    {
      empresaId, codigo: CODIGO_SALARIO_BASE, descricao: "Salário Base", tipo: "remuneratoria", natureza: "base",
      modoCalculo: "sistema", periodicidade: "mensal", incideInss: true, incideIrps: true, incideFerias: true,
      incide13Mes: true, incideIndemnizacao: true, visivelNoRecibo: true, ordemApresentacao: 10, ativa: true, sistema: true,
    },
    {
      empresaId, codigo: "SUBALIM", descricao: "Subsídio de Alimentação", tipo: "remuneratoria", natureza: "subsidio",
      modoCalculo: "valor_fixo", valorFixo: 0, periodicidade: "mensal", incideInss: false, incideIrps: false,
      incideFerias: false, incide13Mes: false, incideIndemnizacao: false, visivelNoRecibo: true, ordemApresentacao: 20,
      ativa: false, sistema: false,
    },
    {
      empresaId, codigo: "SUBTRANSP", descricao: "Subsídio de Transporte", tipo: "remuneratoria", natureza: "subsidio",
      modoCalculo: "valor_fixo", valorFixo: 0, periodicidade: "mensal", incideInss: false, incideIrps: false,
      incideFerias: false, incide13Mes: false, incideIndemnizacao: false, visivelNoRecibo: true, ordemApresentacao: 21,
      ativa: false, sistema: false,
    },
    {
      empresaId, codigo: CODIGO_HORA_EXTRA_UTIL, descricao: "Horas Extraordinárias — Dias Úteis", tipo: "remuneratoria", natureza: "variavel",
      modoCalculo: "sistema", periodicidade: "mensal", incideInss: true, incideIrps: true, incideFerias: false,
      incide13Mes: false, incideIndemnizacao: false, visivelNoRecibo: true, ordemApresentacao: 30, ativa: false, sistema: true,
    },
    {
      empresaId, codigo: CODIGO_HORA_EXTRA_SABADO, descricao: "Horas Extraordinárias — Sábado (descanso complementar)", tipo: "remuneratoria", natureza: "variavel",
      modoCalculo: "sistema", periodicidade: "mensal", incideInss: true, incideIrps: true, incideFerias: false,
      incide13Mes: false, incideIndemnizacao: false, visivelNoRecibo: true, ordemApresentacao: 31, ativa: false, sistema: true,
    },
    {
      empresaId, codigo: CODIGO_HORA_EXTRA_DOMINGO, descricao: "Horas Extraordinárias — Domingo (descanso semanal)", tipo: "remuneratoria", natureza: "variavel",
      modoCalculo: "sistema", periodicidade: "mensal", incideInss: true, incideIrps: true, incideFerias: false,
      incide13Mes: false, incideIndemnizacao: false, visivelNoRecibo: true, ordemApresentacao: 32, ativa: false, sistema: true,
    },
    {
      empresaId, codigo: CODIGO_HORA_EXTRA_FERIADO_NACIONAL, descricao: "Horas Extraordinárias — Feriado Nacional", tipo: "remuneratoria", natureza: "variavel",
      modoCalculo: "sistema", periodicidade: "mensal", incideInss: true, incideIrps: true, incideFerias: false,
      incide13Mes: false, incideIndemnizacao: false, visivelNoRecibo: true, ordemApresentacao: 33, ativa: false, sistema: true,
    },
    {
      empresaId, codigo: CODIGO_HORA_EXTRA_FERIADO_LOCAL, descricao: "Horas Extraordinárias — Feriado Local", tipo: "remuneratoria", natureza: "variavel",
      modoCalculo: "sistema", periodicidade: "mensal", incideInss: true, incideIrps: true, incideFerias: false,
      incide13Mes: false, incideIndemnizacao: false, visivelNoRecibo: true, ordemApresentacao: 34, ativa: false, sistema: true,
    },
    {
      empresaId, codigo: CODIGO_HORA_EXTRA_TOLERANCIA, descricao: "Horas Extraordinárias — Tolerância de Ponto", tipo: "remuneratoria", natureza: "variavel",
      modoCalculo: "sistema", periodicidade: "mensal", incideInss: true, incideIrps: true, incideFerias: false,
      incide13Mes: false, incideIndemnizacao: false, visivelNoRecibo: true, ordemApresentacao: 35, ativa: false, sistema: true,
    },
    {
      empresaId, codigo: "BONUSANT", descricao: "Bónus de Antiguidade", tipo: "remuneratoria", natureza: "variavel",
      modoCalculo: "manual", periodicidade: "mensal", incideInss: true, incideIrps: true, incideFerias: false,
      incide13Mes: false, incideIndemnizacao: false, visivelNoRecibo: true, ordemApresentacao: 40, ativa: true, sistema: false,
    },
    {
      empresaId, codigo: "BONUSPROD", descricao: "Bónus de Produtividade", tipo: "remuneratoria", natureza: "variavel",
      modoCalculo: "manual", periodicidade: "mensal", incideInss: true, incideIrps: true, incideFerias: false,
      incide13Mes: false, incideIndemnizacao: false, visivelNoRecibo: true, ordemApresentacao: 41, ativa: true, sistema: false,
    },
    {
      empresaId, codigo: "BONUSASSID", descricao: "Bónus de Assiduidade", tipo: "remuneratoria", natureza: "variavel",
      modoCalculo: "manual", periodicidade: "mensal", incideInss: true, incideIrps: true, incideFerias: false,
      incide13Mes: false, incideIndemnizacao: false, visivelNoRecibo: true, ordemApresentacao: 42, ativa: true, sistema: false,
    },
    {
      empresaId, codigo: "BONUSEXTRA", descricao: "Bónus Extraordinário", tipo: "remuneratoria", natureza: "variavel",
      modoCalculo: "manual", periodicidade: "eventual", incideInss: true, incideIrps: true, incideFerias: false,
      incide13Mes: false, incideIndemnizacao: false, visivelNoRecibo: true, ordemApresentacao: 43, ativa: true, sistema: false,
    },
    {
      empresaId, codigo: "AJCUSTO", descricao: "Ajuda de Custo", tipo: "remuneratoria", natureza: "outro",
      modoCalculo: "manual", periodicidade: "eventual", incideInss: false, incideIrps: false, incideFerias: false,
      incide13Mes: false, incideIndemnizacao: false, visivelNoRecibo: true, ordemApresentacao: 50, ativa: true, sistema: false,
    },
    {
      empresaId, codigo: "ABONOFALHAS", descricao: "Abono para Falhas", tipo: "remuneratoria", natureza: "outro",
      modoCalculo: "manual", periodicidade: "mensal", incideInss: false, incideIrps: false, incideFerias: false,
      incide13Mes: false, incideIndemnizacao: false, visivelNoRecibo: true, ordemApresentacao: 51, ativa: false, sistema: false,
    },
    {
      empresaId, codigo: "DESCFALTA", descricao: "Desconto por Falta Injustificada", tipo: "desconto", natureza: "outro",
      modoCalculo: "sistema", periodicidade: "mensal", incideInss: false, incideIrps: false, incideFerias: false,
      incide13Mes: false, incideIndemnizacao: false, visivelNoRecibo: true, ordemApresentacao: 60, ativa: false, sistema: true,
    },
    {
      empresaId, codigo: "DESCATRASO", descricao: "Desconto por Atraso", tipo: "desconto", natureza: "outro",
      modoCalculo: "sistema", periodicidade: "mensal", incideInss: false, incideIrps: false, incideFerias: false,
      incide13Mes: false, incideIndemnizacao: false, visivelNoRecibo: true, ordemApresentacao: 61, ativa: false, sistema: true,
    },
    {
      empresaId, codigo: CODIGO_DESCONTO_SAIDA_ANTECIPADA, descricao: "Desconto por Saída Antecipada", tipo: "desconto", natureza: "outro",
      modoCalculo: "sistema", periodicidade: "mensal", incideInss: false, incideIrps: false, incideFerias: false,
      incide13Mes: false, incideIndemnizacao: false, visivelNoRecibo: true, ordemApresentacao: 62, ativa: false, sistema: true,
    },
    {
      empresaId, codigo: "EMPRESTIMO", descricao: "Empréstimo Interno", tipo: "desconto", natureza: "outro",
      modoCalculo: "manual", periodicidade: "mensal", incideInss: false, incideIrps: false, incideFerias: false,
      incide13Mes: false, incideIndemnizacao: false, visivelNoRecibo: true, ordemApresentacao: 70, ativa: true, sistema: false,
    },
    {
      empresaId, codigo: "PENSAOALIM", descricao: "Pensão Alimentícia", tipo: "desconto", natureza: "outro",
      modoCalculo: "manual", periodicidade: "mensal", incideInss: false, incideIrps: false, incideFerias: false,
      incide13Mes: false, incideIndemnizacao: false, visivelNoRecibo: true, ordemApresentacao: 71, ativa: true, sistema: false,
    },
    {
      empresaId, codigo: "RETJUDICIAL", descricao: "Retenção Judicial", tipo: "desconto", natureza: "outro",
      modoCalculo: "manual", periodicidade: "mensal", incideInss: false, incideIrps: false, incideFerias: false,
      incide13Mes: false, incideIndemnizacao: false, visivelNoRecibo: true, ordemApresentacao: 72, ativa: true, sistema: false,
    },
    {
      empresaId, codigo: CODIGO_INSS_TRABALHADOR, descricao: "INSS — Trabalhador", tipo: "desconto", natureza: "contribuicao",
      modoCalculo: "sistema", periodicidade: "mensal", incideInss: false, incideIrps: false, incideFerias: false,
      incide13Mes: false, incideIndemnizacao: false, visivelNoRecibo: true, ordemApresentacao: 900, ativa: true, sistema: true,
    },
    {
      empresaId, codigo: CODIGO_INSS_EMPREGADOR, descricao: "INSS — Encargo Patronal", tipo: "encargo_patronal", natureza: "contribuicao",
      modoCalculo: "sistema", periodicidade: "mensal", incideInss: false, incideIrps: false, incideFerias: false,
      incide13Mes: false, incideIndemnizacao: false, visivelNoRecibo: false, ordemApresentacao: 901, ativa: true, sistema: true,
    },
    {
      empresaId, codigo: CODIGO_IRPS, descricao: "IRPS", tipo: "desconto", natureza: "imposto",
      modoCalculo: "sistema", periodicidade: "mensal", incideInss: false, incideIrps: false, incideFerias: false,
      incide13Mes: false, incideIndemnizacao: false, visivelNoRecibo: true, ordemApresentacao: 902, ativa: true, sistema: true,
    },
  ].map((r) => ({ ...r, dataAtualizacao: undefined } as Omit<RubricaSalarial, "id" | "dataCriacao">));
}

// Semeia as rubricas standard, adicionando apenas os CODIGOS de sistema
// ainda em falta (self-healing por codigo, nao por empresa) — necessario
// porque o desdobramento de HEXTRADF em 5 codigos granulares (Sabado/
// Domingo/Feriado Nacional/Feriado Local/Tolerancia) e o novo DESCSAIDA
// foram adicionados depois de empresas ja terem sido semeadas com o
// conjunto antigo. Mesma logica ja usada para o diario SAL em
// contabilidade.tsx. Tambem depreca o codigo antigo HEXTRADF, se existir,
// para nao ficar um codigo "orfao" activo sem calculo associado.
async function garantirRubricasStandard(empresaId: string): Promise<void> {
  const existentes = (await kv.getByPrefix<RubricaSalarial>(RUBRICA_PREFIX)).filter(
    (item) => item && item.empresaId === empresaId,
  );
  const codigosExistentes = new Set(existentes.map((item) => item.codigo));

  for (const rubrica of rubricasStandardParaSeed(empresaId)) {
    if (codigosExistentes.has(rubrica.codigo)) continue;

    const id = `rubrica_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
    await kv.set(`${RUBRICA_PREFIX}${id}`, { ...rubrica, id, dataCriacao: new Date().toISOString() });
    logger.info(`Rubrica salarial standard semeada para empresa ${empresaId}: ${rubrica.codigo}`);
  }

  const rubricaAntiga = existentes.find((item) => item.codigo === CODIGO_HORA_EXTRA_EXCEPCIONAL_ANTIGO && item.sistema);
  if (rubricaAntiga && rubricaAntiga.ativa) {
    await kv.set(`${RUBRICA_PREFIX}${rubricaAntiga.id}`, {
      ...rubricaAntiga,
      ativa: false,
      descricao: "Horas Extraordinárias — Domingos/Feriados (substituída pelos códigos HEXTRASAB/HEXTRADOM/HEXTRAFERNAC/HEXTRAFERLOC/HEXTRATOL)",
      dataAtualizacao: new Date().toISOString(),
    });
    logger.info(`Rubrica de sistema obsoleta desactivada para empresa ${empresaId}: ${CODIGO_HORA_EXTRA_EXCEPCIONAL_ANTIGO}`);
  }
}

export async function listarRubricasSalariais(
  empresaId: string,
  filtros?: { ativa?: boolean },
): Promise<RubricaSalarial[]> {
  try {
    await garantirRubricasStandard(empresaId);

    const todas = await kv.getByPrefix<RubricaSalarial>(RUBRICA_PREFIX);
    let rubricas = todas.filter((item) => item && item.empresaId === empresaId);

    if (filtros?.ativa !== undefined) {
      rubricas = rubricas.filter((item) => item.ativa === filtros.ativa);
    }

    rubricas.sort((a, b) => a.ordemApresentacao - b.ordemApresentacao);

    return rubricas;
  } catch (error) {
    logger.error("Erro ao listar rubricas salariais:", error);
    return [];
  }
}

export async function buscarRubricaSalarialPorId(id: string): Promise<RubricaSalarial | null> {
  try {
    return (await kv.get(`${RUBRICA_PREFIX}${id}`)) as RubricaSalarial | null;
  } catch (error) {
    logger.error("Erro ao buscar rubrica salarial:", error);
    return null;
  }
}

export async function criarRubricaSalarial(
  dados: Omit<RubricaSalarial, "id" | "dataCriacao" | "sistema">,
): Promise<{ success: boolean; data?: RubricaSalarial; error?: string }> {
  try {
    if (!dados.empresaId || !dados.codigo?.trim() || !dados.descricao?.trim()) {
      return { success: false, error: "Empresa, código e descrição são obrigatórios" };
    }

    const existentes = await listarRubricasSalariais(dados.empresaId);
    if (existentes.some((item) => item.codigo.toUpperCase() === dados.codigo.trim().toUpperCase())) {
      return { success: false, error: "Já existe uma rubrica com este código nesta empresa" };
    }

    if (dados.modoCalculo === "percentual_rubrica" && !dados.rubricaBaseId) {
      return { success: false, error: "Indique a rubrica base para o cálculo percentual" };
    }

    const id = `rubrica_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
    const rubrica: RubricaSalarial = {
      ...dados,
      id,
      codigo: dados.codigo.trim().toUpperCase(),
      sistema: false,
      dataCriacao: new Date().toISOString(),
    };

    await kv.set(`${RUBRICA_PREFIX}${id}`, rubrica);
    logger.info(`Rubrica salarial criada: ${rubrica.codigo} - ${rubrica.descricao}`);

    return { success: true, data: rubrica };
  } catch (error) {
    logger.error("Erro ao criar rubrica salarial:", error);
    return { success: false, error: error.message };
  }
}

export async function atualizarRubricaSalarial(
  id: string,
  dados: Partial<RubricaSalarial>,
): Promise<{ success: boolean; data?: RubricaSalarial; error?: string }> {
  try {
    const rubrica = await buscarRubricaSalarialPorId(id);
    if (!rubrica) {
      return { success: false, error: "Rubrica não encontrada" };
    }

    if (rubrica.sistema && (dados.codigo !== undefined || dados.modoCalculo !== undefined || dados.tipo !== undefined)) {
      return { success: false, error: "Não é possível alterar o código, tipo ou modo de cálculo de uma rubrica de sistema" };
    }

    const rubricaAtualizada: RubricaSalarial = {
      ...rubrica,
      ...dados,
      id: rubrica.id,
      empresaId: rubrica.empresaId,
      codigo: rubrica.sistema ? rubrica.codigo : (dados.codigo?.trim().toUpperCase() ?? rubrica.codigo),
      sistema: rubrica.sistema,
      dataCriacao: rubrica.dataCriacao,
      dataAtualizacao: new Date().toISOString(),
    };

    await kv.set(`${RUBRICA_PREFIX}${id}`, rubricaAtualizada);
    logger.info(`Rubrica salarial atualizada: ${id}`);

    return { success: true, data: rubricaAtualizada };
  } catch (error) {
    logger.error("Erro ao atualizar rubrica salarial:", error);
    return { success: false, error: error.message };
  }
}

export async function deletarRubricaSalarial(id: string): Promise<{ success: boolean; error?: string }> {
  try {
    const rubrica = await buscarRubricaSalarialPorId(id);
    if (!rubrica) {
      return { success: false, error: "Rubrica não encontrada" };
    }

    if (rubrica.sistema) {
      return { success: false, error: "Rubricas de sistema não podem ser eliminadas — desactive-a em vez disso" };
    }

    await kv.del(`${RUBRICA_PREFIX}${id}`);
    logger.info(`Rubrica salarial deletada: ${id}`);

    return { success: true };
  } catch (error) {
    logger.error("Erro ao deletar rubrica salarial:", error);
    return { success: false, error: error.message };
  }
}
