/**
 * 🏢 MÓDULO DE CONTABILIDADE - ERP F-Tonline
 * 
 * Sistema contábil completo compatível com:
 * - SNC (Sistema de Normalização Contabilística) Moçambique
 * - AT (Autoridade Tributária)
 * - Estoque Permanente
 * - Multi-empresa
 * - Multi-exercício
 * 
 * Funcionalidades:
 * ✅ Plano de Contas Hierárquico
 * ✅ Diários Contábeis
 * ✅ Lançamentos Automáticos
 * ✅ Balancete
 * ✅ Balanço
 * ✅ Demonstração de Resultados
 * ✅ Apuramento de IVA
 * ✅ Fecho de Período
 * ✅ Exportação SAF-T
 */

import * as kv from './kv_store.tsx';
import * as exerciciosEconomicos from './exercicios-economicos.tsx';
import * as facturas from './facturas.tsx';
import * as recibos from './recibos.tsx';
import * as vendasDinheiro from './vendas-dinheiro.tsx';
import * as notas from './notas.tsx';
import * as adiantamentos from './adiantamentos.tsx';
import * as compras from './compras.tsx';
import * as fornecedores from './fornecedores.tsx';
import * as movimentosStock from './movimentos-stock.tsx';
import { reservarNumeroSequencial } from './document-numbering.tsx';

// ========================================
// TIPOS E INTERFACES
// ========================================

export interface ContaContabil {
  id: string;
  empresaId: string;
  codigo: string;
  nome: string;
  classe: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8; // Classes SNC
  tipo: 'ativo' | 'passivo' | 'capital' | 'rendimento' | 'gasto' | 'resultado';
  parentId?: string;
  nivel: number;
  aceitaLancamentos: boolean;
  grupoRelatorio?: string; // Para agrupamento em relatórios
  isSystem: boolean; // Conta do sistema (não pode ser apagada)
  ativo: boolean;
  saldoInicial?: number;
  dataCriacao: string;
}

export interface Diario {
  id: string;
  empresaId: string;
  codigo: string;
  nome: string;
  tipo: 'geral' | 'vendas' | 'compras' | 'caixa' | 'bancos' | 'operacoes_diversas' | 'salarios' | 'regularizacoes';
  descricao?: string;
  permiteLancamentoManual?: boolean;
  geraLancamentoAutomatico?: boolean;
  documentosOrigem?: string[];
  ativo: boolean;
  bloqueado: boolean;
  dataCriacao: string;
  totalLancamentos?: number;
  ultimoLancamento?: string;
}

export interface LancamentoContabil {
  id: string;
  empresaId: string;
  diarioId: string;
  origemLancamento?: 'manual' | 'automatico';
  tipoDocumento?: string; // 'fatura', 'recibo', 'compra', etc
  documentoId?: string; // ID do documento origem
  numeroDocumento?: string;
  exercicioEconomicoId?: string;
  anoExercicio?: number;
  clienteId?: string;
  clienteNome?: string;
  fornecedorId?: string;
  fornecedorNome?: string;
  entidadeNome?: string;
  data: string;
  referencia: string;
  descricao: string;
  totalDebito: number;
  totalCredito: number;
  estado: 'rascunho' | 'lancado' | 'estornado';
  dataLancamento?: string;
  criadoPor: string;
  linhas: LinhaLancamento[];
  observacoes?: string;
  documentosSuporte?: DocumentoSuporteContabil[];
  // ✅ Metadados de estorno — o lancamento original nunca e apagado/editado
  // (fica marcado estornado, preservando o historico), e a reversao e um
  // lancamento novo e independente, datado no periodo corrente.
  estornadoEm?: string;
  estornadoPor?: string;
  motivoEstorno?: string;
  lancamentoEstornoId?: string; // no original: aponta para o lancamento de reversao criado
  estornoDeId?: string; // no lancamento de reversao: aponta para o original estornado
  ehEstorno?: boolean;
}

export type EstadoModeloFiscal =
  | 'rascunho'
  | 'preenchido'
  | 'fechado'
  | 'aprovado'
  | 'substituido'
  | 'submetido'
  | 'arquivado';

export interface ModeloFiscalContabil {
  id: string;
  empresaId: string;
  ano: number;
  modeloId: string;
  modeloCodigo: string;
  modeloNome: string;
  natureza?: string;
  periodo: {
    dataInicio: string;
    dataFim: string;
  };
  estado: EstadoModeloFiscal;
  versao: number;
  versaoModelo?: string;
  fonteTemplate?: string;
  camposManuais: Record<string, string>;
  camposGerados: any[];
  hashConteudo?: string;
  criadoEm: string;
  criadoPor?: string;
  atualizadoEm: string;
  atualizadoPor?: string;
  aprovadoEm?: string;
  aprovadoPor?: string;
  fechadoEm?: string;
  fechadoPor?: string;
  submetidoEm?: string;
  submetidoPor?: string;
  historico: Array<{
    data: string;
    usuarioId?: string;
    acao: string;
    estado?: EstadoModeloFiscal;
    versao: number;
    hashConteudo?: string;
  }>;
}

export interface DocumentoSuporteContabil {
  id: string;
  nome: string;
  fileName: string;
  publicUrl?: string;
  mimeType?: string;
  tamanho?: number;
  extensao?: string;
  descricao?: string;
  criadoEm?: string;
}

export interface LinhaLancamento {
  id: string;
  contaId: string;
  contaCodigo?: string; // Desnormalizado para performance
  contaNome?: string;
  descricao: string;
  debito: number;
  credito: number;
  activoFixoId?: string;
  activoFixo?: Partial<ActivoFixo>;
}

export interface PeriodoFiscal {
  id: string;
  empresaId: string;
  ano: number;
  mes: number;
  dataInicio: string;
  dataFim: string;
  estado: 'aberto' | 'fechado';
  dataFecho?: string;
  fechadoPor?: string;
}

export interface ConfiguracaoImposto {
  id: string;
  empresaId: string;
  nome: string;
  taxa: number;
  tipo: 'iva_liquidado' | 'iva_dedutivel' | 'retencao';
  contaId: string;
  ativo: boolean;
}

export interface MapeamentoContabil {
  id: string;
  empresaId: string;
  tipoDocumento: string;
  automatico?: boolean;
  contaVendas?: string; // Conta de receita
  contaCompras?: string; // Conta de gastos/estoque
  contaEstoque?: string; // Conta de estoque
  contaReceber?: string; // Conta de clientes
  contaPagar?: string; // Conta de fornecedores
  contaIVALiquidado?: string;
  contaIVADedutivel?: string;
  contaCMV?: string; // Custo de Mercadoria Vendida
  contaPerdasStock?: string;
  contaRegularizacaoStock?: string;
  contaAdiantamentoFornecedor?: string;
  contaCaixa?: string;
  contaBanco?: string;
  // Contas do Processamento Salarial (RH) — ver gerarLancamentoFolhaPagamento()
  contaGastoRemuneracoes?: string; // 6.2.2 Remunerações dos trabalhadores (debito)
  contaGastoEncargosSociais?: string; // 6.2.3 Encargos sobre remunerações (debito, INSS patronal)
  contaINSSPagar?: string; // 4.4.9 Contribuições para o INSS (credito)
  contaIRPSPagar?: string; // 4.4.2.1 Impostos retidos na fonte — trabalho dependente (credito)
  contaOutrosDescontosPessoal?: string; // 4.6.2.9 Outras operações com os trabalhadores (credito)
  contaRemuneracoesPagar?: string; // 4.6.2.2 Remunerações a pagar aos trabalhadores (credito)
}

export interface ActivoFixo {
  id: string;
  empresaId: string;
  codigo: string;
  descricao: string;
  referenciaInterna?: string;
  numeroSerie?: string;
  dataAquisicao: string;
  dataEntradaServico: string;
  contaId?: string;
  contaCodigo: string;
  contaNome?: string;
  contaAmortizacaoId?: string;
  contaAmortizacaoCodigo?: string;
  contaAmortizacaoNome?: string;
  custoAquisicao: number;
  valorResidual: number;
  vidaUtil: number;
  taxaAmortizacao: number;
  metodoAmortizacao: 'linear' | 'decrescente' | 'unidades_producao';
  unidadesProducaoEstimadas?: number;
  unidadesProducaoPeriodo?: number;
  unidadesProducaoAcumuladas?: number;
  estado: 'activo' | 'em_curso' | 'baixado' | 'alienado' | 'sinistro';
  dataBaixa?: string;
  valorBaixa?: number;
  reavaliacoes?: number;
  outrasAlteracoes?: number;
  observacoes?: string;
  lancamentoId?: string;
  linhaLancamentoId?: string;
  origemLancamentoReferencia?: string;
  origemNumeroDocumento?: string;
  ativo: boolean;
  criadoEm: string;
  atualizadoEm: string;
}

export interface MovimentoReconciliacaoBancaria {
  movimentoId: string;
  lancamentoId: string;
  linhaId?: string;
  data: string;
  referencia?: string;
  descricao?: string;
  debito: number;
  credito: number;
  valor: number;
}

export interface ReconciliacaoBancaria {
  id: string;
  empresaId: string;
  contaId: string;
  contaCodigo: string;
  contaNome: string;
  dataInicio: string;
  dataFim: string;
  dataExtracto: string;
  saldoInicial: number;
  saldoContabil: number;
  saldoReconciliado: number;
  saldoExtracto: number;
  diferenca: number;
  movimentos: MovimentoReconciliacaoBancaria[];
  observacoes?: string;
  estado: 'em_aberto' | 'conferida';
  criadoPor?: string;
  criadoEm: string;
  atualizadoEm: string;
}

// ========================================
// PLANO DE CONTAS SNC - MOÇAMBIQUE
// ========================================

type ContaBaseInput = Pick<ContaContabil, 'codigo' | 'nome'> & {
  tipo?: ContaContabil['tipo'];
  grupoRelatorio?: string;
  aceitaLancamentos?: boolean;
};

export function calcularClasseConta(codigo: string): ContaContabil['classe'] {
  const classe = Number(codigo.split('.')[0]);
  return (classe >= 1 && classe <= 8 ? classe : 1) as ContaContabil['classe'];
}

export function calcularNivelConta(codigo: string): number {
  return Math.max(1, codigo.split('.').length - 1);
}

export function obterCodigoPai(codigo: string): string | undefined {
  const partes = codigo.split('.');
  if (partes.length <= 2) return undefined;
  return partes.slice(0, -1).join('.');
}

function obterCodigoPaiExistente(
  codigo: string,
  contasPorCodigo: Map<string, ContaContabil>
): string | undefined {
  let codigoPai = obterCodigoPai(codigo);

  while (codigoPai) {
    if (contasPorCodigo.has(codigoPai)) return codigoPai;
    codigoPai = obterCodigoPai(codigoPai);
  }

  return undefined;
}

export function compararCodigosContas(a: string, b: string): number {
  return a.localeCompare(b, 'pt', { numeric: true, sensitivity: 'base' });
}

function inferirTipoPorClasse(classe: ContaContabil['classe']): ContaContabil['tipo'] {
  if (classe >= 1 && classe <= 3) return 'ativo';
  if (classe === 4) return 'ativo';
  if (classe === 5) return 'capital';
  if (classe === 6) return 'gasto';
  if (classe === 7) return 'rendimento';
  return 'resultado';
}

function inferirTipoContaBase(codigo: string): ContaContabil['tipo'] {
  const classe = calcularClasseConta(codigo);

  if (classe !== 4) return inferirTipoPorClasse(classe);

  if (codigo.startsWith('4.1.9')) return 'passivo';
  if (codigo.startsWith('4.2.9') || codigo.startsWith('4.2.10')) return 'ativo';
  if (codigo.startsWith('4.2') || codigo.startsWith('4.3')) return 'passivo';
  if (codigo.startsWith('4.4.1.2') || codigo.startsWith('4.4.1.3')) return 'ativo';
  if (codigo.startsWith('4.4.3.1') || codigo.startsWith('4.4.3.2')) return 'ativo';
  if (codigo.startsWith('4.4.3.4.1') || codigo.startsWith('4.4.3.8') || codigo.startsWith('4.4.3.9')) return 'ativo';
  if (codigo.startsWith('4.4.6.1')) return 'ativo';
  if (codigo.startsWith('4.4')) return 'passivo';
  if (codigo.startsWith('4.5') || codigo.startsWith('4.7')) return 'ativo';
  if (codigo.startsWith('4.6.1.3')) return 'ativo';
  if (codigo.startsWith('4.6') || codigo.startsWith('4.8')) return 'passivo';
  if (codigo.startsWith('4.9.1') || codigo.startsWith('4.9.2')) return 'passivo';
  if (codigo.startsWith('4.9.3') || codigo.startsWith('4.9.4')) return 'ativo';

  return 'ativo';
}

function inferirGrupoRelatorioContaBase(codigo: string, tipo: ContaContabil['tipo']): string {
  const classe = calcularClasseConta(codigo);

  if (tipo === 'capital') return 'Capital Próprio';
  if (tipo === 'gasto') return 'Gastos';
  if (tipo === 'rendimento') return 'Rendimentos';
  if (tipo === 'resultado') return 'Resultados';
  if (tipo === 'passivo') {
    if (
      codigo.startsWith('4.3.1.2') ||
      codigo.startsWith('4.3.2') ||
      codigo.startsWith('4.3.3') ||
      codigo.startsWith('4.4.6.2')
    ) {
      return 'Passivo Não Corrente';
    }
    return 'Passivo Corrente';
  }
  if (classe === 3 || codigo.startsWith('4.4.6.1')) return 'Ativo Não Corrente';
  return 'Ativo Corrente';
}

const contasBaseInput: ContaBaseInput[] = [
  { codigo: '1.1', nome: 'CAIXA' },
  { codigo: '1.2', nome: 'BANCOS' },
  { codigo: '1.2.1', nome: 'Depósitos à ordem' },
  { codigo: '1.2.2', nome: 'Depósitos com pré-aviso' },
  { codigo: '1.2.3', nome: 'Depósitos a prazo' },
  { codigo: '1.3', nome: 'OUTROS INSTRUMENTOS FINANCEIROS' },
  { codigo: '1.3.1', nome: 'Derivados' },
  { codigo: '1.3.2', nome: 'Detidos para negociação' },
  { codigo: '1.3.3', nome: 'Outros instrumentos financeiros ao justo valor através dos resultados' },
  { codigo: '2.1', nome: 'COMPRAS' },
  { codigo: '2.1.1', nome: 'Mercadorias' },
  { codigo: '2.1.2', nome: 'Matérias-primas, auxiliares e materiais' },
  { codigo: '2.1.2.1', nome: 'Matérias-primas' },
  { codigo: '2.1.2.2', nome: 'Matérias auxiliares' },
  { codigo: '2.1.2.3', nome: 'Materiais' },
  { codigo: '2.1.2.3.1', nome: 'Combustíveis e lubrificantes' },
  { codigo: '2.1.2.3.2', nome: 'Embalagens comerciais' },
  { codigo: '2.1.2.3.3', nome: 'Peças e sobressalentes' },
  { codigo: '2.1.2.3.9', nome: 'Materiais diversos' },
  { codigo: '2.1.3', nome: 'Ativos biológicos' },
  { codigo: '2.1.7', nome: 'Devoluções de compras' },
  { codigo: '2.1.8', nome: 'Descontos e abatimentos em compras' },
  { codigo: '2.2', nome: 'MERCADORIAS' },
  { codigo: '2.2.1', nome: 'Mercadorias em trânsito' },
  { codigo: '2.2.2', nome: 'Mercadorias em poder de terceiros' },
  { codigo: '2.2.3', nome: 'Mercadorias em armazém' },
  { codigo: '2.3', nome: 'PRODUTOS ACABADOS E INTERMÉDIOS' },
  { codigo: '2.3.1', nome: 'Produtos acabados em poder de terceiros' },
  { codigo: '2.4', nome: 'SUBPRODUTOS, DESPERDÍCIOS, RESÍDUOS E REFUGOS' },
  { codigo: '2.4.1', nome: 'Subprodutos' },
  { codigo: '2.4.2', nome: 'Desperdícios, resíduos e refugos' },
  { codigo: '2.5', nome: 'PRODUTOS OU SERVIÇOS EM CURSO' },
  { codigo: '2.6', nome: 'MATÉRIAS-PRIMAS, AUXILIARES E MATERIAIS' },
  { codigo: '2.6.1', nome: 'Matérias-primas' },
  { codigo: '2.6.2', nome: 'Matérias auxiliares' },
  { codigo: '2.6.3', nome: 'Materiais' },
  { codigo: '2.6.3.1', nome: 'Combustíveis e lubrificantes' },
  { codigo: '2.6.3.2', nome: 'Embalagens comerciais' },
  { codigo: '2.6.3.3', nome: 'Peças e sobressalentes' },
  { codigo: '2.6.3.9', nome: 'Materiais diversos' },
  { codigo: '2.6.4', nome: 'Matérias-primas, auxiliares e materiais em trânsito' },
  { codigo: '2.6.5', nome: 'Matérias-primas, auxiliares e materiais em poder de terceiros' },
  { codigo: '2.7', nome: 'ATIVOS BIOLÓGICOS' },
  { codigo: '2.7.1', nome: 'De produção' },
  { codigo: '2.7.1.1', nome: 'Animais' },
  { codigo: '2.7.1.2', nome: 'Plantas' },
  { codigo: '2.7.2', nome: 'Consumíveis' },
  { codigo: '2.7.2.1', nome: 'Animais' },
  { codigo: '2.7.2.2', nome: 'Plantas' },
  { codigo: '2.8', nome: 'REGULARIZAÇÃO DE INVENTÁRIOS' },
  { codigo: '2.8.2', nome: 'Mercadorias' },
  { codigo: '2.8.3', nome: 'Produtos acabados e intermédios' },
  { codigo: '2.8.4', nome: 'Subprodutos, desperdícios, resíduos e refugos' },
  { codigo: '2.8.5', nome: 'Produtos ou serviços em curso' },
  { codigo: '2.8.6', nome: 'Matérias-primas, auxiliares e materiais' },
  { codigo: '2.8.7', nome: 'Ativos biológicos' },
  { codigo: '2.9', nome: 'AJUSTAMENTOS PARA O VALOR REALIZÁVEL LÍQUIDO' },
  { codigo: '2.9.2', nome: 'Mercadorias' },
  { codigo: '2.9.3', nome: 'Produtos acabados e intermédios' },
  { codigo: '2.9.4', nome: 'Subprodutos, desperdícios, resíduos e refugos' },
  { codigo: '2.9.5', nome: 'Produtos ou serviços em curso' },
  { codigo: '2.9.6', nome: 'Matérias-primas, auxiliares e materiais' },
  { codigo: '2.9.7', nome: 'Ativos biológicos' },
  { codigo: '2.10', nome: 'ADIANTAMENTOS POR CONTA DE COMPRAS' },
  { codigo: '3.1', nome: 'INVESTIMENTOS FINANCEIROS' },
  { codigo: '3.1.1', nome: 'Investimentos em subsidiárias' },
  { codigo: '3.1.2', nome: 'Investimentos em associadas' },
  { codigo: '3.1.3', nome: 'Investimentos em entidades conjuntamente controladas' },
  { codigo: '3.1.4', nome: 'Outros investimentos financeiros' },
  { codigo: '3.1.5', nome: 'Investimentos detidos até à maturidade' },
  { codigo: '3.1.6', nome: 'Outros investimentos disponíveis para venda' },
  { codigo: '3.1.7', nome: 'Outros investimentos financeiros' },
  { codigo: '3.2', nome: 'ATIVOS TANGÍVEIS' },
  { codigo: '3.2.1', nome: 'Construções' },
  { codigo: '3.2.1.1', nome: 'Edifícios industriais' },
  { codigo: '3.2.1.2', nome: 'Edifícios administrativos e comerciais' },
  { codigo: '3.2.1.3', nome: 'Edifícios para habitação e outros fins sociais' },
  { codigo: '3.2.1.6', nome: 'Vias de comunicação e construções afins' },
  { codigo: '3.2.2', nome: 'Equipamento básico' },
  { codigo: '3.2.3', nome: 'Mobiliário e equipamento administrativo social' },
  { codigo: '3.2.4', nome: 'Equipamento de transporte' },
  { codigo: '3.2.5', nome: 'Taras e vasilhame' },
  { codigo: '3.2.6', nome: 'Ferramentas e utensílios' },
  { codigo: '3.2.7', nome: 'Ativos de exploração e avaliação de recursos minerais' },
  { codigo: '3.2.8', nome: 'Equipamentos biológicos' },
  { codigo: '3.2.9', nome: 'Outros ativos tangíveis' },
  { codigo: '3.3', nome: 'ATIVOS INTANGÍVEIS' },
  { codigo: '3.3.1', nome: 'Despesas de desenvolvimento' },
  { codigo: '3.3.2', nome: 'Propriedade industrial e outros direitos' },
  { codigo: '3.3.3', nome: 'Goodwill' },
  { codigo: '3.3.4', nome: 'Ativos de exploração e avaliação de recursos minerais' },
  { codigo: '3.3.5', nome: 'Programas de computador' },
  { codigo: '3.3.6', nome: 'Direito de superfície' },
  { codigo: '3.3.7', nome: 'Outros ativos intangíveis' },
  { codigo: '3.4', nome: 'INVESTIMENTOS EM CURSO' },
  { codigo: '3.4.1', nome: 'Investimentos financeiros em curso' },
  { codigo: '3.4.2', nome: 'Ativos tangíveis' },
  { codigo: '3.4.3', nome: 'Ativos intangíveis' },
  { codigo: '3.4.4', nome: 'Adiantamentos por conta de investimentos de capital' },
  { codigo: '3.5', nome: 'ATIVOS NÃO CORRENTES DETIDOS PARA VENDA' },
  { codigo: '3.6', nome: 'ATIVOS TANGÍVEIS DE INVESTIMENTO' },
  { codigo: '3.8', nome: 'AMORTIZAÇÕES ACUMULADAS' },
  { codigo: '3.8.2', nome: 'Amortizações acumuladas - Ativos tangíveis' },
  { codigo: '3.8.3', nome: 'Amortizações acumuladas - Ativos intangíveis' },
  { codigo: '3.8.6', nome: 'Amortizações acumuladas - Ativos tangíveis de investimento' },
  { codigo: '3.8.7', nome: 'Amortizações acumuladas - Ativos de exploração e avaliação de recursos minerais' },
  { codigo: '3.9', nome: 'IMPARIDADE ACUMULADA DE INVESTIMENTOS DE CAPITAL' },
  { codigo: '3.9.1', nome: 'Investimentos financeiros' },
  { codigo: '3.9.2', nome: 'Ativos tangíveis' },
  { codigo: '3.9.3', nome: 'Ativos intangíveis' },
  { codigo: '3.9.5', nome: 'Ativos não correntes detidas para venda' },
  { codigo: '3.9.6', nome: 'Ativos tangíveis de investimento' },
  { codigo: '3.9.7', nome: 'Ativos de exploração e avaliação de recursos minerais' },
  { codigo: '3.9.8', nome: 'Investimentos em curso' },
  { codigo: '4.1', nome: 'CLIENTES' },
  { codigo: '4.1.1', nome: 'Clientes c/c' },
  { codigo: '4.1.2', nome: 'Clientes - Títulos a receber' },
  { codigo: '4.1.3', nome: 'Clientes - Letras endossadas' },
  { codigo: '4.1.8', nome: 'Clientes de cobrança duvidosa' },
  { codigo: '4.1.9', nome: 'Adiantamentos de clientes' },
  { codigo: '4.2', nome: 'FORNECEDORES' },
  { codigo: '4.2.1', nome: 'Fornecedores c/c' },
  { codigo: '4.2.2', nome: 'Fornecedores - Títulos a pagar' },
  { codigo: '4.2.3', nome: 'Faturas em recepção e conferência' },
  { codigo: '4.2.5', nome: 'Faturas em recepção e conferência' },
  { codigo: '4.2.9', nome: 'Adiantamentos a fornecedores' },
  { codigo: '4.2.10', nome: 'Perdas por imparidade acumuladas' },
  { codigo: '4.3', nome: 'EMPRÉSTIMOS OBTIDOS' },
  { codigo: '4.3.1', nome: 'Empréstimos bancários' },
  { codigo: '4.3.1.1', nome: 'De curto prazo' },
  { codigo: '4.3.1.2', nome: 'De médio e longo prazo' },
  { codigo: '4.3.1.3', nome: 'Descobertos bancários' },
  { codigo: '4.3.2', nome: 'Empréstimos por obrigações' },
  { codigo: '4.3.2.1', nome: 'Convertíveis' },
  { codigo: '4.3.2.2', nome: 'Não convertíveis' },
  { codigo: '4.3.3', nome: 'Empréstimos por títulos de participação' },
  { codigo: '4.3.4', nome: 'Letras descontadas' },
  { codigo: '4.3.9', nome: 'Outros empréstimos obtidos' },
  { codigo: '4.4', nome: 'ESTADO' },
  { codigo: '4.4.1', nome: 'Imposto sobre o rendimento' },
  { codigo: '4.4.1.1', nome: 'Estimativa de imposto' },
  { codigo: '4.4.1.2', nome: 'Pagamentos por conta' },
  { codigo: '4.4.1.3', nome: 'Pagamento especial por conta' },
  { codigo: '4.4.2', nome: 'Impostos retidos na fonte' },
  { codigo: '4.4.2.1', nome: 'Rendimentos de trabalho dependente' },
  { codigo: '4.4.2.2', nome: 'Rendimentos profissionais' },
  { codigo: '4.4.2.3', nome: 'Rendimentos de capitais' },
  { codigo: '4.4.2.4', nome: 'Rendimentos prediais' },
  { codigo: '4.4.2.5', nome: 'Outros rendimentos' },
  { codigo: '4.4.3', nome: 'Imposto sobre o valor acrescentado (IVA)' },
  { codigo: '4.4.3.1', nome: 'IVA suportado' },
  { codigo: '4.4.3.1.1', nome: 'Inventários' },
  { codigo: '4.4.3.1.2', nome: 'Ativos tangíveis e intangíveis' },
  { codigo: '4.4.3.1.3', nome: 'Outros bens e serviços' },
  { codigo: '4.4.3.2', nome: 'IVA dedutível' },
  { codigo: '4.4.3.2.1', nome: 'Inventários' },
  { codigo: '4.4.3.2.2', nome: 'Ativos tangíveis e intangíveis' },
  { codigo: '4.4.3.2.3', nome: 'Outros bens e serviços' },
  { codigo: '4.4.3.3', nome: 'IVA liquidado' },
  { codigo: '4.4.3.3.1', nome: 'Operações gerais' },
  { codigo: '4.4.3.3.2', nome: 'Autoconsumos e operações gratuitas' },
  { codigo: '4.4.3.3.3', nome: 'Operações especiais' },
  { codigo: '4.4.3.4', nome: 'IVA regularizações' },
  { codigo: '4.4.3.4.1', nome: 'Mensais a favor do sujeito passivo' },
  { codigo: '4.4.3.4.2', nome: 'Mensais a favor do Estado' },
  { codigo: '4.4.3.4.3', nome: 'Anuais por cálculo do pro rata definitivo' },
  { codigo: '4.4.3.5', nome: 'IVA apuramento' },
  { codigo: '4.4.3.6', nome: 'IVA liquidações oficiosas' },
  { codigo: '4.4.3.7', nome: 'IVA a pagar' },
  { codigo: '4.4.3.7.1', nome: 'Valores apurados' },
  { codigo: '4.4.3.7.2', nome: 'Liquidações oficiosas' },
  { codigo: '4.4.3.7.3', nome: 'Diferenças entre os valores apurados e as liquidações oficiosas' },
  { codigo: '4.4.3.8', nome: 'IVA a recuperar' },
  { codigo: '4.4.3.9', nome: 'IVA reembolsos pedidos' },
  { codigo: '4.4.4', nome: 'Restantes impostos' },
  { codigo: '4.4.4.1', nome: 'Imposto de selo' },
  { codigo: '4.4.4.2', nome: 'Impostos autárquicos' },
  { codigo: '4.4.5', nome: 'Retificações de impostos, contribuições e outros tributos' },
  { codigo: '4.4.6', nome: 'Impostos diferidos' },
  { codigo: '4.4.6.1', nome: 'Ativos por impostos diferidos' },
  { codigo: '4.4.6.2', nome: 'Passivos por impostos diferidos' },
  { codigo: '4.4.9', nome: 'Contribuições para o INSS' },
  { codigo: '4.5', nome: 'OUTROS DEVEDORES' },
  { codigo: '4.5.1', nome: 'Pessoal' },
  { codigo: '4.5.1.1', nome: 'Adiantamentos aos órgãos sociais' },
  { codigo: '4.5.1.2', nome: 'Adiantamentos aos trabalhadores' },
  { codigo: '4.5.1.3', nome: 'Benefícios pós-emprego' },
  { codigo: '4.5.1.8', nome: 'Outras operações com órgãos sociais' },
  { codigo: '4.5.1.9', nome: 'Outras operações com trabalhadores' },
  { codigo: '4.5.2', nome: 'Subscritores de capital' },
  { codigo: '4.5.2.1', nome: 'Estado e outros organismos públicos' },
  { codigo: '4.5.2.2', nome: 'Entidades privadas' },
  { codigo: '4.5.2.9', nome: 'Outras entidades' },
  { codigo: '4.5.3', nome: 'Obrigacionistas' },
  { codigo: '4.5.4', nome: 'Devedores - sócios, acionistas ou proprietários' },
  { codigo: '4.5.4.1', nome: 'Empréstimos concedidos' },
  { codigo: '4.5.4.2', nome: 'Adiantamento por conta de lucros' },
  { codigo: '4.5.4.3', nome: 'Resultados atribuídos' },
  { codigo: '4.5.4.4', nome: 'Lucros disponíveis' },
  { codigo: '4.5.4.9', nome: 'Outras operações' },
  { codigo: '4.5.5', nome: 'Subsídios a receber' },
  { codigo: '4.5.5.1', nome: 'Estado e outros organismos públicos' },
  { codigo: '4.5.5.2', nome: 'Entidades privadas' },
  { codigo: '4.5.9', nome: 'Devedores diversos' },
  { codigo: '4.6', nome: 'OUTROS CREDORES' },
  { codigo: '4.6.1', nome: 'Fornecedores de investimentos de capital' },
  { codigo: '4.6.1.1', nome: 'Fornecedores de investimentos de capital c/c' },
  { codigo: '4.6.1.2', nome: 'Fornecedores de investimentos de capital - Títulos a pagar' },
  { codigo: '4.6.1.3', nome: 'Fornecedores de investimentos de capital - Adiantamentos' },
  { codigo: '4.6.1.4', nome: 'Fornecedores de investimentos de capital - Locação Financeira' },
  { codigo: '4.6.1.9', nome: 'Outras operações' },
  { codigo: '4.6.2', nome: 'Pessoal' },
  { codigo: '4.6.2.1', nome: 'Remunerações a pagar aos órgãos sociais' },
  { codigo: '4.6.2.2', nome: 'Remunerações a pagar aos trabalhadores' },
  { codigo: '4.6.2.3', nome: 'Benefícios pós-emprego' },
  { codigo: '4.6.2.8', nome: 'Outras operações com os órgãos sociais' },
  { codigo: '4.6.2.9', nome: 'Outras operações com os trabalhadores' },
  { codigo: '4.6.3', nome: 'Sindicatos' },
  { codigo: '4.6.4', nome: 'Credores por subscrições não liberadas' },
  { codigo: '4.6.5', nome: 'Obrigacionistas' },
  { codigo: '4.6.6', nome: 'Consultores, assessores e intermediários' },
  { codigo: '4.6.7', nome: 'Credores - sócios, acionistas ou proprietários' },
  { codigo: '4.6.7.1', nome: 'Empréstimos obtidos' },
  { codigo: '4.6.7.3', nome: 'Resultados atribuídos' },
  { codigo: '4.6.7.4', nome: 'Lucros disponíveis' },
  { codigo: '4.6.9', nome: 'Credores diversos' },
  { codigo: '4.7', nome: 'PERDAS POR IMPARIDADE ACUMULADAS DE CONTAS A RECEBER' },
  { codigo: '4.7.1', nome: 'Clientes' },
  { codigo: '4.7.2', nome: 'Outros devedores' },
  { codigo: '4.8', nome: 'PROVISÕES' },
  { codigo: '4.8.1', nome: 'Processos judiciais em curso' },
  { codigo: '4.8.2', nome: 'Acidentes no trabalho e doenças profissionais' },
  { codigo: '4.8.3', nome: 'Impostos' },
  { codigo: '4.8.4', nome: 'Reestruturação de negócios' },
  { codigo: '4.8.5', nome: 'Contratos onerosos' },
  { codigo: '4.8.6', nome: 'Garantias a clientes' },
  { codigo: '4.8.7', nome: 'Perdas em contratos de construção' },
  { codigo: '4.8.9', nome: 'Outras provisões' },
  { codigo: '4.9', nome: 'ACRÉSCIMOS E DIFERIMENTOS' },
  { codigo: '4.9.1', nome: 'Acréscimos de gastos' },
  { codigo: '4.9.1.1', nome: 'Juros a pagar' },
  { codigo: '4.9.1.2', nome: 'Remunerações a pagar' },
  { codigo: '4.9.1.9', nome: 'Outros acréscimos de gastos' },
  { codigo: '4.9.2', nome: 'Rendimentos diferidos' },
  { codigo: '4.9.2.1', nome: 'Prémios de emissão de obrigações' },
  { codigo: '4.9.2.2', nome: 'Prémios de emissão de títulos de participação' },
  { codigo: '4.9.2.3', nome: 'Réditos de contratos de construção' },
  { codigo: '4.9.2.4', nome: 'Subsídios para investimentos' },
  { codigo: '4.9.2.9', nome: 'Outros rendimentos diferidos' },
  { codigo: '4.9.3', nome: 'Acréscimos de rendimentos' },
  { codigo: '4.9.3.1', nome: 'Juros a receber' },
  { codigo: '4.9.3.3', nome: 'Réditos de contratos de construção' },
  { codigo: '4.9.3.9', nome: 'Outros acréscimos de rendimentos' },
  { codigo: '4.9.4', nome: 'Gastos diferidos' },
  { codigo: '4.9.4.1', nome: 'Desconto de emissão de obrigações' },
  { codigo: '4.9.4.2', nome: 'Desconto de emissão de títulos de participação' },
  { codigo: '4.9.4.3', nome: 'Benefícios pós-emprego' },
  { codigo: '4.9.4.9', nome: 'Outros gastos diferidos' },
  { codigo: '5.1', nome: 'CAPITAL' },
  { codigo: '5.2', nome: 'AÇÕES OU QUOTAS PRÓPRIAS' },
  { codigo: '5.2.1', nome: 'Valor nominal' },
  { codigo: '5.2.2', nome: 'Descontos e prémios' },
  { codigo: '5.3', nome: 'PRESTAÇÕES SUPLEMENTARES' },
  { codigo: '5.4', nome: 'PRÉMIOS DE EMISSÃO DE AÇÕES OU QUOTAS' },
  { codigo: '5.5', nome: 'RESERVAS' },
  { codigo: '5.5.1', nome: 'Reservas legais' },
  { codigo: '5.5.2', nome: 'Reservas estatutárias' },
  { codigo: '5.5.3', nome: 'Reservas livres' },
  { codigo: '5.6', nome: 'EXCEDENTES DE REVALORIZAÇÃO DE ATIVOS TANGÍVEIS E INTANGÍVEIS' },
  { codigo: '5.6.1', nome: 'Revalorizações legais' },
  { codigo: '5.6.1.1', nome: 'Antes de impostos diferidos' },
  { codigo: '5.6.1.2', nome: 'Impostos diferidos' },
  { codigo: '5.6.2', nome: 'Outros excessos' },
  { codigo: '5.6.2.1', nome: 'Antes de impostos diferidos' },
  { codigo: '5.6.2.2', nome: 'Impostos diferidos' },
  { codigo: '5.7', nome: 'AJUSTAMENTOS EM INVESTIMENTOS FINANCEIROS' },
  { codigo: '5.7.1', nome: 'Relacionados com o método da equivalência patrimonial' },
  { codigo: '5.7.1.1', nome: 'Ajustamentos de transição' },
  { codigo: '5.7.1.2', nome: 'Lucros não atribuídos' },
  { codigo: '5.7.1.3', nome: 'Decorrentes de outras variações nos capitais próprios das participadas' },
  { codigo: '5.7.2', nome: 'Lucros não atribuídos' },
  { codigo: '5.7.3', nome: 'Decorrentes de outras variações nos capitais próprios das participadas' },
  { codigo: '5.7.9', nome: 'Outros' },
  { codigo: '5.8', nome: 'OUTRAS VARIAÇÕES NO CAPITAL PRÓPRIO' },
  { codigo: '5.8.1', nome: 'Variações no justo valor de instrumentos financeiros disponíveis para venda' },
  { codigo: '5.8.2', nome: 'Ajustamentos por impostos diferidos' },
  { codigo: '5.8.9', nome: 'Outras variações' },
  { codigo: '5.9', nome: 'RESULTADOS TRANSITADOS' },
  { codigo: '6.1', nome: 'CUSTO DOS INVENTÁRIOS' },
  { codigo: '6.1.1', nome: 'Custo dos inventários vendidos ou consumidos' },
  { codigo: '6.1.1.2', nome: 'De mercadorias' },
  { codigo: '6.1.1.6', nome: 'De matérias primas, auxiliares e materiais' },
  { codigo: '6.1.1.6.1', nome: 'Matérias primas' },
  { codigo: '6.1.1.6.2', nome: 'Matérias auxiliares' },
  { codigo: '6.1.1.6.3', nome: 'Materiais' },
  { codigo: '6.1.1.7', nome: 'Ativos biológicos' },
  { codigo: '6.1.2', nome: 'Variação da produção' },
  { codigo: '6.1.2.1', nome: 'Produtos acabados e intermédios' },
  { codigo: '6.1.2.2', nome: 'Subprodutos, desperdícios, resíduos e refugos' },
  { codigo: '6.1.2.3', nome: 'Produtos e serviços em curso' },
  { codigo: '6.1.2.4', nome: 'Ativos biológicos' },
  { codigo: '6.2', nome: 'GASTOS COM O PESSOAL' },
  { codigo: '6.2.1', nome: 'Remunerações dos órgãos sociais' },
  { codigo: '6.2.2', nome: 'Remunerações dos trabalhadores' },
  { codigo: '6.2.3', nome: 'Encargos sobre remunerações' },
  { codigo: '6.2.4', nome: 'Benefícios pós-emprego' },
  { codigo: '6.2.5', nome: 'Ajudas de custo' },
  { codigo: '6.2.5.1', nome: 'Ajudas de custo tributáveis' },
  { codigo: '6.2.5.2', nome: 'Ajudas de custo não tributáveis' },
  { codigo: '6.2.6', nome: 'Indemnizações' },
  { codigo: '6.2.6.1', nome: 'Indemnizações - Risco segurável' },
  { codigo: '6.2.6.2', nome: 'Indemnizações - Outras' },
  { codigo: '6.2.7', nome: 'Seguros de acidentes no trabalho e doenças profissionais' },
  { codigo: '6.2.8', nome: 'Gastos de ação social' },
  { codigo: '6.2.9', nome: 'Outros gastos com pessoal' },
  { codigo: '6.3', nome: 'FORNECIMENTOS E SERVIÇOS DE TERCEIROS' },
  { codigo: '6.3.1', nome: 'Subcontratos' },
  { codigo: '6.3.2', nome: 'Fornecimentos e serviços' },
  { codigo: '6.3.2.1.1', nome: 'Água' },
  { codigo: '6.3.2.1.2', nome: 'Eletricidade' },
  { codigo: '6.3.2.1.3', nome: 'Combustíveis' },
  { codigo: '6.3.2.1.3.1', nome: 'Gasóleo' },
  { codigo: '6.3.2.1.3.1.1', nome: 'Gasóleo - Viaturas ligeiras de passageiros' },
  { codigo: '6.3.2.1.3.1.2', nome: 'Gasóleo - Outros' },
  { codigo: '6.3.2.1.3.2', nome: 'Restantes combustíveis' },
  { codigo: '6.3.2.1.3.2.1', nome: 'Restantes combustíveis - Viaturas ligeiras de passageiros' },
  { codigo: '6.3.2.1.3.2.2', nome: 'Restantes combustíveis - Outros' },
  { codigo: '6.3.2.1.3.3', nome: 'Lubrificantes' },
  { codigo: '6.3.2.1.3.3.1', nome: 'Lubrificantes - Viaturas ligeiras de passageiros' },
  { codigo: '6.3.2.1.3.3.2', nome: 'Lubrificantes - Outros' },
  { codigo: '6.3.2.1.4', nome: 'Ferramentas e utensílios de desgaste rápido' },
  { codigo: '6.3.2.1.5', nome: 'Material de manutenção e reparação' },
  { codigo: '6.3.2.1.5.1', nome: 'Material de manutenção e reparação - Viaturas ligeiras e passageiros' },
  { codigo: '6.3.2.1.5.2', nome: 'Material de manutenção e reparação - Outros' },
  { codigo: '6.3.2.1.6', nome: 'Material de escritório' },
  { codigo: '6.3.2.1.7', nome: 'Livros e documentação técnica' },
  { codigo: '6.3.2.1.8', nome: 'Artigos para oferta' },
  { codigo: '6.3.2.2.1', nome: 'Manutenção e reparação' },
  { codigo: '6.3.2.2.2', nome: 'Transportes de carga' },
  { codigo: '6.3.2.2.3', nome: 'Transportes de pessoal' },
  { codigo: '6.3.2.2.4', nome: 'Comunicações' },
  { codigo: '6.3.2.2.5', nome: 'Honorários' },
  { codigo: '6.3.2.2.6', nome: 'Comissões a intermediários' },
  { codigo: '6.3.2.2.7', nome: 'Publicidade e propaganda' },
  { codigo: '6.3.2.2.7.1', nome: 'Publicidade e propaganda - Campanhas publicitárias' },
  { codigo: '6.3.2.2.7.2', nome: 'Publicidade e propaganda - Outros' },
  { codigo: '6.3.2.2.8', nome: 'Deslocações e estadias' },
  { codigo: '6.3.2.2.8.1', nome: 'Deslocações e estadias - Em serviço' },
  { codigo: '6.3.2.2.8.2', nome: 'Deslocações e estadias - Outras deslocações' },
  { codigo: '6.3.2.2.9', nome: 'Despesas de representação' },
  { codigo: '6.3.2.3.1', nome: 'Contencioso e notariado' },
  { codigo: '6.3.2.3.2', nome: 'Rendas e alugueres' },
  { codigo: '6.3.2.3.2.1', nome: 'Rendas e alugueres - Locação financeira' },
  { codigo: '6.3.2.3.3', nome: 'Seguros' },
  { codigo: '6.3.2.3.3.1', nome: 'Seguro de vida, acidentes pessoais e doença' },
  { codigo: '6.3.2.3.4', nome: 'Royalties' },
  { codigo: '6.3.2.3.5', nome: 'Limpeza, higiene e conforto' },
  { codigo: '6.3.2.3.6', nome: 'Vigilância e segurança' },
  { codigo: '6.3.2.3.7', nome: 'Trabalhos especializados' },
  { codigo: '6.3.2.9.9', nome: 'Outros fornecimentos e serviços' },
  { codigo: '6.4', nome: 'PERDAS POR IMPARIDADE DO PERÍODO' },
  { codigo: '6.4.1', nome: 'Ajustamentos de inventários para o valor realizável líquido' },
  { codigo: '6.4.2', nome: 'Investimentos financeiros' },
  { codigo: '6.4.3', nome: 'Ativos tangíveis' },
  { codigo: '6.4.4', nome: 'Ativos intangíveis' },
  { codigo: '6.4.5', nome: 'Ativos não-correntes detidos para venda' },
  { codigo: '6.4.6', nome: 'Ativo tangíveis de investimento' },
  { codigo: '6.4.7', nome: 'Ativos de exploração e avaliação de recursos minerais' },
  { codigo: '6.4.8', nome: 'Contas a receber' },
  { codigo: '6.4.8.1', nome: 'Contas a receber - ajustamentos dentro dos limites fiscais' },
  { codigo: '6.4.8.2', nome: 'Contas a receber - ajustamentos para além dos limites fiscais' },
  { codigo: '6.4.9', nome: 'Investimentos em curso' },
  { codigo: '6.5', nome: 'AMORTIZAÇÕES DO PERÍODO' },
  { codigo: '6.5.1', nome: 'Ativos tangíveis' },
  { codigo: '6.5.2', nome: 'Ativos intangíveis' },
  { codigo: '6.5.3', nome: 'Ativos tangíveis de investimento' },
  { codigo: '6.5.4', nome: 'Ativos de exploração e avaliação de recursos minerais' },
  { codigo: '6.6', nome: 'PROVISÕES DO PERÍODO' },
  { codigo: '6.6.1', nome: 'Processos judiciais em curso' },
  { codigo: '6.6.2', nome: 'Acidentes no trabalho e doenças profissionais' },
  { codigo: '6.6.3', nome: 'Impostos' },
  { codigo: '6.6.4', nome: 'Reestruturação de negócios' },
  { codigo: '6.6.5', nome: 'Contratos onerosos' },
  { codigo: '6.6.6', nome: 'Garantias a clientes' },
  { codigo: '6.6.7', nome: 'Perdas em contratos de construção' },
  { codigo: '6.6.9', nome: 'Outras provisões' },
  { codigo: '6.7', nome: 'PERDAS POR REDUÇÕES DO JUSTO VALOR' },
  { codigo: '6.7.1', nome: 'Instrumentos financeiros' },
  { codigo: '6.7.2', nome: 'Ativos tangíveis de investimento' },
  { codigo: '6.7.3', nome: 'Ativos biológicos' },
  { codigo: '6.8', nome: 'OUTROS GASTOS E PERDAS OPERACIONAIS' },
  { codigo: '6.8.1', nome: 'Despesas de investigação e desenvolvimento' },
  { codigo: '6.8.2', nome: 'Impostos e taxas' },
  { codigo: '6.8.2.1', nome: 'Direitos aduaneiros' },
  { codigo: '6.8.2.2', nome: 'Imposto sobre o Valor Acrescentado' },
  { codigo: '6.8.2.3', nome: 'Imposto de selo' },
  { codigo: '6.8.2.4', nome: 'Impostos sobre veículos' },
  { codigo: '6.8.2.5', nome: 'Impostos autárquicos' },
  { codigo: '6.8.3', nome: 'Perdas em Investimentos de capital' },
  { codigo: '6.8.3.1', nome: 'Alienação' },
  { codigo: '6.8.3.2', nome: 'Abates' },
  { codigo: '6.8.3.3', nome: 'Sinistros' },
  { codigo: '6.8.4', nome: 'Perdas em inventários e ativos biológicos' },
  { codigo: '6.8.4.1', nome: 'Sinistros' },
  { codigo: '6.8.4.2', nome: 'Quebras' },
  { codigo: '6.8.4.9', nome: 'Outras' },
  { codigo: '6.8.9', nome: 'Outros gastos operacionais' },
  { codigo: '6.8.9.1', nome: 'Quotizações' },
  { codigo: '6.8.9.2', nome: 'Despesas confidenciais' },
  { codigo: '6.8.9.3', nome: 'Ofertas e amostras de inventários' },
  { codigo: '6.8.9.4', nome: 'Programas de responsabilidade social' },
  { codigo: '6.8.9.5', nome: 'Donativos' },
  { codigo: '6.8.9.5.1', nome: 'Donativos ao Estado' },
  { codigo: '6.8.9.5.2', nome: 'Outros donativos no âmbito do Mecenato' },
  { codigo: '6.8.9.6', nome: 'Multas e penalidades' },
  { codigo: '6.8.9.7', nome: 'Insuficiência da estimativa para impostos' },
  { codigo: '6.8.9.9', nome: 'Outros' },
  { codigo: '6.9', nome: 'GASTOS E PERDAS FINANCEIROS' },
  { codigo: '6.9.1', nome: 'Juros suportados' },
  { codigo: '6.9.1.1', nome: 'Empréstimos bancários' },
  { codigo: '6.9.1.2', nome: 'Empréstimos obrigacionistas e títulos de participação' },
  { codigo: '6.9.1.3', nome: 'Empréstimos de sócios, acionistas ou proprietários' },
  { codigo: '6.9.1.4', nome: 'Outros empréstimos' },
  { codigo: '6.9.1.5', nome: 'Desconto de títulos' },
  { codigo: '6.9.1.6', nome: 'Juros de mora e compensatórios' },
  { codigo: '6.9.1.6.1', nome: 'Juros de mora' },
  { codigo: '6.9.1.6.2', nome: 'Juros compensatórios' },
  { codigo: '6.9.1.9', nome: 'Outros juros' },
  { codigo: '6.9.1.9.1', nome: 'Operações de factoring' },
  { codigo: '6.9.4', nome: 'Diferenças de câmbio desfavoráveis' },
  { codigo: '6.9.4.1', nome: 'Realizadas' },
  { codigo: '6.9.4.2', nome: 'Não realizadas' },
  { codigo: '6.9.5', nome: 'Descontos de pronto pagamento concedidos' },
  { codigo: '6.9.8', nome: 'Outros gastos e perdas financeiros' },
  { codigo: '6.9.8.1', nome: 'Serviços bancários' },
  { codigo: '6.9.8.9', nome: 'Diversos não especificados' },
  { codigo: '7.1', nome: 'VENDAS' },
  { codigo: '7.1.1', nome: 'Mercadorias' },
  { codigo: '7.1.2', nome: 'Produtos acabados e intermédios' },
  { codigo: '7.1.3', nome: 'Subprodutos, desperdícios, resíduos e refugos' },
  { codigo: '7.1.4', nome: 'Ativos biológicos' },
  { codigo: '7.1.5', nome: 'IVA das vendas com imposto incluído' },
  { codigo: '7.1.6', nome: 'Devolução de vendas' },
  { codigo: '7.1.7', nome: 'Descontos e abatimentos' },
  { codigo: '7.2', nome: 'PRESTAÇÃO DE SERVIÇOS' },
  { codigo: '7.2.1', nome: 'IVA da prestação de serviços com imposto incluído' },
  { codigo: '7.2.2', nome: 'Serviços prestados' },
  { codigo: '7.2.6', nome: 'Descontos e abatimentos' },
  { codigo: '7.3', nome: 'INVESTIMENTOS REALIZADOS PELA PRÓPRIA EMPRESA' },
  { codigo: '7.3.1', nome: 'Investimentos financeiros' },
  { codigo: '7.3.2', nome: 'Ativos tangíveis' },
  { codigo: '7.3.3', nome: 'Ativos intangíveis' },
  { codigo: '7.3.4', nome: 'Investimentos em curso' },
  { codigo: '7.4', nome: 'REVERSÕES DO PERÍODO' },
  { codigo: '7.4.1', nome: 'De perdas por imparidade' },
  { codigo: '7.4.1.1', nome: 'Ajustamentos de inventários para o valor realizável líquido' },
  { codigo: '7.4.1.2', nome: 'Investimentos financeiros' },
  { codigo: '7.4.1.3', nome: 'Ativos tangíveis' },
  { codigo: '7.4.1.4', nome: 'Ativos intangíveis' },
  { codigo: '7.4.1.5', nome: 'Ativos não-correntes detidos para venda' },
  { codigo: '7.4.1.6', nome: 'Ativos tangíveis de investimento' },
  { codigo: '7.4.1.7', nome: 'Ativos de exploração e avaliação de recursos minerais' },
  { codigo: '7.4.1.8', nome: 'Contas a receber' },
  { codigo: '7.4.1.9', nome: 'Investimentos em curso' },
  { codigo: '7.4.2', nome: 'De amortizações' },
  { codigo: '7.4.2.1', nome: 'Ativos tangíveis' },
  { codigo: '7.4.2.2', nome: 'Ativos intangíveis' },
  { codigo: '7.4.2.3', nome: 'Ativos tangíveis de investimento' },
  { codigo: '7.4.3', nome: 'De provisões' },
  { codigo: '7.4.3.1', nome: 'Processos judiciais em curso' },
  { codigo: '7.4.3.2', nome: 'Acidentes no trabalho e doenças profissionais' },
  { codigo: '7.4.3.3', nome: 'Impostos' },
  { codigo: '7.4.3.4', nome: 'Reestruturação de negócios' },
  { codigo: '7.4.3.5', nome: 'Contratos onerosos' },
  { codigo: '7.4.3.6', nome: 'Garantias a clientes' },
  { codigo: '7.4.3.7', nome: 'Perdas em contratos de construção' },
  { codigo: '7.4.3.9', nome: 'Outras provisões' },
  { codigo: '7.5', nome: 'RENDIMENTOS SUPLEMENTARES' },
  { codigo: '7.5.1', nome: 'Serviços sociais' },
  { codigo: '7.5.2', nome: 'Aluguer de equipamento' },
  { codigo: '7.5.3', nome: 'Venda de energia' },
  { codigo: '7.5.4', nome: 'Estudos, projetos pesquisas e investigações' },
  { codigo: '7.5.5', nome: 'Assistência técnica' },
  { codigo: '7.5.6', nome: 'Royalties' },
  { codigo: '7.5.7', nome: 'Cargos sociais exercidos noutras empresas' },
  { codigo: '7.5.9', nome: 'Outros rendimentos suplementares inerentes ao valor acrescentado' },
  { codigo: '7.6', nome: 'OUTROS RENDIMENTOS E GANHOS OPERACIONAIS' },
  { codigo: '7.6.1', nome: 'Subsídios para investimentos' },
  { codigo: '7.6.1.1', nome: 'Do Estado e outros organismos públicos' },
  { codigo: '7.6.1.9', nome: 'De outras entidades' },
  { codigo: '7.6.2', nome: 'Subsídios à exploração' },
  { codigo: '7.6.2.1', nome: 'Do Estado e outras entidades públicas' },
  { codigo: '7.6.2.9', nome: 'De outras entidades' },
  { codigo: '7.6.3', nome: 'Ganhos em investimentos de capital' },
  { codigo: '7.6.3.1', nome: 'Alienação' },
  { codigo: '7.6.3.2', nome: 'Sinistros' },
  { codigo: '7.6.4', nome: 'Ganhos em inventários e ativos biológicos' },
  { codigo: '7.6.4.1', nome: 'Sinistros' },
  { codigo: '7.6.4.2', nome: 'Sobras' },
  { codigo: '7.6.4.9', nome: 'Outros' },
  { codigo: '7.6.9', nome: 'Outros rendimentos alheios ao valor acrescentado' },
  { codigo: '7.6.9.1', nome: 'Restituição de impostos' },
  { codigo: '7.6.9.2', nome: 'Benefícios de penalidades contratuais' },
  { codigo: '7.6.9.3', nome: 'Excesso de estimativa para impostos' },
  { codigo: '7.6.9.9', nome: 'Outros' },
  { codigo: '7.8', nome: 'RENDIMENTOS E GANHOS FINANCEIROS' },
  { codigo: '7.8.1', nome: 'Juros obtidos' },
  { codigo: '7.8.1.1', nome: 'Depósitos bancários' },
  { codigo: '7.8.1.2', nome: 'Empréstimos' },
  { codigo: '7.8.1.3', nome: 'Obrigações e títulos de participação' },
  { codigo: '7.8.1.4', nome: 'Outras aplicações de tesouraria' },
  { codigo: '7.8.1.9', nome: 'Outros juros' },
  { codigo: '7.8.2', nome: 'Rendimentos de ativos tangíveis de investimento' },
  { codigo: '7.8.3', nome: 'Rendimentos de investimentos financeiros' },
  { codigo: '7.8.4', nome: 'Diferenças de câmbio favoráveis' },
  { codigo: '7.8.4.1', nome: 'Realizadas' },
  { codigo: '7.8.4.2', nome: 'Não realizadas' },
  { codigo: '7.8.5', nome: 'Descontos de pronto pagamento obtidos' },
  { codigo: '7.8.9', nome: 'Outros rendimentos e ganhos financeiros' },
  { codigo: '7.9', nome: 'GANHOS POR AUMENTO DO JUSTO VALOR' },
  { codigo: '7.9.1', nome: 'Instrumentos financeiros' },
  { codigo: '7.9.2', nome: 'Ativos tangíveis de investimento' },
  { codigo: '7.9.3', nome: 'Ativos biológicos' },
  { codigo: '8.1', nome: 'RESULTADOS OPERACIONAIS' },
  { codigo: '8.2', nome: 'RESULTADOS FINANCEIROS' },
  { codigo: '8.3', nome: 'RESULTADOS CORRENTES' },
  { codigo: '8.5', nome: 'IMPOSTO SOBRE O RENDIMENTO' },
  { codigo: '8.5.1', nome: 'Imposto corrente' },
  { codigo: '8.5.2', nome: 'Imposto diferido' },
  { codigo: '8.8', nome: 'RESULTADO LÍQUIDO DO PERÍODO' },
  { codigo: '8.9', nome: 'DIVIDENDOS ANTECIPADOS' },
];

export const PLANO_CONTAS_BASE: Omit<ContaContabil, 'id' | 'empresaId' | 'dataCriacao'>[] =
  contasBaseInput.map((conta) => {
    const nivel = calcularNivelConta(conta.codigo);
    const temFilhos = contasBaseInput.some((possivelFilho) =>
      possivelFilho.codigo !== conta.codigo && possivelFilho.codigo.startsWith(`${conta.codigo}.`)
    );

    const tipo = conta.tipo ?? inferirTipoContaBase(conta.codigo);

    return {
      codigo: conta.codigo,
      nome: conta.nome,
      classe: calcularClasseConta(conta.codigo),
      tipo,
      nivel,
      aceitaLancamentos: conta.aceitaLancamentos ?? !temFilhos,
      grupoRelatorio: conta.grupoRelatorio ?? inferirGrupoRelatorioContaBase(conta.codigo, tipo),
      isSystem: true,
      ativo: true,
    };
  });

// ========================================
// FUNÇÕES DE PLANO DE CONTAS
// ========================================

const apagarChavesEmLotes = async (keys: string[]) => {
  const tamanhoLote = 100;

  for (let i = 0; i < keys.length; i += tamanhoLote) {
    await kv.mdel(keys.slice(i, i + tamanhoLote));
  }
};

const atualizarReferenciasLancamentos = async (
  empresaId: string,
  contasPorCodigo: Map<string, ContaContabil>,
  codigoPorIdAntigo: Map<string, string>
) => {
  const lancamentos = await kv.getByPrefixWithKeys<LancamentoContabil>(`lancamento_${empresaId}_`);
  let lancamentosAtualizados = 0;
  let linhasAtualizadas = 0;

  for (const { key, value: lancamento } of lancamentos) {
    let alterado = false;
    const linhas = lancamento.linhas.map((linha) => {
      const codigoConta = linha.contaCodigo || codigoPorIdAntigo.get(linha.contaId);
      const novaConta = codigoConta ? contasPorCodigo.get(codigoConta) : undefined;

      if (!novaConta) return linha;

      if (
        linha.contaId === novaConta.id &&
        linha.contaCodigo === novaConta.codigo &&
        linha.contaNome === novaConta.nome
      ) {
        return linha;
      }

      alterado = true;
      linhasAtualizadas++;
      return {
        ...linha,
        contaId: novaConta.id,
        contaCodigo: novaConta.codigo,
        contaNome: novaConta.nome,
      };
    });

    if (alterado) {
      await kv.set(key, { ...lancamento, linhas });
      lancamentosAtualizados++;
    }
  }

  return { lancamentosAtualizados, linhasAtualizadas };
};

export async function inicializarPlanoContas(empresaId: string) {
  console.log(`[CONTABILIDADE] Substituindo plano de contas para empresa: ${empresaId}`);
  
  try {
    const contasExistentes = await kv.getByPrefixWithKeys<ContaContabil>(`conta_${empresaId}_`);
    const codigoPorIdAntigo = new Map<string, string>(
      contasExistentes.map(({ value }) => [value.id, value.codigo])
    );
    const codigosBase = new Set(PLANO_CONTAS_BASE.map((conta) => conta.codigo));
    const codigoPertenceAoPlanoNovo = (codigo: string) =>
      codigosBase.has(codigo) || PLANO_CONTAS_BASE.some((conta) => codigo.startsWith(`${conta.codigo}.`));
    const contasPersonalizadas = contasExistentes
      .map(({ value }) => value)
      .filter((conta) => !conta.isSystem && !codigosBase.has(conta.codigo) && codigoPertenceAoPlanoNovo(conta.codigo));
    const idsContasPersonalizadas = new Set(contasPersonalizadas.map((conta) => conta.id));
    const contasPorCodigo = new Map<string, ContaContabil>();
    const contasCriadas: ContaContabil[] = [];
    const chavesParaApagar = contasExistentes
      .filter(({ value }) => !idsContasPersonalizadas.has(value.id))
      .map(({ key }) => key);

    if (chavesParaApagar.length > 0) {
      await apagarChavesEmLotes(chavesParaApagar);
    }
    
    for (const contaBase of [...PLANO_CONTAS_BASE].sort((a, b) => compararCodigosContas(a.codigo, b.codigo))) {
      const codigoPai = obterCodigoPaiExistente(contaBase.codigo, contasPorCodigo);
      const contaPai = codigoPai ? contasPorCodigo.get(codigoPai) : undefined;
      const conta: ContaContabil = {
        id: `conta_${empresaId}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
        empresaId,
        ...contaBase,
        parentId: contaPai?.id,
        dataCriacao: new Date().toISOString(),
      };
      
      await kv.set(conta.id, conta);
      contasPorCodigo.set(conta.codigo, conta);
      contasCriadas.push(conta);
    }

    const contasPersonalizadasMantidas: ContaContabil[] = [];
    for (const contaPersonalizada of contasPersonalizadas.sort((a, b) => compararCodigosContas(a.codigo, b.codigo))) {
      const codigoPai = obterCodigoPaiExistente(contaPersonalizada.codigo, contasPorCodigo);
      const contaPai = codigoPai ? contasPorCodigo.get(codigoPai) : undefined;
      const classe = calcularClasseConta(contaPersonalizada.codigo);
      const tipo = contaPersonalizada.tipo || contaPai?.tipo || inferirTipoContaBase(contaPersonalizada.codigo);
      const contaAtualizada: ContaContabil = {
        ...contaPersonalizada,
        empresaId,
        classe,
        tipo,
        parentId: contaPai?.id,
        nivel: calcularNivelConta(contaPersonalizada.codigo),
        grupoRelatorio:
          contaPersonalizada.grupoRelatorio ||
          inferirGrupoRelatorioContaBase(contaPersonalizada.codigo, tipo),
        isSystem: false,
        ativo: contaPersonalizada.ativo ?? true,
      };

      await kv.set(contaAtualizada.id, contaAtualizada);
      contasPorCodigo.set(contaAtualizada.codigo, contaAtualizada);
      contasPersonalizadasMantidas.push(contaAtualizada);
    }
    
    const planoCompleto = Array.from(contasPorCodigo.values()).sort((a, b) =>
      compararCodigosContas(a.codigo, b.codigo)
    );
    const referenciasLancamentos = await atualizarReferenciasLancamentos(
      empresaId,
      contasPorCodigo,
      codigoPorIdAntigo
    );

    console.log(`[CONTABILIDADE] Plano substituido: ${planoCompleto.length} contas (${contasCriadas.length} base, ${contasPersonalizadasMantidas.length} personalizadas)`);
    return {
      success: true,
      data: planoCompleto,
      count: planoCompleto.length,
      created: contasCriadas.length,
      replaced: chavesParaApagar.length,
      customPreserved: contasPersonalizadasMantidas.length,
      lancamentosAtualizados: referenciasLancamentos.lancamentosAtualizados,
      linhasLancamentoAtualizadas: referenciasLancamentos.linhasAtualizadas,
      message: `Plano de contas substituido pelo PGC-NIRF com ${contasCriadas.length} contas base`,
    };
  } catch (error: any) {
    console.error('[CONTABILIDADE] ❌ Erro ao inicializar plano de contas:', error);
    return { success: false, error: error.message };
  }
}

export async function obterContas(empresaId: string) {
  try {
    const contas = (await kv.getByPrefix(`conta_${empresaId}_`))
      .filter((conta: ContaContabil) => conta.ativo !== false);
    
    // Ordenar por código
    contas.sort((a: ContaContabil, b: ContaContabil) => compararCodigosContas(a.codigo, b.codigo));
    
    return { success: true, data: contas };
  } catch (error: any) {
    return { success: false, error: error.message };
  }
}

function normalizarTexto(valor: unknown): string {
  return String(valor || '').trim();
}

function normalizarDataISO(valor: unknown): string {
  return normalizarTexto(valor).slice(0, 10);
}

function normalizarNumero(valor: unknown, fallback = 0): number {
  if (typeof valor === 'number') {
    return Number.isFinite(valor) ? valor : fallback;
  }

  const original = String(valor ?? '').trim();
  if (!original) return fallback;

  const negativo = original.startsWith('-') || /^\(.+\)$/.test(original);
  const limpo = original.replace(/\s+/g, '').replace(/[^\d.,]/g, '');
  if (!/\d/.test(limpo)) return fallback;

  const ultimaVirgula = limpo.lastIndexOf(',');
  const ultimoPonto = limpo.lastIndexOf('.');
  let separadorDecimal = '';

  if (ultimaVirgula >= 0 && ultimoPonto >= 0) {
    separadorDecimal = ultimaVirgula > ultimoPonto ? ',' : '.';
  } else if (ultimaVirgula >= 0) {
    const casas = limpo.length - ultimaVirgula - 1;
    separadorDecimal = casas > 0 && casas <= 2 ? ',' : '';
  } else if (ultimoPonto >= 0) {
    const casas = limpo.length - ultimoPonto - 1;
    separadorDecimal = casas > 0 && casas <= 2 ? '.' : '';
  }

  const normalizado = separadorDecimal
    ? (() => {
        const posicao = limpo.lastIndexOf(separadorDecimal);
        const inteiro = limpo.slice(0, posicao).replace(/[.,]/g, '') || '0';
        const decimal = limpo.slice(posicao + 1).replace(/[.,]/g, '');
        return `${inteiro}.${decimal}`;
      })()
    : limpo.replace(/[.,]/g, '');

  const numero = Number(`${negativo ? '-' : ''}${normalizado}`);
  return Number.isFinite(numero) ? numero : fallback;
}

const arredondarCentimos = (valor: number) =>
  Math.round((valor + Number.EPSILON) * 100) / 100;

function calcularDiferencaConferencia(saldoExtracto: number, saldoReconciliado: number) {
  const diferencaDirecta = arredondarCentimos(saldoExtracto - saldoReconciliado);
  const sinalOposto =
    Math.abs(saldoExtracto) > 0 &&
    Math.abs(saldoReconciliado) > 0 &&
    Math.abs(Math.abs(saldoExtracto) - Math.abs(saldoReconciliado)) < 0.01 &&
    Math.abs(diferencaDirecta) >= 0.01;
  const diferenca = sinalOposto
    ? arredondarCentimos(Math.abs(saldoExtracto) - Math.abs(saldoReconciliado))
    : diferencaDirecta;

  return { diferenca, sinalOposto };
}

function normalizarMetodoAmortizacao(valor: unknown): ActivoFixo['metodoAmortizacao'] {
  const metodo = normalizarTexto(valor) as ActivoFixo['metodoAmortizacao'];
  return metodo === 'decrescente' || metodo === 'unidades_producao' ? metodo : 'linear';
}

function normalizarEstadoActivo(valor: unknown): ActivoFixo['estado'] {
  const estado = normalizarTexto(valor) as ActivoFixo['estado'];
  return estado === 'em_curso' || estado === 'baixado' || estado === 'alienado' || estado === 'sinistro'
    ? estado
    : 'activo';
}

async function validarContaActivo(empresaId: string, contaId?: string, contaCodigo?: string) {
  const contas = await kv.getByPrefix<ContaContabil>(`conta_${empresaId}_`);
  return contas.find((conta) =>
    conta.ativo !== false &&
    ((contaId && conta.id === contaId) || (contaCodigo && conta.codigo === contaCodigo))
  );
}

function codigoContaAmortizacaoPadrao(codigoContaActivo: string) {
  if (codigoContaActivo.startsWith('3.3')) return '3.8.3';
  if (codigoContaActivo.startsWith('3.6')) return '3.8.6';
  if (codigoContaActivo.startsWith('3.2.7')) return '3.8.7';
  if (codigoContaActivo.startsWith('3.2')) return '3.8.2';
  return '';
}

function contaEhAmortizacaoAcumulada(conta?: ContaContabil) {
  return Boolean(conta && conta.ativo !== false && conta.aceitaLancamentos !== false && conta.codigo.startsWith('3.8'));
}

async function obterContaAmortizacaoPadrao(empresaId: string, contaActivo?: ContaContabil) {
  if (!contaActivo) return undefined;

  const codigoPadrao = codigoContaAmortizacaoPadrao(contaActivo.codigo);
  if (!codigoPadrao) return undefined;

  const contas = await kv.getByPrefix<ContaContabil>(`conta_${empresaId}_`);
  return (
    contas.find((conta) => contaEhAmortizacaoAcumulada(conta) && conta.codigo === codigoPadrao) ||
    contas.find((conta) => contaEhAmortizacaoAcumulada(conta) && conta.codigo.startsWith(`${codigoPadrao}.`))
  );
}

async function gerarCodigoActivoFixo(empresaId: string, dataReferencia?: string) {
  const ano = normalizarDataISO(dataReferencia).slice(0, 4) || String(new Date().getFullYear());
  const prefixo = `AF-${ano}-`;
  const activos = await kv.getByPrefix<ActivoFixo>(`activo_fixo_${empresaId}_`);
  const ultimoNumero = activos.reduce((maior, activo) => {
    const match = normalizarTexto(activo.codigo).toUpperCase().match(new RegExp(`^${prefixo}(\\d+)$`));
    return match ? Math.max(maior, Number(match[1]) || 0) : maior;
  }, 0);

  return `${prefixo}${String(ultimoNumero + 1).padStart(4, '0')}`;
}

async function normalizarActivoFixo(
  empresaId: string,
  dados: Partial<ActivoFixo>,
  existente?: ActivoFixo,
): Promise<ActivoFixo | { error: string }> {
  const descricao = normalizarTexto(dados.descricao ?? existente?.descricao);
  const dataAquisicao = normalizarDataISO(dados.dataAquisicao ?? existente?.dataAquisicao);
  let codigo = normalizarTexto(dados.codigo ?? existente?.codigo).toUpperCase();
  if (!codigo && !existente) {
    codigo = await gerarCodigoActivoFixo(empresaId, dataAquisicao);
  }
  const dataEntradaServico = normalizarDataISO(dados.dataEntradaServico ?? existente?.dataEntradaServico) || dataAquisicao;
  const conta = await validarContaActivo(empresaId, dados.contaId ?? existente?.contaId, dados.contaCodigo ?? existente?.contaCodigo);
  const contaAmortizacao = dados.contaAmortizacaoId || dados.contaAmortizacaoCodigo
    ? await validarContaActivo(empresaId, dados.contaAmortizacaoId ?? existente?.contaAmortizacaoId, dados.contaAmortizacaoCodigo ?? existente?.contaAmortizacaoCodigo)
    : await obterContaAmortizacaoPadrao(empresaId, conta);
  const custoAquisicao = normalizarNumero(dados.custoAquisicao ?? existente?.custoAquisicao);
  const valorResidual = Math.max(0, normalizarNumero(dados.valorResidual ?? existente?.valorResidual));
  const vidaUtilInformada = Math.max(0, normalizarNumero(dados.vidaUtil ?? existente?.vidaUtil));
  const vidaUtil = vidaUtilInformada > 0 ? vidaUtilInformada : 5;
  const taxaInformada = normalizarNumero(dados.taxaAmortizacao ?? existente?.taxaAmortizacao);
  const taxaAmortizacao = taxaInformada > 0 ? taxaInformada : vidaUtil > 0 ? Number((100 / vidaUtil).toFixed(4)) : 0;
  const metodoAmortizacao = normalizarMetodoAmortizacao(dados.metodoAmortizacao ?? existente?.metodoAmortizacao);
  const unidadesProducaoEstimadas = normalizarNumero(dados.unidadesProducaoEstimadas ?? existente?.unidadesProducaoEstimadas);
  const estado = normalizarEstadoActivo(dados.estado ?? existente?.estado);
  const dataBaixa = normalizarDataISO(dados.dataBaixa ?? existente?.dataBaixa);

  if (!codigo) return { error: 'Codigo do activo e obrigatorio' };
  if (!descricao) return { error: 'Descricao do activo e obrigatoria' };
  if (!dataAquisicao) return { error: 'Data de aquisicao e obrigatoria' };
  if (!conta) return { error: 'Conta contabilistica do activo e obrigatoria' };
  if ((dados.contaAmortizacaoId || dados.contaAmortizacaoCodigo) && !contaEhAmortizacaoAcumulada(contaAmortizacao)) {
    return { error: 'Conta de amortizacao acumulada invalida' };
  }
  if (custoAquisicao <= 0) return { error: 'Custo de aquisicao deve ser maior que zero' };
  if (valorResidual > custoAquisicao) return { error: 'Valor residual nao pode ser maior que o custo de aquisicao' };
  if (metodoAmortizacao === 'unidades_producao' && unidadesProducaoEstimadas <= 0) {
    return { error: 'Unidades estimadas sao obrigatorias para amortizacao por unidades de producao' };
  }
  if ((estado === 'baixado' || estado === 'alienado' || estado === 'sinistro') && !dataBaixa) {
    return { error: 'Data de baixa e obrigatoria para activos baixados, alienados ou sinistrados' };
  }

  const agora = new Date().toISOString();
  return {
    id: existente?.id || `activo_fixo_${empresaId}_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`,
    empresaId,
    codigo,
    descricao,
    referenciaInterna: normalizarTexto(dados.referenciaInterna ?? existente?.referenciaInterna) || undefined,
    numeroSerie: normalizarTexto(dados.numeroSerie ?? existente?.numeroSerie) || undefined,
    dataAquisicao,
    dataEntradaServico,
    contaId: conta.id,
    contaCodigo: conta.codigo,
    contaNome: conta.nome,
    contaAmortizacaoId: contaAmortizacao?.id,
    contaAmortizacaoCodigo: contaAmortizacao?.codigo,
    contaAmortizacaoNome: contaAmortizacao?.nome,
    custoAquisicao,
    valorResidual,
    vidaUtil,
    taxaAmortizacao,
    metodoAmortizacao,
    unidadesProducaoEstimadas,
    unidadesProducaoPeriodo: normalizarNumero(dados.unidadesProducaoPeriodo ?? existente?.unidadesProducaoPeriodo),
    unidadesProducaoAcumuladas: normalizarNumero(dados.unidadesProducaoAcumuladas ?? existente?.unidadesProducaoAcumuladas),
    estado,
    dataBaixa: dataBaixa || undefined,
    valorBaixa: normalizarNumero(dados.valorBaixa ?? existente?.valorBaixa),
    reavaliacoes: normalizarNumero(dados.reavaliacoes ?? existente?.reavaliacoes),
    outrasAlteracoes: normalizarNumero(dados.outrasAlteracoes ?? existente?.outrasAlteracoes),
    observacoes: normalizarTexto(dados.observacoes ?? existente?.observacoes) || undefined,
    lancamentoId: normalizarTexto(dados.lancamentoId ?? existente?.lancamentoId) || undefined,
    linhaLancamentoId: normalizarTexto(dados.linhaLancamentoId ?? existente?.linhaLancamentoId) || undefined,
    origemLancamentoReferencia: normalizarTexto(dados.origemLancamentoReferencia ?? existente?.origemLancamentoReferencia) || undefined,
    origemNumeroDocumento: normalizarTexto(dados.origemNumeroDocumento ?? existente?.origemNumeroDocumento) || undefined,
    ativo: dados.ativo ?? existente?.ativo ?? true,
    criadoEm: existente?.criadoEm || agora,
    atualizadoEm: agora,
  };
}

export async function obterActivosFixos(empresaId: string) {
  try {
    const activos = (await kv.getByPrefix<ActivoFixo>(`activo_fixo_${empresaId}_`))
      .filter((activo) => activo.ativo !== false)
      .sort((a, b) => compararCodigosContas(a.codigo, b.codigo));

    return { success: true, data: activos };
  } catch (error: any) {
    return { success: false, error: error.message };
  }
}

export async function criarActivoFixo(empresaId: string, dados: Partial<ActivoFixo>) {
  try {
    const existentes = await kv.getByPrefix<ActivoFixo>(`activo_fixo_${empresaId}_`);

    const activo = await normalizarActivoFixo(empresaId, dados);
    if ('error' in activo) return { success: false, error: activo.error };

    const duplicado = existentes.find((item) => item.ativo !== false && item.codigo === activo.codigo);
    if (duplicado) {
      return { success: false, error: `Ja existe um activo com o codigo ${activo.codigo}` };
    }

    await kv.set(activo.id, activo);
    return { success: true, data: activo };
  } catch (error: any) {
    return { success: false, error: error.message };
  }
}

export async function atualizarActivoFixo(empresaId: string, id: string, dados: Partial<ActivoFixo>) {
  try {
    const existente = await kv.get(id) as ActivoFixo | undefined;
    if (!existente || existente.empresaId !== empresaId || existente.ativo === false) {
      return { success: false, error: 'Activo nao encontrado' };
    }

    const codigo = normalizarTexto(dados.codigo ?? existente.codigo).toUpperCase();
    const activos = await kv.getByPrefix<ActivoFixo>(`activo_fixo_${empresaId}_`);
    const duplicado = activos.find((activo) => activo.ativo !== false && activo.id !== id && activo.codigo === codigo);
    if (duplicado) {
      return { success: false, error: `Ja existe outro activo com o codigo ${codigo}` };
    }

    const activo = await normalizarActivoFixo(empresaId, dados, existente);
    if ('error' in activo) return { success: false, error: activo.error };

    await kv.set(id, activo);
    return { success: true, data: activo };
  } catch (error: any) {
    return { success: false, error: error.message };
  }
}

export async function apagarActivoFixo(empresaId: string, id: string) {
  try {
    const existente = await kv.get(id) as ActivoFixo | undefined;
    if (!existente || existente.empresaId !== empresaId || existente.ativo === false) {
      return { success: false, error: 'Activo nao encontrado' };
    }

    await kv.set(id, {
      ...existente,
      ativo: false,
      atualizadoEm: new Date().toISOString(),
    });

    return { success: true, data: { id } };
  } catch (error: any) {
    return { success: false, error: error.message };
  }
}

function normalizarMovimentoReconciliacao(dados: Partial<MovimentoReconciliacaoBancaria>): MovimentoReconciliacaoBancaria | null {
  const movimentoId = normalizarTexto(dados.movimentoId);
  const lancamentoId = normalizarTexto(dados.lancamentoId);

  if (!movimentoId || !lancamentoId) return null;

  const debito = normalizarNumero(dados.debito);
  const credito = normalizarNumero(dados.credito);

  return {
    movimentoId,
    lancamentoId,
    linhaId: normalizarTexto(dados.linhaId) || undefined,
    data: normalizarTexto(dados.data).slice(0, 10),
    referencia: normalizarTexto(dados.referencia) || undefined,
    descricao: normalizarTexto(dados.descricao) || undefined,
    debito,
    credito,
    valor: normalizarNumero(dados.valor ?? (debito - credito)),
  };
}

function normalizarReconciliacaoBancaria(
  empresaId: string,
  dados: Partial<ReconciliacaoBancaria>,
  existente?: ReconciliacaoBancaria,
  usuarioId?: string,
): ReconciliacaoBancaria | { error: string } {
  const agora = new Date().toISOString();
  const contaId = normalizarTexto(dados.contaId ?? existente?.contaId);
  const contaCodigo = normalizarTexto(dados.contaCodigo ?? existente?.contaCodigo);
  const contaNome = normalizarTexto(dados.contaNome ?? existente?.contaNome);
  const dataInicio = normalizarTexto(dados.dataInicio ?? existente?.dataInicio).slice(0, 10);
  const dataFim = normalizarTexto(dados.dataFim ?? existente?.dataFim).slice(0, 10);
  const dataExtracto = normalizarTexto(dados.dataExtracto ?? existente?.dataExtracto).slice(0, 10) || dataFim;

  if (!contaId || !contaCodigo || !contaNome) return { error: 'Seleccione a conta bancária' };
  if (!dataInicio || !dataFim) return { error: 'Informe o período da reconciliação' };
  if (dataInicio > dataFim) return { error: 'A data inicial não pode ser maior que a data final' };

  const movimentos = (Array.isArray(dados.movimentos) ? dados.movimentos : existente?.movimentos || [])
    .map((movimento) => normalizarMovimentoReconciliacao(movimento))
    .filter((movimento): movimento is MovimentoReconciliacaoBancaria => Boolean(movimento));

  const saldoInicial = normalizarNumero(dados.saldoInicial ?? existente?.saldoInicial);
  const saldoContabil = normalizarNumero(dados.saldoContabil ?? existente?.saldoContabil);
  const saldoReconciliado = normalizarNumero(dados.saldoReconciliado ?? existente?.saldoReconciliado);
  const saldoExtracto = normalizarNumero(dados.saldoExtracto ?? existente?.saldoExtracto);
  const { diferenca } = calcularDiferencaConferencia(saldoExtracto, saldoReconciliado);
  const observacoes = normalizarTexto(dados.observacoes ?? existente?.observacoes);
  const estado = Math.abs(diferenca) < 0.01 ? 'conferida' : 'em_aberto';

  if (estado === 'em_aberto' && !observacoes) {
    return { error: 'Informe uma observacao para guardar reconciliacao com diferenca' };
  }

  return {
    id: existente?.id || normalizarTexto(dados.id) || `reconciliacao_bancaria_${empresaId}_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`,
    empresaId,
    contaId,
    contaCodigo,
    contaNome,
    dataInicio,
    dataFim,
    dataExtracto,
    saldoInicial,
    saldoContabil,
    saldoReconciliado,
    saldoExtracto,
    diferenca,
    movimentos,
    observacoes: observacoes || undefined,
    estado,
    criadoPor: existente?.criadoPor || usuarioId || normalizarTexto(dados.criadoPor) || undefined,
    criadoEm: existente?.criadoEm || agora,
    atualizadoEm: agora,
  };
}

export async function obterReconciliacoesBancarias(
  empresaId: string,
  filtros?: {
    contaId?: string;
    dataInicio?: string;
    dataFim?: string;
  },
) {
  try {
    let reconciliacoes = await kv.getByPrefix<ReconciliacaoBancaria>(`reconciliacao_bancaria_${empresaId}_`);

    if (filtros?.contaId) {
      reconciliacoes = reconciliacoes.filter((item) => item.contaId === filtros.contaId);
    }
    if (filtros?.dataInicio) {
      reconciliacoes = reconciliacoes.filter((item) => item.dataFim >= filtros.dataInicio!);
    }
    if (filtros?.dataFim) {
      reconciliacoes = reconciliacoes.filter((item) => item.dataInicio <= filtros.dataFim!);
    }

    reconciliacoes.sort((a, b) =>
      b.dataFim.localeCompare(a.dataFim) ||
      b.atualizadoEm.localeCompare(a.atualizadoEm)
    );

    return { success: true, data: reconciliacoes };
  } catch (error: any) {
    return { success: false, error: error.message };
  }
}

export async function salvarReconciliacaoBancaria(
  empresaId: string,
  dados: Partial<ReconciliacaoBancaria>,
  usuarioId?: string,
) {
  try {
    const id = normalizarTexto(dados.id);
    const existente = id ? await kv.get(id) as ReconciliacaoBancaria | undefined : undefined;

    if (existente && existente.empresaId !== empresaId) {
      return { success: false, error: 'Reconciliação não pertence a esta empresa' };
    }

    const reconciliacao = normalizarReconciliacaoBancaria(empresaId, dados, existente, usuarioId);
    if ('error' in reconciliacao) return { success: false, error: reconciliacao.error };

    if (reconciliacao.estado === 'conferida') {
      const reconciliacoes = await kv.getByPrefix<ReconciliacaoBancaria>(`reconciliacao_bancaria_${empresaId}_`);
      const movimentosJaConferidos = new Set(
        reconciliacoes
          .filter((item) => item.id !== reconciliacao.id)
          .filter((item) => item.contaId === reconciliacao.contaId)
          .filter((item) => item.estado === 'conferida')
          .flatMap((item) => (item.movimentos || []).map((movimento) => movimento.movimentoId))
      );
      const conflito = reconciliacao.movimentos.find((movimento) => movimentosJaConferidos.has(movimento.movimentoId));
      if (conflito) {
        return { success: false, error: 'Existe movimento ja conferido noutra reconciliacao' };
      }
    }

    await kv.set(reconciliacao.id, reconciliacao);
    return { success: true, data: reconciliacao };
  } catch (error: any) {
    return { success: false, error: error.message };
  }
}

export async function apagarReconciliacaoBancaria(empresaId: string, id: string) {
  try {
    const existente = await kv.get(id) as ReconciliacaoBancaria | undefined;
    if (!existente || existente.empresaId !== empresaId) {
      return { success: false, error: 'Reconciliação não encontrada' };
    }

    await kv.del(id);
    return { success: true, data: { id } };
  } catch (error: any) {
    return { success: false, error: error.message };
  }
}

function contaEhContaActivoFixo(conta?: ContaContabil) {
  if (!conta || conta.ativo === false || !conta.aceitaLancamentos) return false;

  const codigo = normalizarTexto(conta.codigo);
  const nome = normalizarTexto(conta.nome).toLowerCase();
  const prefixosActivoFixo = ['3.2', '3.3', '3.4', '3.6'];

  if (conta.classe !== 3 || !prefixosActivoFixo.some((prefixo) => codigo === prefixo || codigo.startsWith(`${prefixo}.`))) {
    return false;
  }
  if (codigo.startsWith('3.8') || codigo.startsWith('3.9')) return false;
  if (nome.includes('amort') || nome.includes('imparidade')) return false;

  return true;
}

async function sincronizarActivosDoLancamento(
  lancamento: LancamentoContabil,
  contasPorId: Map<string, ContaContabil>,
) {
  let alterouLinha = false;
  const linhasAtualizadas: LinhaLancamento[] = [];

  for (const linha of lancamento.linhas) {
    const conta = contasPorId.get(linha.contaId);
    const contaActivo = contaEhContaActivoFixo(conta);
    const temFichaActivo = Boolean(linha.activoFixo || linha.activoFixoId);

    if (lancamento.origemLancamento === 'manual' && contaActivo && linha.debito > 0 && !temFichaActivo) {
      return {
        success: false,
        error: `Preencha a ficha do activo para a conta ${conta?.codigo || ''} - ${conta?.nome || ''}`,
      };
    }

    if (!linha.activoFixo) {
      linhasAtualizadas.push(linha);
      continue;
    }

    if (!contaActivo || !conta) {
      return {
        success: false,
        error: `A conta ${conta?.codigo || linha.contaCodigo || ''} - ${conta?.nome || linha.contaNome || ''} nao e uma conta de activo fixo`,
      };
    }

    const dadosActivo: Partial<ActivoFixo> = {
      ...linha.activoFixo,
      contaId: conta.id,
      contaCodigo: conta.codigo,
      contaNome: conta.nome,
      dataAquisicao: normalizarDataISO(linha.activoFixo.dataAquisicao) || lancamento.data,
      dataEntradaServico:
        normalizarDataISO(linha.activoFixo.dataEntradaServico) ||
        normalizarDataISO(linha.activoFixo.dataAquisicao) ||
        lancamento.data,
      custoAquisicao: normalizarNumero(linha.activoFixo.custoAquisicao, linha.debito),
      lancamentoId: lancamento.id,
      linhaLancamentoId: linha.id,
      origemLancamentoReferencia: lancamento.referencia,
      origemNumeroDocumento: lancamento.numeroDocumento,
    };

    const activoId = normalizarTexto(linha.activoFixoId || linha.activoFixo.id);
    const resultado = activoId
      ? await atualizarActivoFixo(lancamento.empresaId, activoId, dadosActivo)
      : await criarActivoFixo(lancamento.empresaId, dadosActivo);

    if (!resultado.success) {
      return { success: false, error: resultado.error || 'Nao foi possivel guardar a ficha do activo' };
    }

    alterouLinha = true;
    linhasAtualizadas.push({
      ...linha,
      activoFixoId: resultado.data.id,
      activoFixo: resultado.data,
    });
  }

  if (!alterouLinha) return { success: true, data: lancamento };

  return {
    success: true,
    data: {
      ...lancamento,
      linhas: linhasAtualizadas,
    },
  };
}

export async function criarConta(dados: Omit<ContaContabil, 'id' | 'dataCriacao'>) {
  try {
    // Validações
    if (!dados.codigo || !dados.nome) {
      return { success: false, error: 'Código e nome são obrigatórios' };
    }

    const codigo = dados.codigo.trim();
    if (!/^[0-9]+(\.[0-9]+)*$/.test(codigo)) {
      return { success: false, error: 'Código inválido. Use apenas números e pontos (ex: 4.1.1)' };
    }
    
    // Verificar se código já existe
    const contasExistentes = await kv.getByPrefix(`conta_${dados.empresaId}_`);
    const codigoDuplicado = contasExistentes.find((c: ContaContabil) => c.codigo === codigo);
    
    if (codigoDuplicado) {
      return { success: false, error: `Já existe uma conta com o código ${codigo}` };
    }

    const codigoPai = obterCodigoPai(codigo);
    const contaPai = codigoPai
      ? contasExistentes.find((c: ContaContabil) => c.codigo === codigoPai)
      : undefined;

    if (codigoPai && !contaPai) {
      return { success: false, error: `Conta pai ${codigoPai} não encontrada` };
    }

    const classe = calcularClasseConta(codigo);
    
    const tipo = dados.tipo || contaPai?.tipo || inferirTipoContaBase(codigo);
    const conta: ContaContabil = {
      id: `conta_${dados.empresaId}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
      ...dados,
      codigo,
      classe,
      tipo,
      parentId: contaPai?.id,
      nivel: calcularNivelConta(codigo),
      aceitaLancamentos: dados.aceitaLancamentos ?? true,
      grupoRelatorio: dados.grupoRelatorio || inferirGrupoRelatorioContaBase(codigo, tipo),
      isSystem: dados.isSystem ?? false,
      ativo: dados.ativo ?? true,
      dataCriacao: new Date().toISOString(),
    };
    
    await kv.set(conta.id, conta);

    let warning: string | undefined;
    if (contaPai?.aceitaLancamentos) {
      await kv.set(contaPai.id, {
        ...contaPai,
        aceitaLancamentos: false,
      });

      const referenciaMapeamento = await contaEstaMapeada(dados.empresaId, contaPai.id);
      if (referenciaMapeamento) {
        warning = `A conta pai "${contaPai.codigo} - ${contaPai.nome}" deixou de aceitar lancamentos directos e esta configurada em ${referenciaMapeamento}. Verifique o mapeamento se quiser apontar para esta nova subconta.`;
      }
    }
    console.log(`[CONTABILIDADE] ✅ Conta criada: ${conta.codigo} - ${conta.nome}`);

    return { success: true, data: conta, warning };
  } catch (error: any) {
    console.error('[CONTABILIDADE] ❌ Erro ao criar conta:', error);
    return { success: false, error: error.message };
  }
}

function contaTemDescendentes(conta: ContaContabil, contas: ContaContabil[]): boolean {
  return contas.some((item) => item.id !== conta.id && item.codigo.startsWith(`${conta.codigo}.`));
}

// Verifica se uma conta esta referenciada em algum mapeamento contabil, para
// avisar quando ela deixa de aceitar lancamentos (ex: por ganhar subcontas) —
// os lancamentos automaticos continuam a funcionar via resolverContaLancavelPorId
// (que procura uma subconta lancavel), mas o mapeamento configurado passa a
// apontar para uma conta que ja nao lanca directamente, o que vale a pena revisar.
async function contaEstaMapeada(empresaId: string, contaId: string): Promise<string | undefined> {
  const mapeamentos = await kv.getByPrefix<MapeamentoContabil>(`mapeamento_${empresaId}_`);
  for (const mapeamento of mapeamentos) {
    const referencia = CAMPOS_CONTA_MAPEAMENTO.find(({ campo }) => mapeamento[campo] === contaId);
    if (referencia) return `${referencia.rotulo} (${mapeamento.tipoDocumento})`;
  }
  return undefined;
}

async function contaTemLancamentos(empresaId: string, conta: ContaContabil): Promise<boolean> {
  const lancamentos = await kv.getByPrefix<LancamentoContabil>(`lancamento_${empresaId}_`);
  return lancamentos.some((lancamento) =>
    lancamento.linhas?.some((linha) => linha.contaId === conta.id || linha.contaCodigo === conta.codigo)
  );
}

async function atualizarLinhasLancamentoConta(
  empresaId: string,
  contaAnterior: ContaContabil,
  contaAtualizada: ContaContabil
) {
  const lancamentos = await kv.getByPrefixWithKeys<LancamentoContabil>(`lancamento_${empresaId}_`);

  for (const { key, value: lancamento } of lancamentos) {
    let alterado = false;
    const linhas = lancamento.linhas.map((linha) => {
      if (linha.contaId !== contaAtualizada.id && linha.contaCodigo !== contaAnterior.codigo) {
        return linha;
      }

      alterado = true;
      return {
        ...linha,
        contaId: contaAtualizada.id,
        contaCodigo: contaAtualizada.codigo,
        contaNome: contaAtualizada.nome,
      };
    });

    if (alterado) {
      await kv.set(key, { ...lancamento, linhas });
    }
  }
}

export async function atualizarConta(id: string, dados: Partial<ContaContabil>) {
  try {
    const contaExistente = await kv.get(id) as ContaContabil;
    
    if (!contaExistente) {
      return { success: false, error: 'Conta não encontrada' };
    }
    
    // Não permitir alterar código se for conta do sistema
    if (contaExistente.isSystem && dados.codigo && dados.codigo !== contaExistente.codigo) {
      return { success: false, error: 'Não é possível alterar o código de contas do sistema' };
    }
    
    const contaAtualizada = {
      ...contaExistente,
      ...dados,
      id: contaExistente.id,
      empresaId: contaExistente.empresaId,
      isSystem: contaExistente.isSystem,
      dataCriacao: contaExistente.dataCriacao,
    };
    
    await kv.set(id, contaAtualizada);
    console.log(`[CONTABILIDADE] ✅ Conta atualizada: ${contaAtualizada.codigo}`);
    
    return { success: true, data: contaAtualizada };
  } catch (error: any) {
    return { success: false, error: error.message };
  }
}

// ========================================
// FUNÇÕES DE DIÁRIOS
// ========================================

export async function atualizarContaSegura(id: string, dados: Partial<ContaContabil>, empresaIdEsperada?: string) {
  try {
    const contaExistente = await kv.get(id) as ContaContabil;

    if (!contaExistente) {
      return { success: false, error: 'Conta nÃ£o encontrada' };
    }

    if (empresaIdEsperada && contaExistente.empresaId !== empresaIdEsperada) {
      return { success: false, error: 'Conta nÃƒÂ£o pertence a esta empresa' };
    }

    if (contaExistente.isSystem) {
      return { success: false, error: 'Contas do sistema sÃ³ podem ser alteradas nas ferramentas avanÃ§adas' };
    }

    const contasEmpresa = await kv.getByPrefix<ContaContabil>(`conta_${contaExistente.empresaId}_`);
    const outrasContas = contasEmpresa.filter((conta) => conta.id !== id);
    const codigo = (dados.codigo || contaExistente.codigo).trim();
    const nome = (dados.nome || contaExistente.nome).trim();

    if (!codigo || !nome) {
      return { success: false, error: 'CÃ³digo e nome sÃ£o obrigatÃ³rios' };
    }

    if (!/^[0-9]+(\.[0-9]+)*$/.test(codigo)) {
      return { success: false, error: 'CÃ³digo invÃ¡lido. Use apenas nÃºmeros e pontos (ex: 4.1.1)' };
    }

    if (outrasContas.some((conta) => conta.codigo === codigo)) {
      return { success: false, error: `JÃ¡ existe uma conta com o cÃ³digo ${codigo}` };
    }

    const codigoAlterado = codigo !== contaExistente.codigo;
    if (codigoAlterado && contaTemDescendentes(contaExistente, contasEmpresa)) {
      return { success: false, error: 'NÃ£o Ã© possÃ­vel alterar o cÃ³digo de uma conta com subcontas' };
    }

    if (codigoAlterado && await contaTemLancamentos(contaExistente.empresaId, contaExistente)) {
      return { success: false, error: 'NÃ£o Ã© possÃ­vel alterar o cÃ³digo de uma conta jÃ¡ usada em lanÃ§amentos' };
    }

    const estaDesactivando = dados.ativo === false && contaExistente.ativo !== false;
    if (estaDesactivando && await contaTemLancamentos(contaExistente.empresaId, contaExistente)) {
      return { success: false, error: 'NÃ£o Ã© possÃ­vel desactivar uma conta jÃ¡ usada em lanÃ§amentos' };
    }

    const codigoPai = obterCodigoPai(codigo);
    const contaPai = codigoPai
      ? contasEmpresa.find((conta) => conta.codigo === codigoPai)
      : undefined;

    if (codigoPai && !contaPai) {
      return { success: false, error: `Conta pai ${codigoPai} nÃ£o encontrada` };
    }

    const classe = calcularClasseConta(codigo);
    const tipo = contaPai?.tipo || inferirTipoContaBase(codigo);
    const temDescendentes = outrasContas.some((conta) => conta.codigo.startsWith(`${codigo}.`));
    const contaAtualizada: ContaContabil = {
      ...contaExistente,
      ...dados,
      id: contaExistente.id,
      empresaId: contaExistente.empresaId,
      codigo,
      nome,
      classe,
      tipo,
      parentId: contaPai?.id,
      nivel: calcularNivelConta(codigo),
      aceitaLancamentos: temDescendentes ? false : (dados.aceitaLancamentos ?? contaExistente.aceitaLancamentos ?? true),
      grupoRelatorio: dados.grupoRelatorio || inferirGrupoRelatorioContaBase(codigo, tipo),
      isSystem: contaExistente.isSystem,
      dataCriacao: contaExistente.dataCriacao,
    };

    await kv.set(id, contaAtualizada);
    await atualizarLinhasLancamentoConta(contaExistente.empresaId, contaExistente, contaAtualizada);

    let warning: string | undefined;
    if (contaPai?.aceitaLancamentos) {
      await kv.set(contaPai.id, {
        ...contaPai,
        aceitaLancamentos: false,
      });

      const referenciaMapeamento = await contaEstaMapeada(contaExistente.empresaId, contaPai.id);
      if (referenciaMapeamento) {
        warning = `A conta pai "${contaPai.codigo} - ${contaPai.nome}" deixou de aceitar lancamentos directos e esta configurada em ${referenciaMapeamento}. Verifique o mapeamento se quiser apontar para esta subconta.`;
      }
    }

    console.log(`[CONTABILIDADE] Conta atualizada: ${contaAtualizada.codigo}`);
    return { success: true, data: contaAtualizada, warning };
  } catch (error: any) {
    return { success: false, error: error.message };
  }
}

export async function apagarConta(id: string, empresaIdEsperada?: string) {
  try {
    const conta = await kv.get(id) as ContaContabil;

    if (!conta) {
      return { success: false, error: 'Conta nÃ£o encontrada' };
    }

    if (empresaIdEsperada && conta.empresaId !== empresaIdEsperada) {
      return { success: false, error: 'Conta nÃƒÂ£o pertence a esta empresa' };
    }

    if (conta.isSystem) {
      return { success: false, error: 'Contas do sistema nÃ£o podem ser apagadas' };
    }

    const contasEmpresa = await kv.getByPrefix<ContaContabil>(`conta_${conta.empresaId}_`);
    if (contaTemDescendentes(conta, contasEmpresa)) {
      return { success: false, error: 'NÃ£o Ã© possÃ­vel apagar uma conta com subcontas' };
    }

    if (await contaTemLancamentos(conta.empresaId, conta)) {
      return { success: false, error: 'NÃ£o Ã© possÃ­vel apagar uma conta jÃ¡ usada em lanÃ§amentos' };
    }

    await kv.del(id);

    if (conta.parentId) {
      const contaPai = contasEmpresa.find((item) => item.id === conta.parentId);
      const paiAindaTemFilhos = contaPai
        ? contasEmpresa.some((item) => item.id !== conta.id && item.id !== contaPai.id && item.codigo.startsWith(`${contaPai.codigo}.`))
        : false;

      if (contaPai && !contaPai.isSystem && !paiAindaTemFilhos) {
        await kv.set(contaPai.id, {
          ...contaPai,
          aceitaLancamentos: true,
        });
      }
    }

    console.log(`[CONTABILIDADE] Conta apagada: ${conta.codigo} - ${conta.nome}`);
    return { success: true };
  } catch (error: any) {
    return { success: false, error: error.message };
  }
}

function normalizarTipoDiario(tipo?: string): Diario['tipo'] {
  const valor = (tipo || '').trim().toLowerCase();
  if (valor === 'banco') return 'bancos';
  if (['salario', 'salários', 'salarios', 'folha_salario', 'folha_salarios'].includes(valor)) return 'salarios';
  if (['regularizacao', 'regularização', 'regularizacoes', 'regularizações'].includes(valor)) return 'regularizacoes';
  if (['geral', 'vendas', 'compras', 'caixa', 'bancos', 'operacoes_diversas', 'salarios', 'regularizacoes'].includes(valor)) {
    return valor as Diario['tipo'];
  }
  return 'geral';
}

function obterConfiguracaoDiarioPorTipo(tipo: Diario['tipo'] | 'banco' | string) {
  const tipoNormalizado = normalizarTipoDiario(tipo);
  const configuracoes: Record<Diario['tipo'], {
    permiteLancamentoManual: boolean;
    geraLancamentoAutomatico: boolean;
    documentosOrigem: string[];
  }> = {
    geral: {
      permiteLancamentoManual: true,
      geraLancamentoAutomatico: false,
      documentosOrigem: [],
    },
    vendas: {
      permiteLancamentoManual: true,
      geraLancamentoAutomatico: true,
      documentosOrigem: ['fatura', 'factura_recibo', 'venda_dinheiro', 'nota_credito', 'nota_debito'],
    },
    compras: {
      permiteLancamentoManual: true,
      geraLancamentoAutomatico: true,
      documentosOrigem: ['compra'],
    },
    caixa: {
      permiteLancamentoManual: true,
      geraLancamentoAutomatico: true,
      documentosOrigem: ['recibo', 'adiantamento', 'pagamento'],
    },
    bancos: {
      permiteLancamentoManual: true,
      geraLancamentoAutomatico: true,
      documentosOrigem: ['recibo', 'adiantamento', 'pagamento'],
    },
    operacoes_diversas: {
      permiteLancamentoManual: true,
      geraLancamentoAutomatico: false,
      documentosOrigem: [],
    },
    salarios: {
      permiteLancamentoManual: true,
      geraLancamentoAutomatico: true,
      documentosOrigem: ['salarios', 'salario', 'folha_pagamento', 'folha_salarial'],
    },
    regularizacoes: {
      permiteLancamentoManual: true,
      geraLancamentoAutomatico: false,
      documentosOrigem: ['regularizacao'],
    },
  };

  return configuracoes[tipoNormalizado] || configuracoes.geral;
}

function normalizarDiario(diario: Diario): Diario {
  const tipo = normalizarTipoDiario((diario as any).tipo);
  const defaults = obterConfiguracaoDiarioPorTipo(tipo);

  return {
    ...diario,
    tipo,
    permiteLancamentoManual: diario.permiteLancamentoManual ?? defaults.permiteLancamentoManual,
    geraLancamentoAutomatico: diario.geraLancamentoAutomatico ?? defaults.geraLancamentoAutomatico,
    documentosOrigem: diario.documentosOrigem ?? defaults.documentosOrigem,
    ativo: diario.ativo ?? true,
    bloqueado: diario.bloqueado ?? false,
  };
}

function prepararDadosDiario(
  empresaId: string,
  dados: Partial<Diario>,
  diarioExistente?: Diario
): Diario {
  const tipo = normalizarTipoDiario(dados.tipo || diarioExistente?.tipo || 'geral');
  const defaults = obterConfiguracaoDiarioPorTipo(tipo);

  return normalizarDiario({
    id: diarioExistente?.id || `diario_${empresaId}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
    empresaId,
    codigo: (dados.codigo || diarioExistente?.codigo || '').trim().toUpperCase(),
    nome: (dados.nome || diarioExistente?.nome || '').trim(),
    tipo,
    descricao: dados.descricao ?? diarioExistente?.descricao,
    permiteLancamentoManual: dados.permiteLancamentoManual ?? diarioExistente?.permiteLancamentoManual ?? defaults.permiteLancamentoManual,
    geraLancamentoAutomatico: dados.geraLancamentoAutomatico ?? diarioExistente?.geraLancamentoAutomatico ?? defaults.geraLancamentoAutomatico,
    documentosOrigem: dados.documentosOrigem ?? diarioExistente?.documentosOrigem ?? defaults.documentosOrigem,
    ativo: dados.ativo ?? diarioExistente?.ativo ?? true,
    bloqueado: dados.bloqueado ?? diarioExistente?.bloqueado ?? false,
    dataCriacao: diarioExistente?.dataCriacao || new Date().toISOString(),
  });
}

export async function inicializarDiarios(empresaId: string) {
  console.log(`[CONTABILIDADE] Inicializando diários para empresa: ${empresaId}`);
  
  try {
    const diariosBase: Partial<Diario>[] = [
      { codigo: 'GER', nome: 'Diario Geral', tipo: 'geral', descricao: 'Lancamentos gerais e manuais' },
      { codigo: 'VND', nome: 'Diario de Vendas', tipo: 'vendas', descricao: 'Documentos de venda e notas relacionadas' },
      { codigo: 'CPR', nome: 'Diario de Compras', tipo: 'compras', descricao: 'Compras e documentos de fornecedores' },
      { codigo: 'BAN', nome: 'Diario de Bancos', tipo: 'bancos', descricao: 'Movimentos bancarios' },
      { codigo: 'CXA', nome: 'Diario de Caixa', tipo: 'caixa', descricao: 'Movimentos de caixa' },
      { codigo: 'SAL', nome: 'Diario de Salarios', tipo: 'salarios', descricao: 'Processamento de salarios e encargos' },
      { codigo: 'REG', nome: 'Diario de Regularizacoes', tipo: 'regularizacoes', descricao: 'Regularizacoes e ajustes contabilisticos' },
    ];
    
    const diariosExistentes = await kv.getByPrefix(`diario_${empresaId}_`);
    const diariosPorCodigo = new Map<string, Diario>(
      diariosExistentes.map((diario: Diario) => {
        const diarioNormalizado = normalizarDiario(diario);
        return [diarioNormalizado.codigo.trim().toUpperCase(), diarioNormalizado] as [string, Diario];
      })
    );
    const diariosCriados = [];
    for (const diarioBase of diariosBase) {
      const codigo = (diarioBase.codigo || '').trim().toUpperCase();
      if (diariosPorCodigo.has(codigo)) continue;

      const diario = prepararDadosDiario(empresaId, diarioBase);

      await kv.set(diario.id, diario);
      diariosPorCodigo.set(diario.codigo.trim().toUpperCase(), diario);
      diariosCriados.push(diario);
    }

    // Auto-correcao: o diario SAL foi originalmente semeado com
    // geraLancamentoAutomatico=false (antes de existir processamento
    // salarial). Corrige diarios ja existentes que ainda tenham esse valor
    // antigo, sem tocar num valor que o utilizador tenha alterado
    // deliberadamente para true.
    const diarioSalExistente = diariosPorCodigo.get('SAL');
    if (diarioSalExistente && diarioSalExistente.geraLancamentoAutomatico === false) {
      const diarioCorrigido = { ...diarioSalExistente, geraLancamentoAutomatico: true };
      await kv.set(diarioCorrigido.id, diarioCorrigido);
      diariosPorCodigo.set('SAL', diarioCorrigido);
    }
    
    const diarios = Array.from(diariosPorCodigo.values()).sort((a, b) =>
      a.codigo.localeCompare(b.codigo, 'pt', { numeric: true, sensitivity: 'base' })
    );

    console.log(`[CONTABILIDADE] ✅ Diários completos: ${diarios.length} (${diariosCriados.length} novos)`);
    return { success: true, data: diarios, created: diariosCriados.length };
  } catch (error: any) {
    console.error('[CONTABILIDADE] ❌ Erro ao inicializar diários:', error);
    return { success: false, error: error.message };
  }
}

export async function obterDiarios(empresaId: string) {
  try {
    await inicializarDiarios(empresaId);

    const diarios = (await kv.getByPrefix(`diario_${empresaId}_`))
      .map((diario: Diario) => normalizarDiario(diario));
    const lancamentos = await kv.getByPrefix(`lancamento_${empresaId}_`);
    
    // Para cada diário, contar lançamentos e obter último lançamento
    const diariosComStats = await Promise.all(
      diarios.map(async (diario: any) => {
        const lancamentosDoDiario = lancamentos.filter((l: any) => l.diarioId === diario.id);
        
        return {
          ...diario,
          totalLancamentos: lancamentosDoDiario.length,
          ultimoLancamento: lancamentosDoDiario.length > 0
            ? lancamentosDoDiario.sort((a: any, b: any) => 
                new Date(b.data).getTime() - new Date(a.data).getTime()
              )[0].data
            : undefined,
        };
      })
    );
    
    const diariosUnicosPorCodigo = new Map<string, any>();
    for (const diario of diariosComStats) {
      const codigo = String(diario.codigo || '').trim().toUpperCase();
      if (!codigo) continue;

      const existente = diariosUnicosPorCodigo.get(codigo);
      if (
        !existente ||
        (diario.totalLancamentos || 0) > (existente.totalLancamentos || 0) ||
        ((diario.totalLancamentos || 0) === (existente.totalLancamentos || 0) && diario.ativo !== false && existente.ativo === false)
      ) {
        diariosUnicosPorCodigo.set(codigo, diario);
      }
    }

    const diariosOrdenados = Array.from(diariosUnicosPorCodigo.values()).sort((a, b) =>
      a.codigo.localeCompare(b.codigo, 'pt', { numeric: true, sensitivity: 'base' })
    );

    return { success: true, data: diariosOrdenados };
  } catch (error: any) {
    return { success: false, error: error.message };
  }
}

export async function criarDiario(empresaId: string, dados: any) {
  try {
    const diario = prepararDadosDiario(empresaId, dados);
    if (!diario.codigo || !diario.nome) {
      return { success: false, error: 'Codigo e nome sao obrigatorios' };
    }

    // Verificar se código já existe
    const diariosExistentes = await kv.getByPrefix(`diario_${empresaId}_`);
    const codigoExiste = diariosExistentes.some(
      (d: any) => String(d.codigo || '').trim().toUpperCase() === diario.codigo
    );
    
    if (codigoExiste) {
      return { success: false, error: 'Já existe um diário com este código' };
    }
    
    await kv.set(diario.id, diario);
    console.log(`[CONTABILIDADE] ✅ Diário criado: ${diario.codigo} - ${diario.nome}`);
    
    return { success: true, data: diario };
  } catch (error: any) {
    console.error('[CONTABILIDADE] ❌ Erro ao criar diário:', error);
    return { success: false, error: error.message };
  }
}

export async function atualizarDiario(id: string, dados: Partial<Diario>, empresaIdEsperada?: string) {
  try {
    const diarioExistente = await kv.get(id) as Diario;

    if (!diarioExistente) {
      return { success: false, error: 'Diario nao encontrado' };
    }

    if (empresaIdEsperada && diarioExistente.empresaId !== empresaIdEsperada) {
      return { success: false, error: 'Diario nao pertence a esta empresa' };
    }

    const diarioAtualizado = prepararDadosDiario(diarioExistente.empresaId, dados, diarioExistente);
    if (!diarioAtualizado.codigo || !diarioAtualizado.nome) {
      return { success: false, error: 'Codigo e nome sao obrigatorios' };
    }

    const diariosExistentes = await kv.getByPrefix(`diario_${diarioExistente.empresaId}_`);
    const codigoDuplicado = diariosExistentes.some(
      (diario: Diario) =>
        diario.id !== id &&
        String(diario.codigo || '').trim().toUpperCase() === diarioAtualizado.codigo
    );

    if (codigoDuplicado) {
      return { success: false, error: 'Ja existe um diario com este codigo' };
    }

    await kv.set(id, diarioAtualizado);
    console.log(`[CONTABILIDADE] Diario atualizado: ${diarioAtualizado.codigo}`);

    return { success: true, data: diarioAtualizado };
  } catch (error: any) {
    return { success: false, error: error.message };
  }
}

export async function atualizarBloqueioDiario(id: string, bloqueado: boolean) {
  try {
    const diario = await kv.get(id) as any;
    
    if (!diario) {
      return { success: false, error: 'Diário não encontrado' };
    }
    
    diario.bloqueado = bloqueado;
    await kv.set(id, diario);
    
    console.log(`[CONTABILIDADE] ✅ Diário ${bloqueado ? 'bloqueado' : 'desbloqueado'}: ${diario.codigo}`);
    
    return { success: true, data: diario };
  } catch (error: any) {
    console.error('[CONTABILIDADE] ❌ Erro ao atualizar bloqueio:', error);
    return { success: false, error: error.message };
  }
}

// ========================================
// FUNÇÕES DE LANÇAMENTOS CONTÁBEIS
// ========================================

function obterAnoReferenciaLancamento(data?: string) {
  const dataBase = data ? new Date(data) : new Date();
  if (Number.isNaN(dataBase.getTime())) {
    return String(new Date().getFullYear());
  }

  return String(dataBase.getFullYear());
}

function extrairNumeroReferenciaLancamento(referencia: string | undefined, ano: string) {
  const match = (referencia || '').trim().toUpperCase().match(/^LC[/-](\d{4})[/-](\d+)$/);
  if (!match || match[1] !== ano) {
    return 0;
  }

  return Number(match[2]) || 0;
}

export async function preverProximaReferenciaLancamento(empresaId: string, data?: string, anoExercicio?: number) {
  try {
    const ano = String(anoExercicio || obterAnoReferenciaLancamento(data));
    const lancamentos = await kv.getByPrefix<LancamentoContabil>(`lancamento_${empresaId}_`);
    const ultimoNumero = lancamentos.reduce((maior, lancamento) => {
      return Math.max(maior, extrairNumeroReferenciaLancamento(lancamento.referencia, ano));
    }, 0);
    const proximoNumero = String(ultimoNumero + 1).padStart(4, '0');

    return { success: true, data: { referencia: `LC/${ano}/${proximoNumero}` } };
  } catch (error: any) {
    console.error('[CONTABILIDADE] Erro ao prever referencia do lancamento:', error);
    return { success: false, error: error.message };
  }
}

async function gerarReferenciaLancamento(empresaId: string, data?: string, anoExercicio?: number) {
  const ano = Number(anoExercicio || obterAnoReferenciaLancamento(data));
  const lancamentos = await kv.getByPrefix<LancamentoContabil>(`lancamento_${empresaId}_`);
  const reserva = await reservarNumeroSequencial<LancamentoContabil>({
    empresaId,
    modulo: "contabilidade",
    tipoDocumento: "lancamento_contabil",
    ano,
    documentosExistentes: lancamentos,
    extrairSequencia: (lancamento, anoDocumento) =>
      extrairNumeroReferenciaLancamento(lancamento.referencia, String(anoDocumento)),
    formatarNumero: (sequencia, anoDocumento) => `LC/${anoDocumento}/${String(sequencia).padStart(4, '0')}`,
  });

  return reserva.numeroDocumento;
}

export async function criarLancamento(dados: Omit<LancamentoContabil, 'id'>) {
  try {
    if (!dados.diarioId) {
      return { success: false, error: 'Diario e obrigatorio' };
    }

    const descricao = (dados.descricao || '').trim();
    if (!dados.data || !descricao) {
      return { success: false, error: 'Data e descricao sao obrigatorias' };
    }

    const exercicio = await exerciciosEconomicos.validarExercicioAberto(
      dados.empresaId,
      dados.data,
      'lancamento contabil',
    );

    const linhasEntrada = Array.isArray(dados.linhas) ? dados.linhas : [];
    if (linhasEntrada.length < 2) {
      return { success: false, error: 'E necessario pelo menos 2 linhas validas' };
    }

    const diarioRaw = await kv.get(dados.diarioId) as Diario;
    if (!diarioRaw) {
      return { success: false, error: 'Diario nao encontrado ou inativo' };
    }

    const diario = normalizarDiario(diarioRaw);
    if (diario.empresaId !== dados.empresaId || !diario.ativo) {
      return { success: false, error: 'Diario nao encontrado ou inativo' };
    }

    if (diario.bloqueado) {
      return { success: false, error: 'Diario bloqueado para novos lancamentos' };
    }

    const origemLancamento = dados.origemLancamento || (dados.tipoDocumento ? 'automatico' : 'manual');
    if (origemLancamento === 'manual' && diario.permiteLancamentoManual === false) {
      return { success: false, error: 'Este diario nao aceita lancamentos manuais' };
    }

    if (origemLancamento === 'automatico' && diario.geraLancamentoAutomatico === false) {
      return { success: false, error: 'Este diario nao esta habilitado para lancamentos automaticos' };
    }

    if (origemLancamento === 'automatico' && dados.documentoId) {
      const tipoDocumento = normalizarTipoDocumentoContabil(dados.tipoDocumento);
      const lancamentosExistentes = await kv.getByPrefix<LancamentoContabil>(`lancamento_${dados.empresaId}_`);
      const lancamentoExistente = lancamentosExistentes.find((lancamento) =>
        lancamento.documentoId === dados.documentoId &&
        normalizarTipoDocumentoContabil(lancamento.tipoDocumento) === tipoDocumento &&
        lancamento.estado !== 'estornado'
      );

      if (lancamentoExistente) {
        return { success: false, error: 'Ja existe lancamento contabil para este documento fiscal' };
      }
    }

    const estadoLancamento = (
      dados.estado || (origemLancamento === 'automatico' ? 'rascunho' : 'lancado')
    ) as LancamentoContabil['estado'];
    if (estadoLancamento !== 'rascunho' && estadoLancamento !== 'lancado') {
      return { success: false, error: 'Estado invalido para novo lancamento' };
    }

    const contas = await kv.getByPrefix(`conta_${dados.empresaId}_`);
    const contasPorId = new Map<string, ContaContabil>(
      contas.map((conta: ContaContabil) => [conta.id, conta])
    );

    const linhasNormalizadas: LinhaLancamento[] = [];
    for (const linha of linhasEntrada) {
      const debito = Number(linha.debito) || 0;
      const credito = Number(linha.credito) || 0;

      if (debito === 0 && credito === 0) {
        continue;
      }

      if (debito < 0 || credito < 0) {
        return { success: false, error: 'Valores de debito e credito nao podem ser negativos' };
      }

      if (debito > 0 && credito > 0) {
        return { success: false, error: 'Uma linha nao pode ter debito e credito ao mesmo tempo' };
      }

      if (!linha.contaId) {
        return { success: false, error: 'Todas as linhas com valor devem ter uma conta' };
      }

      const conta = contasPorId.get(linha.contaId);
      if (!conta) {
        return { success: false, error: 'Uma das contas selecionadas não foi encontrada' };
      }

      if (!conta.aceitaLancamentos) {
        return {
          success: false,
          error: `A conta ${conta.codigo} - ${conta.nome} é sintética e não aceita lançamentos`,
        };
      }

      linhasNormalizadas.push({
        id: linha.id || `linha_${Date.now()}_${Math.random().toString(36).substr(2, 6)}`,
        contaId: conta.id,
        contaCodigo: conta.codigo,
        contaNome: conta.nome,
        descricao: (linha.descricao || '').trim(),
        debito,
        credito,
        activoFixoId: linha.activoFixoId,
        activoFixo: linha.activoFixo,
      });
    }

    if (linhasNormalizadas.length < 2) {
      return { success: false, error: 'E necessario pelo menos 2 linhas validas' };
    }

    const totalDebito = linhasNormalizadas.reduce((sum, linha) => sum + linha.debito, 0);
    const totalCredito = linhasNormalizadas.reduce((sum, linha) => sum + linha.credito, 0);

    if (totalDebito <= 0 || totalCredito <= 0) {
      return { success: false, error: 'Debito e credito devem ter valor maior que zero' };
    }
    
    if (Math.abs(totalDebito - totalCredito) > 0.01) {
      return {
        success: false,
        error: `Lançamento não está balanceado. Débito: ${totalDebito.toFixed(2)}, Crédito: ${totalCredito.toFixed(2)}`,
      };
    }
    
    const referencia = await gerarReferenciaLancamento(dados.empresaId, dados.data, exercicio.ano);

    const lancamento: LancamentoContabil = {
      id: `lancamento_${dados.empresaId}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
      ...dados,
      referencia,
      exercicioEconomicoId: exercicio.id,
      anoExercicio: exercicio.ano,
      descricao,
      numeroDocumento: dados.numeroDocumento?.trim() || dados.numeroDocumento,
      observacoes: dados.observacoes?.trim() || undefined,
      documentosSuporte: Array.isArray(dados.documentosSuporte) ? dados.documentosSuporte : undefined,
      origemLancamento,
      estado: estadoLancamento,
      dataLancamento: estadoLancamento === 'lancado'
        ? dados.dataLancamento || new Date().toISOString()
        : undefined,
      linhas: linhasNormalizadas,
      totalDebito,
      totalCredito,
    };
    
    const sincronizacaoActivos = await sincronizarActivosDoLancamento(lancamento, contasPorId);
    if (!sincronizacaoActivos.success) {
      return sincronizacaoActivos;
    }

    await kv.set(lancamento.id, sincronizacaoActivos.data);
    console.log(`[CONTABILIDADE] ✅ Lançamento criado: ${lancamento.referencia}`);
    
    return { success: true, data: sincronizacaoActivos.data };
  } catch (error: any) {
    console.error('[CONTABILIDADE] ❌ Erro ao criar lançamento:', error);
    return { success: false, error: error.message };
  }
}

export async function atualizarLancamento(id: string, dados: Partial<LancamentoContabil>) {
  try {
    const lancamentoAtual = await kv.get(id) as LancamentoContabil;

    if (!lancamentoAtual) {
      return { success: false, error: 'Lancamento nao encontrado' };
    }

    if (lancamentoAtual.estado === 'estornado') {
      return { success: false, error: 'Lancamento estornado nao pode ser alterado' };
    }

    if (lancamentoAtual.estado === 'lancado') {
      return { success: false, error: 'Lancamento ja lancado nao pode ser alterado; faca estorno se necessario' };
    }

    const diarioId = dados.diarioId || lancamentoAtual.diarioId;
    const data = dados.data || lancamentoAtual.data;
    const descricao = (dados.descricao || lancamentoAtual.descricao || '').trim();
    const estadoLancamento = (dados.estado || lancamentoAtual.estado || 'rascunho') as LancamentoContabil['estado'];

    if (!diarioId) {
      return { success: false, error: 'Diario e obrigatorio' };
    }

    if (!data || !descricao) {
      return { success: false, error: 'Data e descricao sao obrigatorias' };
    }

    const exercicio = await exerciciosEconomicos.validarExercicioAberto(
      lancamentoAtual.empresaId,
      data,
      'lancamento contabil',
    );

    if (estadoLancamento !== 'rascunho' && estadoLancamento !== 'lancado') {
      return { success: false, error: 'Estado invalido para atualizacao' };
    }

    const diarioRaw = await kv.get(diarioId) as Diario;
    if (!diarioRaw) {
      return { success: false, error: 'Diario nao encontrado ou inativo' };
    }

    const diario = normalizarDiario(diarioRaw);
    if (diario.empresaId !== lancamentoAtual.empresaId || !diario.ativo) {
      return { success: false, error: 'Diario nao encontrado ou inativo' };
    }

    if (diario.bloqueado) {
      return { success: false, error: 'Diario bloqueado para lancamentos' };
    }

    const origemLancamento = dados.origemLancamento || lancamentoAtual.origemLancamento || 'manual';
    if (origemLancamento === 'manual' && diario.permiteLancamentoManual === false) {
      return { success: false, error: 'Este diario nao aceita lancamentos manuais' };
    }

    const linhasEntrada = Array.isArray(dados.linhas) ? dados.linhas : lancamentoAtual.linhas;
    if (!Array.isArray(linhasEntrada) || linhasEntrada.length < 2) {
      return { success: false, error: 'E necessario pelo menos 2 linhas validas' };
    }

    const contas = await kv.getByPrefix(`conta_${lancamentoAtual.empresaId}_`);
    const contasPorId = new Map<string, ContaContabil>(
      contas.map((conta: ContaContabil) => [conta.id, conta])
    );

    const linhasNormalizadas: LinhaLancamento[] = [];
    for (const linha of linhasEntrada) {
      const debito = Number(linha.debito) || 0;
      const credito = Number(linha.credito) || 0;

      if (debito === 0 && credito === 0) continue;

      if (debito < 0 || credito < 0) {
        return { success: false, error: 'Valores de debito e credito nao podem ser negativos' };
      }

      if (debito > 0 && credito > 0) {
        return { success: false, error: 'Uma linha nao pode ter debito e credito ao mesmo tempo' };
      }

      if (!linha.contaId) {
        return { success: false, error: 'Todas as linhas com valor devem ter uma conta' };
      }

      const conta = contasPorId.get(linha.contaId);
      if (!conta) {
        return { success: false, error: 'Uma das contas selecionadas nao foi encontrada' };
      }

      if (!conta.aceitaLancamentos) {
        return {
          success: false,
          error: `A conta ${conta.codigo} - ${conta.nome} nao aceita lancamentos`,
        };
      }

      linhasNormalizadas.push({
        id: linha.id || `linha_${Date.now()}_${Math.random().toString(36).substr(2, 6)}`,
        contaId: conta.id,
        contaCodigo: conta.codigo,
        contaNome: conta.nome,
        descricao: (linha.descricao || '').trim(),
        debito,
        credito,
        activoFixoId: linha.activoFixoId,
        activoFixo: linha.activoFixo,
      });
    }

    if (linhasNormalizadas.length < 2) {
      return { success: false, error: 'E necessario pelo menos 2 linhas validas' };
    }

    const totalDebito = linhasNormalizadas.reduce((sum, linha) => sum + linha.debito, 0);
    const totalCredito = linhasNormalizadas.reduce((sum, linha) => sum + linha.credito, 0);

    if (totalDebito <= 0 || totalCredito <= 0) {
      return { success: false, error: 'Debito e credito devem ter valor maior que zero' };
    }

    if (Math.abs(totalDebito - totalCredito) > 0.01) {
      return {
        success: false,
        error: `Lancamento nao esta balanceado. Debito: ${totalDebito.toFixed(2)}, Credito: ${totalCredito.toFixed(2)}`,
      };
    }

    const anoReferenciaAtual = String(lancamentoAtual.referencia || '').match(/^LC[/-](\d{4})[/-]\d+$/i)?.[1];
    const referencia = anoReferenciaAtual === String(exercicio.ano)
      ? lancamentoAtual.referencia
      : await gerarReferenciaLancamento(lancamentoAtual.empresaId, data, exercicio.ano);

    const lancamentoAtualizado: LancamentoContabil = {
      ...lancamentoAtual,
      ...dados,
      id: lancamentoAtual.id,
      empresaId: lancamentoAtual.empresaId,
      referencia,
      exercicioEconomicoId: exercicio.id,
      anoExercicio: exercicio.ano,
      diarioId,
      data,
      descricao,
      numeroDocumento: dados.numeroDocumento?.trim() || dados.numeroDocumento || lancamentoAtual.numeroDocumento,
      observacoes: dados.observacoes?.trim() || undefined,
      documentosSuporte: Array.isArray(dados.documentosSuporte)
        ? dados.documentosSuporte
        : lancamentoAtual.documentosSuporte,
      origemLancamento,
      estado: estadoLancamento,
      dataLancamento: estadoLancamento === 'lancado'
        ? dados.dataLancamento || lancamentoAtual.dataLancamento || new Date().toISOString()
        : undefined,
      linhas: linhasNormalizadas,
      totalDebito,
      totalCredito,
    };

    const sincronizacaoActivos = await sincronizarActivosDoLancamento(lancamentoAtualizado, contasPorId);
    if (!sincronizacaoActivos.success) {
      return sincronizacaoActivos;
    }

    await kv.set(id, sincronizacaoActivos.data);
    return { success: true, data: sincronizacaoActivos.data };
  } catch (error: any) {
    console.error('[CONTABILIDADE] Erro ao atualizar lancamento:', error);
    return { success: false, error: error.message };
  }
}

export async function lancarLancamento(id: string) {
  try {
    const lancamento = await kv.get(id) as LancamentoContabil;
    
    if (!lancamento) {
      return { success: false, error: 'Lançamento não encontrado' };
    }
    
    if (lancamento.estado === 'lancado') {
      return { success: false, error: 'Lançamento já foi lançado' };
    }
    
    if (lancamento.estado === 'estornado') {
      return { success: false, error: 'Lancamento estornado nao pode ser lancado novamente' };
    }

    const exercicio = await exerciciosEconomicos.validarExercicioAberto(
      lancamento.empresaId,
      lancamento.data,
      'lancamento contabil',
    );

    lancamento.estado = 'lancado';
    lancamento.exercicioEconomicoId = exercicio.id;
    lancamento.anoExercicio = exercicio.ano;
    lancamento.dataLancamento = new Date().toISOString();

    await kv.set(id, lancamento);
    console.log(`[CONTABILIDADE] ✅ Lançamento lançado: ${lancamento.referencia}`);

    return { success: true, data: lancamento };
  } catch (error: any) {
    return { success: false, error: error.message };
  }
}

// ✅ Cada venda gera DOIS lancamentos independentes (receita + CMV), e ambos
// nascem em rascunho — nao existia nenhuma forma de lancar varios de uma vez,
// so um a um (POST /lancamentos/:id/lancar). Isto tornava facil um dos dois
// ficar esquecido em rascunho, o que um futuro "modelo fiscal automatico"
// que so leia lancamentos 'lancado' nao detectaria — reportaria receita ou
// CMV em falta silenciosamente. Esta funcao permite lancar tudo o que estiver
// em rascunho de uma vez, com o mesmo resumo/erros dos outros fluxos em massa.
export async function lancarLancamentosPendentes(
  empresaId: string,
  filtros: { dataInicio?: string; dataFim?: string } = {},
) {
  try {
    const lancamentos = await kv.getByPrefixWithKeys<LancamentoContabil>(`lancamento_${empresaId}_`);
    const pendentes = lancamentos.filter(({ value }) =>
      value?.estado === 'rascunho' &&
      filtrarPorPeriodo(value.data, filtros.dataInicio, filtros.dataFim)
    );

    let lancados = 0;
    let erros = 0;
    const errosDetalhados: string[] = [];

    for (const { key, value } of pendentes) {
      const resultado = await lancarLancamento(key);
      if (resultado.success) {
        lancados++;
      } else {
        erros++;
        errosDetalhados.push(`${value.referencia || key}: ${resultado.error || 'Erro desconhecido'}`);
      }
    }

    return {
      success: erros === 0,
      resumo: {
        total: pendentes.length,
        lancados,
        erros,
      },
      errosDetalhados,
      error: erros > 0
        ? `Alguns lancamentos nao puderam ser lancados. ${errosDetalhados[0] || ''}`.trim()
        : undefined,
    };
  } catch (error: any) {
    console.error('[CONTABILIDADE] Erro ao lancar lancamentos pendentes:', error);
    return { success: false, error: error.message };
  }
}

export async function estornarLancamento(id: string, opcoes: { usuarioId?: string; motivo?: string } = {}) {
  try {
    const lancamento = await kv.get(id) as LancamentoContabil;

    if (!lancamento) {
      return { success: false, error: 'Lançamento não encontrado' };
    }

    if (lancamento.estado !== 'lancado') {
      return { success: false, error: 'Apenas lançamentos já lançados podem ser estornados' };
    }

    const agora = new Date().toISOString();
    const dataEstorno = agora.slice(0, 10);

    // ✅ O lancamento original nunca e apagado/mutado nos valores — cria-se um
    // lancamento de reversao novo, datado no periodo corrente (nao retroactivo
    // ao periodo original), com debito/credito trocados. Isto preserva o
    // historico: um Balancete/DRE ja emitido para um periodo fechado continua
    // a bater certo mesmo que o lancamento venha a ser estornado mais tarde.
    const linhasEstorno = lancamento.linhas.map((linha) => ({
      id: `linha_${Date.now()}_${Math.random().toString(36).substr(2, 6)}`,
      contaId: linha.contaId,
      contaCodigo: linha.contaCodigo,
      contaNome: linha.contaNome,
      descricao: linha.descricao,
      debito: linha.credito,
      credito: linha.debito,
      // Nao copiar a ficha completa do activo (linha.activoFixo) para nao
      // reaccionar a sincronizacao de Activos Fixos (criar/actualizar ficha)
      // a partir de uma reversao — so o id, para satisfazer a validacao de
      // "linha com debito para conta de activo precisa de ficha preenchida".
      activoFixoId: linha.activoFixoId,
    })) as LinhaLancamento[];

    const resultadoEstorno = await criarLancamento({
      empresaId: lancamento.empresaId,
      diarioId: lancamento.diarioId,
      origemLancamento: 'manual',
      data: dataEstorno,
      referencia: '',
      descricao: `Estorno de ${lancamento.referencia} - ${lancamento.descricao}`,
      totalDebito: lancamento.totalCredito,
      totalCredito: lancamento.totalDebito,
      estado: 'lancado',
      criadoPor: opcoes.usuarioId || lancamento.criadoPor,
      linhas: linhasEstorno,
      estornoDeId: lancamento.id,
      ehEstorno: true,
    } as Omit<LancamentoContabil, 'id'>);

    if (!resultadoEstorno.success) {
      return { success: false, error: `Não foi possível criar o lançamento de estorno: ${resultadoEstorno.error}` };
    }

    lancamento.estado = 'estornado';
    lancamento.estornadoEm = agora;
    lancamento.estornadoPor = opcoes.usuarioId;
    lancamento.motivoEstorno = opcoes.motivo;
    lancamento.lancamentoEstornoId = resultadoEstorno.data.id;

    await kv.set(id, lancamento);
    console.log(`[CONTABILIDADE] ✅ Lançamento estornado: ${lancamento.referencia} -> reversão ${resultadoEstorno.data.referencia}`);

    return { success: true, data: lancamento, estorno: resultadoEstorno.data };
  } catch (error: any) {
    console.error('[CONTABILIDADE] ❌ Erro ao estornar lançamento:', error);
    return { success: false, error: error.message };
  }
}

export async function obterLancamentos(empresaId: string, filtros?: {
  dataInicio?: string;
  dataFim?: string;
  diarioId?: string;
  estado?: string;
}) {
  try {
    let lancamentos = (await kv.getByPrefix(`lancamento_${empresaId}_`)).map(
      (lancamento: LancamentoContabil) => ({
        ...lancamento,
        estado: lancamento.estado || 'rascunho',
      })
    );
    
    // Aplicar filtros
    if (filtros) {
      if (filtros.dataInicio) {
        lancamentos = lancamentos.filter((l: LancamentoContabil) => l.data >= filtros.dataInicio!);
      }
      if (filtros.dataFim) {
        lancamentos = lancamentos.filter((l: LancamentoContabil) => l.data <= filtros.dataFim!);
      }
      if (filtros.diarioId) {
        lancamentos = lancamentos.filter((l: LancamentoContabil) => l.diarioId === filtros.diarioId);
      }
      if (filtros.estado) {
        lancamentos = lancamentos.filter((l: LancamentoContabil) => l.estado === filtros.estado);
      }
    }
    
    // Ordenar por data decrescente
    lancamentos.sort((a: LancamentoContabil, b: LancamentoContabil) => 
      new Date(b.data).getTime() - new Date(a.data).getTime()
    );
    
    const diarios = await kv.getByPrefix(`diario_${empresaId}_`);
    const diariosPorId = new Map<string, Diario>(
      diarios.map((diario: Diario) => {
        const diarioNormalizado = normalizarDiario(diario);
        return [diarioNormalizado.id, diarioNormalizado] as [string, Diario];
      })
    );

    const lancamentosComDiario = lancamentos.map((lancamento: LancamentoContabil) => {
      const diario = diariosPorId.get(lancamento.diarioId);
      return {
        ...lancamento,
        diarioNome: diario ? `${diario.codigo} - ${diario.nome}` : undefined,
      };
    });

    return { success: true, data: lancamentosComDiario };
  } catch (error: any) {
    return { success: false, error: error.message };
  }
}

// ========================================
// GERAÇÃO AUTOMÁTICA DE LANÇAMENTOS
// ========================================

type TipoDocumentoContabil =
  | 'fatura'
  | 'factura'
  | 'factura_recibo'
  | 'factura_proforma'
  | 'venda_dinheiro'
  | 'recibo'
  | 'nota_credito'
  | 'nota_debito'
  | 'adiantamento'
  | 'compra'
  | 'pagamento'
  | 'movimento_stock'
  | 'salarios';

interface DocumentoFiscalLancamento {
  id: string;
  tipo: TipoDocumentoContabil | 'todos';
  tipoContabil: TipoDocumentoContabil;
  tipoLabel: string;
  tipoDocumentoOriginal?: string;
  condicaoPagamento?: string;
  numeroDocumento: string;
  data: string;
  clienteId?: string;
  clienteNome?: string;
  fornecedorId?: string;
  fornecedorNome?: string;
  entidadeNome?: string;
  estado?: string;
  totalGeral: number;
  totalIVA: number;
  subtotal: number;
  linhas: any[];
  metodoPagamento?: string;
  contaBancaria?: string;
  referenciaPagamento?: string;
  documentosRelacionados?: Array<{
    id: string;
    tipo: TipoDocumentoContabil;
    tipoLabel: string;
    numeroDocumento: string;
    data: string;
    totalGeral: number;
  }>;
  totalRecebido?: number;
  lancamentoExistente?: {
    id: string;
    referencia: string;
    estado: LancamentoContabil['estado'];
  };
}

interface FiltrosDocumentosFiscais {
  tipo?: string;
  dataInicio?: string;
  dataFim?: string;
  numeroDocumento?: string;
  documentoIds?: string[];
}

function normalizarTipoDocumentoContabil(tipo?: string): TipoDocumentoContabil {
  const valor = (tipo || '').trim().toLowerCase().replace(/-/g, '_');

  if (valor === 'factura' || valor === 'fatura') return 'fatura';
  if (valor === 'factura_recibo' || valor === 'fatura_recibo') return 'factura_recibo';
  if (valor === 'factura_proforma' || valor === 'fatura_proforma' || valor === 'proforma') return 'factura_proforma';
  if (valor === 'vd' || valor === 'venda_dinheiro' || valor === 'vendas_dinheiro') return 'venda_dinheiro';
  if (valor === 'nota_credito' || valor === 'nota_de_credito' || valor === 'credito') return 'nota_credito';
  if (valor === 'nota_debito' || valor === 'nota_de_debito' || valor === 'debito') return 'nota_debito';
  if (valor === 'adiantamento') return 'adiantamento';
  if (valor === 'recibo') return 'recibo';
  if (valor === 'compra') return 'compra';
  if (valor === 'pagamento') return 'pagamento';
  if (valor === 'movimento_stock' || valor === 'stock' || valor === 'inventario_stock') return 'movimento_stock';
  if (valor === 'salarios' || valor === 'salario' || valor === 'folha_pagamento' || valor === 'folha_salarial') return 'salarios';

  return 'fatura';
}

function documentoEhProformaContabil(tipo?: string): boolean {
  return normalizarTipoDocumentoContabil(tipo) === 'factura_proforma';
}

function obterTipoMapeamentoPrincipal(tipo: TipoDocumentoContabil): string {
  if (tipo === 'factura_recibo' || tipo === 'venda_dinheiro' || tipo === 'nota_credito' || tipo === 'nota_debito') {
    return 'fatura';
  }

  return tipo === 'factura' ? 'fatura' : tipo;
}

function obterDataDocumentoFiscal(documento: any): string {
  return String(
    documento.dataEmissao ||
      documento.dataPagamento ||
      documento.dataDocumento ||
      documento.data ||
      documento.dataCriacao ||
      ''
  ).slice(0, 10);
}

function documentoFiscalAtivo(documento: any): boolean {
  if (documento?.geraLancamentoContabil === false) return false;
  const estado = String(documento.estado || '').trim().toLowerCase();
  return !['anulado', 'anulada', 'cancelado', 'cancelada', 'estornado'].includes(estado);
}

function numeroIncluiFiltro(numeroDocumento: string, filtro?: string): boolean {
  const termo = (filtro || '').trim().toLowerCase();
  return !termo || numeroDocumento.toLowerCase().includes(termo);
}

function filtrarPorPeriodo(data: string, dataInicio?: string, dataFim?: string): boolean {
  if (!data) return false;
  if (dataInicio && data < dataInicio) return false;
  if (dataFim && data > dataFim) return false;
  return true;
}

function normalizarSubtotalDocumentoFiscal(
  totalGeral: number,
  totalIVA: number,
  subtotalInformado: unknown
): number {
  const subtotal = Number(subtotalInformado);
  if (!Number.isFinite(subtotal)) {
    return Math.max(totalGeral - totalIVA, 0);
  }

  // ✅ Sem exigir totalIVA > 0 aqui: uma empresa isenta/ISPC (0% IVA) com
  // desconto de linha tambem cai neste caso (subtotal bruto + 0 > totalGeral
  // liquido), e antes ficava sem normalizar por causa desta condicao.
  if (subtotal + totalIVA > totalGeral + 0.01) {
    return Math.max(totalGeral - totalIVA, 0);
  }

  return subtotal;
}

function obterValoresLinhasDocumentoFiscal(linhas: any[] | undefined, campos: string[]): string[] {
  return (linhas || [])
    .flatMap((linha) => campos.map((campo) => String(linha?.[campo] || '').trim()))
    .filter(Boolean);
}

function metodoPagamentoEhNumerario(metodo?: string): boolean {
  const valor = normalizarTextoDocumento(metodo);
  return ['numerario', 'dinheiro', 'cash'].includes(valor);
}

function obterMetodoPagamentoDocumentoFiscal(documento: any, linhas?: any[]): string {
  const valores = [
    String(documento?.formaPagamento || '').trim(),
    String(documento?.meioPagamento || '').trim(),
    String(documento?.metodoPagamento || '').trim(),
    String(documento?.tipoPagamento || '').trim(),
    ...obterValoresLinhasDocumentoFiscal(linhas, ['formaPagamento', 'meioPagamento', 'metodoPagamento', 'tipoPagamento']),
  ].filter(Boolean);

  return (
    valores.find(metodoPagamentoUsaBanco) ||
    valores.find((valor) => !metodoPagamentoEhNumerario(valor)) ||
    valores[0] ||
    ''
  );
}

function obterContaBancariaDocumentoFiscal(documento: any, linhas?: any[]): string {
  return String(
    documento?.contaBancaria ||
      documento?.banco ||
      obterValoresLinhasDocumentoFiscal(linhas, ['contaBancaria', 'banco'])[0] ||
      ''
  ).trim();
}

function criarDocumentoFiscalLancamento(
  documento: any,
  tipoContabil: TipoDocumentoContabil,
  tipoLabel: string
): DocumentoFiscalLancamento {
  const totalGeral = Number(documento.totalGeral ?? documento.valor ?? 0) || 0;
  const totalIVA = Number(documento.totalImpostos ?? documento.totalIVA ?? 0) || 0;
  const subtotal = normalizarSubtotalDocumentoFiscal(totalGeral, totalIVA, documento.subtotal);
  const clienteNome = documento.cliente?.nome || documento.clienteNome || '';
  const fornecedorNome = documento.fornecedor?.nome || documento.fornecedorNome || '';

  const linhasDocumento = documento.itens || documento.linhas || documento.pagamentos || [];

  return {
    id: documento.id,
    tipo: tipoContabil,
    tipoContabil,
    tipoLabel,
    tipoDocumentoOriginal: documento.tipoDocumento || documento.tipo || documento.documentoTipo || '',
    condicaoPagamento: documento.condicaoPagamento || documento.condicao || '',
    numeroDocumento: String(documento.numeroDocumento || documento.numero || documento.referencia || ''),
    data: obterDataDocumentoFiscal(documento),
    clienteId: documento.clienteId,
    clienteNome,
    fornecedorId: documento.fornecedorId,
    fornecedorNome,
    entidadeNome: fornecedorNome || clienteNome,
    estado: documento.estado,
    totalGeral,
    totalIVA,
    subtotal,
    linhas: linhasDocumento,
    metodoPagamento: obterMetodoPagamentoDocumentoFiscal(documento, linhasDocumento),
    contaBancaria: obterContaBancariaDocumentoFiscal(documento, linhasDocumento),
    referenciaPagamento:
      documento.referenciaPagamento ||
      documento.referenciaTransferencia ||
      documento.referencia ||
      documento.numeroCheque ||
      '',
    };
}

function arredondarMoeda(valor: number) {
  return Math.round(((Number(valor) || 0) + Number.EPSILON) * 100) / 100;
}

function obterValorLinhaDocumento(linha: any) {
  return Number(
    linha?.valorPago ??
      linha?.valorRecebido ??
      linha?.valor ??
      linha?.totalGeral ??
      linha?.total ??
      linha?.montante ??
      0
  ) || 0;
}

function linhaDocumentoEhAdiantamento(linha: any) {
  const tipo = normalizarTipoDocumentoContabil(linha?.tipoDocumento || linha?.tipo || linha?.documentoTipo);
  return tipo === 'adiantamento' || Boolean(linha?.adiantamentoId || linha?.adiantamentoNumero);
}

function obterValorAdiantamentosNoDocumento(documento: { linhas?: any[] }) {
  return arredondarMoeda(
    (documento.linhas || [])
      .filter(linhaDocumentoEhAdiantamento)
      .reduce((sum, linha) => sum + obterValorLinhaDocumento(linha), 0)
  );
}

function normalizarTextoDocumento(valor?: string) {
  return (valor || '')
    .normalize('NFD')
    .replace(/[\u0300-\u036f]/g, '')
    .trim()
    .toLowerCase();
}

function documentoCompraEhProntoPagamento(documento: {
  tipoDocumentoOriginal?: string;
  tipoLabel?: string;
  condicaoPagamento?: string;
  estado?: string;
  liquidacaoImediata?: boolean;
}) {
  // ✅ Quando o chamador (contabilizarCompraOperacional) ja indica
  // explicitamente o perfil server-derivado do documento de compra
  // (obterPerfilDocumentoCompra(...).liquidacaoImediata), usa esse valor em
  // vez de tentar adivinhar por substring no condicaoPagamento — um campo que
  // vem directamente do corpo do pedido do cliente e podia redireccionar o
  // credito do lancamento de "a pagar" para "caixa/banco" sem validacao.
  if (typeof documento.liquidacaoImediata === 'boolean') {
    return documento.liquidacaoImediata;
  }

  const texto = normalizarTextoDocumento(
    [
      documento.tipoDocumentoOriginal,
      documento.tipoLabel,
      documento.condicaoPagamento,
      documento.estado,
    ].join(' ')
  );

  return (
    texto.includes('recibo') ||
    texto === 'vd' ||
    texto.includes(' vd ') ||
    texto.includes('venda dinheiro') ||
    texto.includes('venda_dinheiro') ||
    texto.includes('venda a dinheiro') ||
    texto.includes('pronto_pagamento') ||
    texto.includes('pronto pagamento') ||
    texto.includes('a vista') ||
    texto.includes('avista') ||
    texto.includes('vista')
  );
}

function documentoCompraEhNotaCredito(documento: {
  tipoDocumentoOriginal?: string;
  tipoLabel?: string;
}) {
  return normalizarTextoDocumento(`${documento.tipoDocumentoOriginal || ''} ${documento.tipoLabel || ''}`).includes('nota credito');
}

function obterNomeLinhaDocumento(linha: any) {
  return String(
    linha?.descricao ||
      linha?.nome ||
      linha?.artigoNome ||
      linha?.produtoNome ||
      linha?.designacao ||
      linha?.codigo ||
      ''
  ).trim();
}

function obterResumoArtigosDocumento(documento: { linhas?: any[] }) {
  const nomes = (documento.linhas || [])
    .map(obterNomeLinhaDocumento)
    .filter(Boolean)
    .filter((nome, index, lista) => lista.indexOf(nome) === index);

  if (nomes.length === 0) return '';
  if (nomes.length === 1) return nomes[0];
  if (nomes.length === 2) return `${nomes[0]} e ${nomes[1]}`;
  return `${nomes.slice(0, 2).join(', ')} e mais ${nomes.length - 2}`;
}

function obterDescricaoMercadoriaLancamento(
  prefixo: 'Venda' | 'Compra' | 'Nota de credito',
  documento: { linhas?: any[]; numeroDocumento: string }
) {
  const resumoArtigos = obterResumoArtigosDocumento(documento);
  return resumoArtigos
    ? `${prefixo} de ${resumoArtigos}`
    : `${prefixo} ${documento.numeroDocumento}`;
}

function linhaContabilEhServico(linha: any): boolean {
  const codigo = normalizarTextoDocumento(
    linha?.codigo ||
      linha?.artigoCodigo ||
      linha?.produtoCodigo ||
      linha?.artigoId ||
      linha?.produtoId ||
      ''
  );
  const tipo = normalizarTextoDocumento(
    linha?.tipoArtigo ||
      linha?.artigoTipo ||
      linha?.tipoProduto ||
      linha?.artigo?.tipo ||
      linha?.produto?.tipo ||
      linha?.item?.tipo ||
      linha?.tipo ||
      linha?.categoriaArtigo ||
      ''
  );

  return tipo === 'servico' || tipo.includes('servico') || /^serv[._-]/.test(codigo);
}

function calcularBaseLinhaContabil(linha: any): number {
  const quantidade = Number(linha?.quantidade || 0);
  const precoUnitario = Number(linha?.precoUnitario ?? linha?.valorUnitario ?? linha?.preco ?? 0);
  const desconto = Number(linha?.desconto || 0);
  const taxaIva = Math.max(0, Number(linha?.iva ?? linha?.taxaIva ?? 0)) / 100;

  if (quantidade > 0 && precoUnitario >= 0) {
    const bruto = quantidade * precoUnitario;
    if (linha?.ivaIncluso && taxaIva > 0) {
      const baseBruta = bruto / (1 + taxaIva);
      return arredondarMoeda(Math.max(0, baseBruta - (baseBruta * desconto) / 100));
    }

    return arredondarMoeda(Math.max(0, bruto - (bruto * desconto) / 100));
  }

  const valorLinha = Number(linha?.subtotalSemIva ?? linha?.baseTributavel ?? linha?.valorBase ?? linha?.subtotal ?? linha?.total ?? 0);
  if (valorLinha <= 0) return 0;

  if (linha?.ivaIncluso && taxaIva > 0) {
    return arredondarMoeda(valorLinha / (1 + taxaIva));
  }

  return arredondarMoeda(valorLinha);
}

// ✅ Documentos de crédito (nota de crédito de venda, nota de crédito de
// fornecedor) guardam subtotal/totalGeral/totalImpostos como valores NEGATIVOS
// para indicar uma redução — mas as linhas de lançamento em si são sempre
// magnitudes positivas (o sinal débito/crédito já é decidido por `sentido` em
// quem chama esta função). Por isso trabalha-se sempre em valor absoluto aqui.
function calcularSubtotaisContabilPorTipo(documento: { linhas?: any[]; subtotal: number }) {
  const linhas = Array.isArray(documento.linhas) ? documento.linhas : [];
  const subtotalDocumento = Math.abs(arredondarMoeda(Number(documento.subtotal || 0)));

  if (linhas.length === 0) {
    return { produtos: subtotalDocumento, servicos: 0 };
  }

  let produtos = 0;
  let servicos = 0;

  linhas.forEach((linha) => {
    const valor = calcularBaseLinhaContabil(linha);
    if (linhaContabilEhServico(linha)) {
      servicos += valor;
    } else {
      produtos += valor;
    }
  });

  const totalCalculado = arredondarMoeda(produtos + servicos);
  if (totalCalculado <= 0) {
    return { produtos: subtotalDocumento, servicos: 0 };
  }

  const fator = subtotalDocumento > 0 ? subtotalDocumento / totalCalculado : 1;
  return {
    produtos: arredondarMoeda(produtos * fator),
    servicos: arredondarMoeda(servicos * fator),
  };
}

async function obterMapeamentosParaLancamentoAutomatico(
  empresaId: string
): Promise<MapeamentoContabil[]> {
  let mapeamentos = await kv.getByPrefix(`mapeamento_${empresaId}_`);

  if (mapeamentos.length === 0) {
    const inicializacao = await inicializarMapeamentosBase(empresaId);
    if (inicializacao.success) {
      mapeamentos = inicializacao.data || [];
    }
  }

  const contas = await kv.getByPrefix(`conta_${empresaId}_`) as ContaContabil[];
  const findConta = (...codigos: string[]) =>
    encontrarContaLancavelPorCodigoOuPrefixo(contas, codigos) ||
    (contas.find((c: ContaContabil) => codigos.includes(c.codigo)) as ContaContabil | undefined)?.id;

  const mapeamentosBase: MapeamentoContabil[] = [
    {
      id: `mapeamento_fallback_${empresaId}_fatura`,
      empresaId,
      tipoDocumento: 'fatura',
      contaVendas: findConta('7.1.1', '71.01'),
      contaReceber: findConta('4.1.1', '21.01'),
      contaIVALiquidado: findConta('4.4.3.3.1', '24.01'),
      contaEstoque: findConta('2.2.3', '31.01'),
    },
    {
      id: `mapeamento_fallback_${empresaId}_compra`,
      empresaId,
      tipoDocumento: 'compra',
      contaCompras: findConta('2.2.3', '31.01'),
      contaPagar: findConta('4.2.1', '22.01'),
      contaIVADedutivel: findConta('4.4.3.2.1', '24.02'),
      contaEstoque: findConta('2.2.3', '31.01'),
    },
    {
      id: `mapeamento_fallback_${empresaId}_recibo`,
      empresaId,
      tipoDocumento: 'recibo',
      contaCaixa: findConta('1.1', '11.01'),
      contaBanco: findConta('1.2.1', '12.01', '1.2', '12'),
      contaReceber: findConta('4.1.1', '21.01'),
    },
    {
      id: `mapeamento_fallback_${empresaId}_adiantamento`,
      empresaId,
      tipoDocumento: 'adiantamento',
      contaCaixa: findConta('1.1', '11.01'),
      contaBanco: findConta('1.2.1', '12.01', '1.2', '12'),
      contaReceber: findConta('4.1.9', '21.09', '4.1.1'),
    },
    {
      id: `mapeamento_fallback_${empresaId}_pagamento`,
      empresaId,
      tipoDocumento: 'pagamento',
      contaCaixa: findConta('1.1', '11.01'),
      contaBanco: findConta('1.2.1', '12.01', '1.2', '12'),
      contaPagar: findConta('4.2.1', '22.01'),
      contaAdiantamentoFornecedor: findConta('4.2.9'),
    },
    {
      id: `mapeamento_fallback_${empresaId}_movimento_stock`,
      empresaId,
      tipoDocumento: 'movimento_stock',
      contaEstoque: findConta('2.2.3', '31.01'),
      contaCMV: findConta('6.1.1.2', '61.01'),
      contaPerdasStock: findConta('6.8.4.2', '6.8.4', '6.1.1.2'),
      contaRegularizacaoStock: findConta('7.4.1.1', '7.4.1'),
    },
    {
      id: `mapeamento_fallback_${empresaId}_salarios`,
      empresaId,
      tipoDocumento: 'salarios',
      contaGastoRemuneracoes: findConta('6.2.2'),
      contaGastoEncargosSociais: findConta('6.2.3'),
      contaINSSPagar: findConta('4.4.9'),
      contaIRPSPagar: findConta('4.4.2.1'),
      contaOutrosDescontosPessoal: findConta('4.6.2.9'),
      contaRemuneracoesPagar: findConta('4.6.2.2'),
    },
  ];

  const tiposExistentes = new Set(
    mapeamentos.map((mapeamento: MapeamentoContabil) =>
      normalizarTipoDocumentoContabil(mapeamento.tipoDocumento)
    )
  );

  return [
    ...mapeamentos,
    ...mapeamentosBase.filter((mapeamento) =>
      !tiposExistentes.has(normalizarTipoDocumentoContabil(mapeamento.tipoDocumento))
    ),
  ];
}

function encontrarMapeamento(
  mapeamentos: MapeamentoContabil[],
  tipo: string
): MapeamentoContabil | undefined {
  return mapeamentos.find((m) => normalizarTipoDocumentoContabil(m.tipoDocumento) === normalizarTipoDocumentoContabil(tipo));
}

// ========================================
// CONTABILIZACAO DA FOLHA DE PAGAMENTO (RH)
// ========================================
//
// Lancamento unico por folha processada (1 colaborador + 1 mes), gerado a
// pedido do utilizador a partir do ecra de Processamento Salarial. Usa
// criarLancamento() directamente (nao gerarLancamentoAutomatico(), que esta
// desenhada a volta de documentos fiscais com cliente/fornecedor/IVA e nao
// encaixa numa folha de salarios) — a protecao contra duplicados de
// criarLancamento (por documentoId+tipoDocumento) evita relancar a mesma
// folha duas vezes.
//
// Partida dupla (contas do POC mocambicano ja semeadas em ambas as listas
// de mapeamentosBase acima):
//   Debito  6.2.2 Remuneracoes dos trabalhadores   = totalRemuneracoes
//   Debito  6.2.3 Encargos sobre remuneracoes      = totalEncargosPatronais (INSS patronal)
//   Credito 4.4.9 Contribuicoes para o INSS        = descontoINSSTrabalhador + totalEncargosPatronais
//   Credito 4.4.2.1 Impostos retidos na fonte (trabalho dependente) = descontoIRPS
//   Credito 4.6.2.9 Outras operacoes com os trabalhadores = outros descontos manuais (emprestimos, pensao alimenticia, etc.)
//   Credito 4.6.2.2 Remuneracoes a pagar aos trabalhadores = salarioLiquido
// Debito e credito ficam sempre balanceados por construcao (ver
// supabase/functions/server/calculo-salarial.tsx).
interface FolhaPagamentoParaLancamento {
  id: string;
  empresaId: string;
  colaboradorId: string;
  ano: number;
  mes: number;
  numeroRecibo: string;
  totalRemuneracoes: number;
  totalDescontos: number;
  totalEncargosPatronais: number;
  descontoINSSTrabalhador: number;
  descontoIRPS: number;
  salarioLiquido: number;
}

export async function gerarLancamentoFolhaPagamento(
  empresaId: string,
  folha: FolhaPagamentoParaLancamento,
  colaboradorNome: string,
  criadoPor: string,
) {
  try {
    const mapeamentos = await obterMapeamentosParaLancamentoAutomatico(empresaId);
    const mapeamento = encontrarMapeamento(mapeamentos, 'salarios');

    const contasEmFalta: string[] = [];
    if (!mapeamento?.contaGastoRemuneracoes) contasEmFalta.push('Gasto com Remunerações (6.2.2)');
    if (!mapeamento?.contaGastoEncargosSociais) contasEmFalta.push('Encargos sobre Remunerações (6.2.3)');
    if (!mapeamento?.contaINSSPagar) contasEmFalta.push('INSS a Pagar (4.4.9)');
    if (!mapeamento?.contaIRPSPagar) contasEmFalta.push('IRPS a Pagar (4.4.2.1)');
    if (!mapeamento?.contaRemuneracoesPagar) contasEmFalta.push('Remunerações a Pagar (4.6.2.2)');

    if (contasEmFalta.length > 0) {
      return {
        success: false,
        error: `Configure as contas de salários em Mapeamentos antes de contabilizar: ${contasEmFalta.join(', ')}`,
      };
    }

    const diarios = (await kv.getByPrefix(`diario_${empresaId}_`)).map((d: Diario) => normalizarDiario(d));
    const diarioSalarios = diarios.find((d) => d.codigo.trim().toUpperCase() === 'SAL');
    if (!diarioSalarios) {
      return { success: false, error: 'Diário de Salários (SAL) não encontrado — abra o módulo de Contabilidade uma vez para o inicializar' };
    }

    const outrosDescontos = arredondarMoeda(
      folha.totalDescontos - folha.descontoINSSTrabalhador - folha.descontoIRPS,
    );

    const linhas: Omit<LinhaLancamento, 'id' | 'contaCodigo' | 'contaNome'>[] = [
      {
        contaId: mapeamento!.contaGastoRemuneracoes!,
        descricao: `Remunerações — ${colaboradorNome} (${folha.mes}/${folha.ano})`,
        debito: arredondarMoeda(folha.totalRemuneracoes),
        credito: 0,
      },
    ];

    if (folha.totalEncargosPatronais > 0) {
      linhas.push({
        contaId: mapeamento!.contaGastoEncargosSociais!,
        descricao: `Encargos sobre remunerações (INSS patronal) — ${colaboradorNome} (${folha.mes}/${folha.ano})`,
        debito: arredondarMoeda(folha.totalEncargosPatronais),
        credito: 0,
      });
    }

    const totalINSSPagar = arredondarMoeda(folha.descontoINSSTrabalhador + folha.totalEncargosPatronais);
    if (totalINSSPagar > 0) {
      linhas.push({
        contaId: mapeamento!.contaINSSPagar!,
        descricao: `INSS a pagar — ${colaboradorNome} (${folha.mes}/${folha.ano})`,
        debito: 0,
        credito: totalINSSPagar,
      });
    }

    if (folha.descontoIRPS > 0) {
      linhas.push({
        contaId: mapeamento!.contaIRPSPagar!,
        descricao: `IRPS retido na fonte — ${colaboradorNome} (${folha.mes}/${folha.ano})`,
        debito: 0,
        credito: folha.descontoIRPS,
      });
    }

    if (outrosDescontos > 0) {
      if (!mapeamento!.contaOutrosDescontosPessoal) {
        return {
          success: false,
          error: 'Configure a conta "Outras Operações com os Trabalhadores" (4.6.2.9) em Mapeamentos — existem descontos manuais nesta folha (empréstimos, pensão alimentícia, etc.)',
        };
      }
      linhas.push({
        contaId: mapeamento!.contaOutrosDescontosPessoal,
        descricao: `Outros descontos — ${colaboradorNome} (${folha.mes}/${folha.ano})`,
        debito: 0,
        credito: outrosDescontos,
      });
    }

    linhas.push({
      contaId: mapeamento!.contaRemuneracoesPagar!,
      descricao: `Líquido a pagar — ${colaboradorNome} (${folha.mes}/${folha.ano})`,
      debito: 0,
      credito: arredondarMoeda(folha.salarioLiquido),
    });

    const ultimoDiaDoMes = new Date(folha.ano, folha.mes, 0).toISOString().slice(0, 10);

    return await criarLancamento({
      empresaId,
      diarioId: diarioSalarios.id,
      tipoDocumento: 'salarios',
      documentoId: `folha_pagamento_${folha.id}`,
      numeroDocumento: folha.numeroRecibo,
      entidadeNome: colaboradorNome,
      data: ultimoDiaDoMes,
      referencia: '',
      descricao: `Processamento salarial ${String(folha.mes).padStart(2, '0')}/${folha.ano} — ${colaboradorNome}`,
      criadoPor,
      linhas: linhas as LinhaLancamento[],
      totalDebito: 0,
      totalCredito: 0,
    } as any);
  } catch (error: any) {
    return { success: false, error: error.message };
  }
}

function contaObrigatoria(contaId: string | undefined, mensagem: string): string {
  if (!contaId) {
    throw new Error(mensagem);
  }

  return contaId;
}

function metodoPagamentoUsaBanco(metodo?: string): boolean {
  const valor = (metodo || '')
    .normalize('NFD')
    .replace(/[\u0300-\u036f]/g, '')
    .trim()
    .toLowerCase();
  const compacto = valor.replace(/[\s_-]+/g, '');

  return [
    'transfer',
    'banco',
    'bank',
    'deposit',
    'cheque',
    'cartao',
    'card',
    'visa',
    'mastercard',
    'pos',
    'tpa',
    'multicaixa',
    'emola',
    'e-mola',
    'e_mola',
    'mpesa',
    'm-pesa',
    'm_pesa',
    'mkesh',
    'm-kesh',
    'm_kesh',
  ].some((termo) => valor.includes(termo) || compacto.includes(termo.replace(/[\s_-]+/g, '')));
}

function normalizarMetodoPagamentoTesouraria(metodo?: string) {
  const texto = normalizarTextoDocumento(metodo).replace(/[_-]/g, ' ');
  return {
    texto,
    compacto: texto.replace(/\s+/g, ''),
  };
}

function obterRotuloMetodoPagamento(metodo?: string): string {
  const { compacto, texto } = normalizarMetodoPagamentoTesouraria(metodo);
  if (!compacto) return 'meio de pagamento';
  if (compacto.includes('mpesa')) return 'M-Pesa';
  if (compacto.includes('emola')) return 'E-Mola';
  if (compacto.includes('mkesh')) return 'M-Kesh';
  if (
    compacto.includes('cartao') ||
    compacto.includes('card') ||
    compacto.includes('visa') ||
    compacto.includes('mastercard') ||
    compacto.includes('tpa') ||
    compacto.includes('pos') ||
    compacto.includes('multicaixa')
  ) {
    return 'Cartao/TPA';
  }
  if (texto.includes('cheque')) return 'Cheque';
  if (texto.includes('transfer') || texto.includes('deposit') || texto.includes('banco')) {
    return 'Transferencia/Deposito bancario';
  }

  return metodo?.trim() || 'meio de pagamento';
}

function metodoPagamentoExigeContaPropria(metodo?: string): boolean {
  const { compacto } = normalizarMetodoPagamentoTesouraria(metodo);
  if (!compacto) return false;

  return [
    'mpesa',
    'emola',
    'mkesh',
    'cartao',
    'card',
    'visa',
    'mastercard',
    'tpa',
    'pos',
    'multicaixa',
  ].some((termo) => compacto.includes(termo));
}

function tokensContaPorMetodoPagamento(metodo?: string): string[] {
  const { compacto } = normalizarMetodoPagamentoTesouraria(metodo);
  if (compacto.includes('mpesa')) return ['mpesa', 'm pesa'];
  if (compacto.includes('emola')) return ['emola', 'e mola'];
  if (compacto.includes('mkesh')) return ['mkesh', 'm kesh'];
  if (compacto.includes('visa')) return ['visa', 'cartao', 'tpa'];
  if (compacto.includes('mastercard')) return ['mastercard', 'cartao', 'tpa'];
  if (compacto.includes('multicaixa')) return ['multicaixa', 'cartao', 'tpa'];
  if (compacto.includes('tpa') || compacto.includes('pos') || compacto.includes('cartao') || compacto.includes('card')) {
    return ['tpa', 'pos', 'cartao', 'cartão', 'visa', 'mastercard', 'multicaixa'];
  }

  return [];
}

function contaCorrespondeMetodoPagamento(
  conta: Pick<ContaContabil, 'nome' | 'codigo'> | undefined,
  metodo?: string
): boolean {
  if (!conta) return false;
  const tokens = tokensContaPorMetodoPagamento(metodo);
  if (tokens.length === 0) return false;

  const textoConta = normalizarTextoDocumento(`${conta.codigo} ${conta.nome}`).replace(/[_-]/g, ' ');
  const compactoConta = textoConta.replace(/\s+/g, '');

  return tokens.some((token) => {
    const tokenNormalizado = normalizarTextoDocumento(token).replace(/[_-]/g, ' ');
    const tokenCompacto = tokenNormalizado.replace(/\s+/g, '');
    return textoConta.includes(tokenNormalizado) || compactoConta.includes(tokenCompacto);
  });
}

function obterMetodoPagamentoDocumentoLancamento(documento?: {
  metodoPagamento?: string;
  linhas?: any[];
}): string {
  const valores = [
    String(documento?.metodoPagamento || '').trim(),
    ...obterValoresLinhasDocumentoFiscal(documento?.linhas, ['formaPagamento', 'meioPagamento', 'metodoPagamento', 'tipoPagamento']),
  ].filter(Boolean);

  return (
    valores.find(metodoPagamentoUsaBanco) ||
    valores.find((valor) => !metodoPagamentoEhNumerario(valor)) ||
    valores[0] ||
    ''
  );
}

function obterContaBancariaDocumentoLancamento(documento?: {
  contaBancaria?: string;
  linhas?: any[];
}): string {
  return String(
    documento?.contaBancaria ||
      obterValoresLinhasDocumentoFiscal(documento?.linhas, ['contaBancaria', 'banco'])[0] ||
      ''
  ).trim();
}

function normalizarNomeContaFinanceira(valor?: string): string {
  return normalizarTextoDocumento(valor)
    .replace(/\b(banco|bank|conta|contas|depositos|deposito|ordem|a|de|da|do|das|dos)\b/g, ' ')
    .replace(/\s+/g, ' ')
    .trim();
}

function contaCorrespondeBancoDocumento(
  conta: Pick<ContaContabil, 'nome' | 'codigo'>,
  bancoDocumento?: string
): boolean {
  const banco = normalizarNomeContaFinanceira(bancoDocumento);
  if (!banco) return false;

  const nomeConta = normalizarNomeContaFinanceira(`${conta.codigo} ${conta.nome}`);
  if (!nomeConta) return false;
  if (nomeConta.includes(banco) || banco.includes(nomeConta)) return true;

  const tokensBanco = banco.split(' ').filter((token) => token.length >= 3);
  if (tokensBanco.length === 0) return false;

  const tokensEncontrados = tokensBanco.filter((token) => nomeConta.includes(token)).length;
  return tokensEncontrados > 0 && tokensEncontrados / tokensBanco.length >= 0.5;
}

function documentoUsaBanco(documento?: { metodoPagamento?: string; contaBancaria?: string; linhas?: any[] }): boolean {
  return Boolean(
    metodoPagamentoUsaBanco(obterMetodoPagamentoDocumentoLancamento(documento)) ||
      obterContaBancariaDocumentoLancamento(documento)
  );
}

function encontrarContaLancavelPorCodigoOuPrefixo(
  contas: ContaContabil[],
  codigos: string[]
): string | undefined {
  const contasOrdenadas = [...contas].sort((a, b) => compararCodigosContas(a.codigo, b.codigo));
  const correspondeCodigo = (conta: ContaContabil) =>
    codigos.some((codigo) => conta.codigo === codigo || conta.codigo.startsWith(`${codigo}.`));

  return (
    contasOrdenadas.find((conta) => correspondeCodigo(conta) && conta.aceitaLancamentos !== false)?.id ||
    contasOrdenadas.find(correspondeCodigo)?.id
  );
}

function encontrarContaReceitaServicos(contas: ContaContabil[]): string | undefined {
  const contasOrdenadas = [...contas].sort((a, b) => compararCodigosContas(a.codigo, b.codigo));

  const contaServico = contasOrdenadas.find((conta) => {
    const nome = normalizarTextoDocumento(conta.nome);
    const codigo = String(conta.codigo || '').trim();
    const contaNaoEhReceitaServico =
      nome.includes('iva') ||
      nome.includes('desconto') ||
      nome.includes('abatimento') ||
      nome.includes('devolucao');

    return (
      conta.ativo !== false &&
      conta.aceitaLancamentos !== false &&
      codigo.startsWith('7.2') &&
      !contaNaoEhReceitaServico &&
      (codigo === '7.2.2' || nome.includes('prestacao') || nome.includes('servico'))
    );
  });

  return contaServico?.id;
}

async function garantirContaReceitaServicos(
  empresaId: string,
  contas: ContaContabil[]
): Promise<string | undefined> {
  const contaExistente = encontrarContaReceitaServicos(contas);
  if (contaExistente) return contaExistente;

  const contaPai = contas.find((conta) => conta.codigo === '7.2');
  if (!contaPai) return undefined;

  const agora = new Date().toISOString();
  const contaServico: ContaContabil = {
    id: `conta_${empresaId}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
    empresaId,
    codigo: '7.2.2',
    nome: 'Serviços prestados',
    classe: 7,
    tipo: 'rendimento',
    parentId: contaPai.id,
    nivel: calcularNivelConta('7.2.2'),
    aceitaLancamentos: true,
    grupoRelatorio: 'Rendimentos',
    isSystem: true,
    ativo: true,
    dataCriacao: agora,
  };

  await kv.set(contaServico.id, contaServico);
  contas.push(contaServico);

  if (contaPai.aceitaLancamentos !== false) {
    await kv.set(contaPai.id, { ...contaPai, aceitaLancamentos: false });
  }

  return contaServico.id;
}

function resolverContaLancavelPorId(
  contas: ContaContabil[],
  contaId?: string
): string | undefined {
  if (!contaId) return undefined;

  const conta = contas.find((item) => item.id === contaId);
  // ✅ Conta referenciada ja nao existe (apagada/id desactualizado) — devolver
  // undefined em vez do contaId antigo, para a cadeia de fallback (||) do
  // chamador conseguir tentar a proxima opcao em vez de travar aqui com uma
  // conta invalida.
  if (!conta) return undefined;
  if (conta.aceitaLancamentos !== false) return contaId;

  return [...contas]
    .sort((a, b) => compararCodigosContas(a.codigo, b.codigo))
    .find((item) =>
      item.codigo.startsWith(`${conta.codigo}.`) &&
      item.aceitaLancamentos !== false
    )?.id;
}

function encontrarContaBancoPorDocumento(
  contas: ContaContabil[],
  documento: { contaBancaria?: string; linhas?: any[] }
): string | undefined {
  const bancoDocumento = obterContaBancariaDocumentoLancamento(documento);
  if (!bancoDocumento) return undefined;

  const contaPorReferencia = contas.find((conta) =>
    conta.aceitaLancamentos !== false &&
    (conta.id === bancoDocumento || conta.codigo === bancoDocumento)
  );
  if (contaPorReferencia) return contaPorReferencia.id;

  return [...contas]
    .sort((a, b) => compararCodigosContas(a.codigo, b.codigo))
    .find((conta) =>
      (conta.codigo.startsWith('1.2') || conta.codigo.startsWith('12')) &&
      conta.aceitaLancamentos !== false &&
      contaCorrespondeBancoDocumento(conta, bancoDocumento)
    )?.id;
}

function encontrarContaFinanceiraPorMetodo(
  contas: ContaContabil[],
  metodo?: string
): string | undefined {
  if (!metodoPagamentoExigeContaPropria(metodo)) return undefined;

  return [...contas]
    .sort((a, b) => compararCodigosContas(a.codigo, b.codigo))
    .find((conta) =>
      (conta.codigo.startsWith('1.1') ||
        conta.codigo.startsWith('1.2') ||
        conta.codigo.startsWith('11') ||
        conta.codigo.startsWith('12')) &&
      conta.aceitaLancamentos !== false &&
      contaCorrespondeMetodoPagamento(conta, metodo)
    )?.id;
}

function resolverContaTesouraria(
  mapeamento: MapeamentoContabil | undefined,
  documento: { metodoPagamento?: string; contaBancaria?: string; linhas?: any[] },
  fallback?: MapeamentoContabil,
  contas: ContaContabil[] = []
) {
  const contaCaixa =
    resolverContaLancavelPorId(contas, mapeamento?.contaCaixa) ||
    resolverContaLancavelPorId(contas, fallback?.contaCaixa) ||
    encontrarContaLancavelPorCodigoOuPrefixo(contas, ['1.1', '11.01']);
  const contaBanco =
    encontrarContaBancoPorDocumento(contas, documento) ||
    resolverContaLancavelPorId(contas, mapeamento?.contaBanco) ||
    resolverContaLancavelPorId(contas, fallback?.contaBanco) ||
    encontrarContaLancavelPorCodigoOuPrefixo(contas, ['1.2.1', '12.01', '1.2', '12']);

  if (documentoUsaBanco(documento)) {
    return contaBanco;
  }

  return contaCaixa || contaBanco;
}

function resolverContaTesourariaValidada(
  mapeamento: MapeamentoContabil | undefined,
  documento: { metodoPagamento?: string; contaBancaria?: string; linhas?: any[] },
  fallback: MapeamentoContabil | undefined,
  contas: ContaContabil[],
  contexto: string
) {
  const metodo = obterMetodoPagamentoDocumentoLancamento(documento);
  const bancoDocumento = obterContaBancariaDocumentoLancamento(documento);
  const contaBancoDocumento = encontrarContaBancoPorDocumento(contas, documento);
  const contaMetodo = encontrarContaFinanceiraPorMetodo(contas, metodo);
  const contaFallback = resolverContaTesouraria(mapeamento, documento, fallback, contas);
  const contaFallbackDetalhe = contas.find((conta) => conta.id === contaFallback);
  const usaBanco = documentoUsaBanco(documento);
  const exigeContaPropria = metodoPagamentoExigeContaPropria(metodo);

  if (!usaBanco) {
    return contaObrigatoria(
      contaFallback,
      `Conta de caixa/numerario nao configurada na contabilidade para ${contexto}`
    );
  }

  if (bancoDocumento && !contaBancoDocumento) {
    throw new Error(
      `Nao existe conta contabilistica para ${bancoDocumento}. Crie uma conta lancavel no plano de contas e configure o mapeamento antes de registar ${contexto}.`
    );
  }

  if (contaBancoDocumento) {
    return contaBancoDocumento;
  }

  if (exigeContaPropria) {
    const contaValidaParaMetodo =
      contaMetodo ||
      (contaFallbackDetalhe && contaCorrespondeMetodoPagamento(contaFallbackDetalhe, metodo)
        ? contaFallback
        : undefined);

    if (!contaValidaParaMetodo) {
      throw new Error(
        `Nao existe conta contabilistica para ${obterRotuloMetodoPagamento(metodo)}. Crie uma conta lancavel para este meio de pagamento no plano de contas e configure o mapeamento antes de registar ${contexto}.`
      );
    }

    return contaValidaParaMetodo;
  }

  return contaObrigatoria(
    contaBancoDocumento || contaFallback,
    `Conta bancaria nao configurada na contabilidade para ${contexto}`
  );
}

function calcularTotaisLinhasLancamento(linhas: LinhaLancamento[]) {
  const arredondar = (valor: number) => Math.round((valor + Number.EPSILON) * 100) / 100;
  const totalDebito = arredondar(linhas.reduce((sum, linha) => sum + (Number(linha.debito) || 0), 0));
  const totalCredito = arredondar(linhas.reduce((sum, linha) => sum + (Number(linha.credito) || 0), 0));

  return { totalDebito, totalCredito };
}

export async function gerarLancamentoAutomatico(documento: {
  tipo: TipoDocumentoContabil;
  empresaId: string;
  documentoId: string;
  numeroDocumento: string;
  tipoDocumentoOriginal?: string;
  condicaoPagamento?: string;
  data: string;
  clienteId?: string;
  clienteNome?: string;
  fornecedorId?: string;
  fornecedorNome?: string;
  entidadeNome?: string;
  linhas: any[];
  totalGeral: number;
  totalIVA: number;
  subtotal: number;
  forcar?: boolean;
  previsualizar?: boolean;
  metodoPagamento?: string;
  contaBancaria?: string;
  referenciaPagamento?: string;
  documentosRelacionados?: DocumentoFiscalLancamento['documentosRelacionados'];
  liquidacaoImediata?: boolean;
}) {
  const tipoContabil = normalizarTipoDocumentoContabil(documento.tipo);
  console.log(`[CONTABILIDADE] 🔄 Gerando lançamento automático para ${tipoContabil}: ${documento.numeroDocumento}`);

  if (tipoContabil === 'factura_proforma') {
    return {
      success: true,
      skipped: true,
      message: 'Factura Proforma nao gera lancamento contabil automatico',
    };
  }
  
  try {
    // Obter mapeamento contábil
    const lancamentosExistentes = await kv.getByPrefix(`lancamento_${documento.empresaId}_`);
    const lancamentoExistente = lancamentosExistentes.find((lancamento: LancamentoContabil) =>
      lancamento.documentoId === documento.documentoId &&
      normalizarTipoDocumentoContabil(lancamento.tipoDocumento) === tipoContabil &&
      lancamento.estado !== 'estornado'
    ) as LancamentoContabil | undefined;

    const estadoLancamentoExistente = (lancamentoExistente?.estado || 'rascunho') as LancamentoContabil['estado'];
    const podeAtualizarLancamentoExistente = lancamentoExistente && estadoLancamentoExistente === 'rascunho';

    if (lancamentoExistente && !podeAtualizarLancamentoExistente) {
      return { success: true, data: lancamentoExistente, skipped: true, message: 'Lancamento contabil ja existe para este documento' };
    }

    const mapeamentos = await obterMapeamentosParaLancamentoAutomatico(documento.empresaId);
    const mapeamento = encontrarMapeamento(
      mapeamentos,
      obterTipoMapeamentoPrincipal(tipoContabil)
    );
    const mapeamentoRecebimento = encontrarMapeamento(mapeamentos, 'recibo');
    const mapeamentoAdiantamento = encontrarMapeamento(mapeamentos, 'adiantamento');
    const mapeamentoPagamento = encontrarMapeamento(mapeamentos, 'pagamento');
    const contas = await kv.getByPrefix(`conta_${documento.empresaId}_`) as ContaContabil[];
    const linhas: LinhaLancamento[] = [];
    const subtotaisPorTipo = calcularSubtotaisContabilPorTipo(documento);
    const contaReceitaServicos = await garantirContaReceitaServicos(documento.empresaId, contas);
    const contaGastosServicos =
      encontrarContaLancavelPorCodigoOuPrefixo(contas, ['6.3.2.9.9', '6.3.2', '63']) ||
      mapeamento?.contaCompras ||
      mapeamento?.contaEstoque;
    const adicionarLinhaValor = (
      contaId: string | undefined,
      descricao: string,
      debito: number,
      credito: number,
      erro: string
    ) => {
      const valorDebito = arredondarMoeda(debito);
      const valorCredito = arredondarMoeda(credito);
      if (valorDebito <= 0 && valorCredito <= 0) return;
      linhas.push({
        id: `linha_${Date.now()}_${linhas.length + 1}`,
        contaId: contaObrigatoria(contaId, erro),
        descricao,
        debito: valorDebito,
        credito: valorCredito,
      });
    };
    const adicionarReceitaVenda = (descricao: string, sentido: 'credito' | 'debito') => {
      // ✅ Usa subtotaisPorTipo (já calculado como magnitude absoluta, correcto
      // mesmo para notas de crédito onde documento.subtotal vem negativo) em vez
      // de recalcular a partir de documento.subtotal directamente.
      const produtos = subtotaisPorTipo.produtos;
      const servicos = subtotaisPorTipo.servicos;

      if (produtos > 0) {
        adicionarLinhaValor(
          mapeamento?.contaVendas,
          descricao,
          sentido === 'debito' ? produtos : 0,
          sentido === 'credito' ? produtos : 0,
          'Conta de vendas nao configurada'
        );
      }

      if (servicos > 0) {
        adicionarLinhaValor(
          contaReceitaServicos,
          `${descricao} - servicos`,
          sentido === 'debito' ? servicos : 0,
          sentido === 'credito' ? servicos : 0,
          'Conta de prestacao de servicos nao configurada'
        );
      }
    };
    const adicionarCompraOuServico = (descricao: string, sentido: 'debito' | 'credito') => {
      // ✅ Ver comentário em adicionarReceitaVenda — mesma correcção de sinal.
      const produtos = subtotaisPorTipo.produtos;
      const servicos = subtotaisPorTipo.servicos;
      const contaCompras = mapeamento?.contaEstoque || mapeamento?.contaCompras;

      if (produtos > 0) {
        adicionarLinhaValor(
          contaCompras,
          descricao,
          sentido === 'debito' ? produtos : 0,
          sentido === 'credito' ? produtos : 0,
          'Conta de compras/estoque nao configurada'
        );
      }

      if (servicos > 0) {
        adicionarLinhaValor(
          contaGastosServicos,
          `${descricao} - servicos`,
          sentido === 'debito' ? servicos : 0,
          sentido === 'credito' ? servicos : 0,
          'Conta de fornecimentos e servicos nao configurada'
        );
      }
    };
    
    if (!mapeamento) {
      console.log(`[CONTABILIDADE] Mapeamento nao encontrado para tipo: ${tipoContabil}`);
      return { success: false, error: `Mapeamento contabil nao configurado para ${tipoContabil}` };
    }
    
    if (mapeamento.automatico === false && !documento.forcar) {
      return { success: true, skipped: true, message: `Lancamento automatico desativado para ${tipoContabil}` };
    }

    // Obter diário apropriado
    const diarios = (await kv.getByPrefix(`diario_${documento.empresaId}_`))
      .map((diario: Diario) => normalizarDiario(diario));
    let diario: Diario | undefined;
    const encontrarDiarioPorTipo = (tipo: Diario['tipo']) =>
      diarios.find((d: Diario) => d.tipo === tipo);
    const encontrarDiarioTesouraria = () =>
      documentoUsaBanco(documento)
        ? encontrarDiarioPorTipo('bancos') || encontrarDiarioPorTipo('caixa')
        : encontrarDiarioPorTipo('caixa') || encontrarDiarioPorTipo('bancos');
    
    switch (tipoContabil) {
      case 'fatura':
      case 'factura':
      case 'nota_credito':
      case 'nota_debito':
        diario = encontrarDiarioPorTipo('vendas');
        break;
      case 'factura_recibo':
      case 'venda_dinheiro':
        diario = encontrarDiarioTesouraria();
        break;
      case 'compra':
        diario = documentoCompraEhProntoPagamento(documento)
          ? encontrarDiarioTesouraria()
          : encontrarDiarioPorTipo('compras');
        break;
      case 'recibo':
      case 'adiantamento':
      case 'pagamento':
        diario = encontrarDiarioTesouraria();
        break;
      default:
        diario = encontrarDiarioPorTipo('geral');
    }
    
    if (!diario) {
      return { success: false, error: 'Diário não encontrado' };
    }
    
    // Gerar linhas do lançamento baseado no tipo de documento
    const descricaoVenda = obterDescricaoMercadoriaLancamento('Venda', documento);
    const descricaoCompra = obterDescricaoMercadoriaLancamento('Compra', documento);
    const descricaoNotaCredito = obterDescricaoMercadoriaLancamento('Nota de credito', documento);
    
    if (tipoContabil === 'fatura' || tipoContabil === 'factura' || tipoContabil === 'nota_debito') {
      // VENDA: Débito Clientes, Crédito Vendas + IVA
      linhas.push({
        id: `linha_${Date.now()}_1`,
        contaId: contaObrigatoria(mapeamento.contaReceber, 'Conta de clientes nao configurada'),
        descricao: descricaoVenda,
        debito: documento.totalGeral,
        credito: 0,
      });
      
      adicionarReceitaVenda(descricaoVenda, 'credito');
      
      if (documento.totalIVA > 0) {
        linhas.push({
          id: `linha_${Date.now()}_3`,
          contaId: contaObrigatoria(mapeamento.contaIVALiquidado, 'Conta de IVA liquidado nao configurada'),
          descricao: `IVA ${documento.numeroDocumento}`,
          debito: 0,
          credito: documento.totalIVA,
        });
      }
      
      // TODO: Se estoque permanente, adicionar CMV e Estoque
    } else if (tipoContabil === 'factura_recibo' || tipoContabil === 'venda_dinheiro') {
      const contaTesouraria = resolverContaTesourariaValidada(
        mapeamentoRecebimento,
        documento,
        mapeamento,
        contas,
        `recebimento ${documento.numeroDocumento}`
      );

      linhas.push({
        id: `linha_${Date.now()}_1`,
        contaId: contaObrigatoria(contaTesouraria, 'Conta de caixa/banco nao configurada no mapeamento de recibos'),
        descricao: `Recebimento ${documento.numeroDocumento}`,
        debito: documento.totalGeral,
        credito: 0,
      });

      adicionarReceitaVenda(descricaoVenda, 'credito');

      if (documento.totalIVA > 0) {
        linhas.push({
          id: `linha_${Date.now()}_3`,
          contaId: contaObrigatoria(mapeamento.contaIVALiquidado, 'Conta de IVA liquidado nao configurada'),
          descricao: `IVA ${documento.numeroDocumento}`,
          debito: 0,
          credito: documento.totalIVA,
        });
      }
    } else if (tipoContabil === 'nota_credito') {
      // ✅ Nota de crédito guarda totalGeral/totalImpostos como valores negativos
      // (representa uma redução) — usar sempre a magnitude absoluta nas linhas.
      adicionarReceitaVenda(descricaoNotaCredito, 'debito');

      const ivaNotaCredito = Math.abs(documento.totalIVA);
      if (ivaNotaCredito > 0) {
        linhas.push({
          id: `linha_${Date.now()}_2`,
          contaId: contaObrigatoria(mapeamento.contaIVALiquidado, 'Conta de IVA liquidado nao configurada'),
          descricao: `IVA nota de credito ${documento.numeroDocumento}`,
          debito: ivaNotaCredito,
          credito: 0,
        });
      }

      linhas.push({
        id: `linha_${Date.now()}_3`,
        contaId: contaObrigatoria(mapeamento.contaReceber, 'Conta de clientes nao configurada'),
        descricao: descricaoNotaCredito,
        debito: 0,
        credito: Math.abs(documento.totalGeral),
      });
    } else if (tipoContabil === 'compra') {
      if (documentoCompraEhNotaCredito(documento)) {
        // NOTA DE CREDITO DE FORNECEDOR: reduz dívida, compra/estoque e IVA dedutível
        // ✅ Assim como a nota de crédito de venda, pode vir com valores negativos
        // (representa uma redução) — usar sempre a magnitude absoluta nas linhas.
        linhas.push({
          id: `linha_${Date.now()}_1`,
          contaId: contaObrigatoria(mapeamento.contaPagar, 'Conta de fornecedores nao configurada'),
          descricao: descricaoCompra,
          debito: Math.abs(documento.totalGeral),
          credito: 0,
        });

        adicionarCompraOuServico(descricaoCompra, 'credito');

        const ivaCompraCredito = Math.abs(documento.totalIVA);
        if (ivaCompraCredito > 0) {
          linhas.push({
            id: `linha_${Date.now()}_3`,
            contaId: contaObrigatoria(mapeamento.contaIVADedutivel, 'Conta de IVA dedutivel nao configurada'),
            descricao: `IVA nota de credito ${documento.numeroDocumento}`,
            debito: 0,
            credito: ivaCompraCredito,
          });
        }
      } else {
        // COMPRA / NOTA DE DEBITO FORNECEDOR: aumenta estoque/compra e IVA dedutível
        adicionarCompraOuServico(descricaoCompra, 'debito');
        
        if (documento.totalIVA > 0) {
          linhas.push({
            id: `linha_${Date.now()}_2`,
            contaId: contaObrigatoria(mapeamento.contaIVADedutivel, 'Conta de IVA dedutivel nao configurada'),
            descricao: `IVA ${documento.numeroDocumento}`,
            debito: documento.totalIVA,
            credito: 0,
          });
        }

        const contaCredito = documentoCompraEhProntoPagamento(documento)
          ? resolverContaTesourariaValidada(
              mapeamentoPagamento,
              documento,
              mapeamentoRecebimento,
              contas,
              `pagamento da compra ${documento.numeroDocumento}`
            )
          : mapeamento.contaPagar;
        
        linhas.push({
          id: `linha_${Date.now()}_3`,
          contaId: contaObrigatoria(
            contaCredito,
            documentoCompraEhProntoPagamento(documento)
              ? 'Conta de caixa/banco nao configurada no mapeamento de pagamentos'
              : 'Conta de fornecedores nao configurada'
          ),
          descricao: descricaoCompra,
          debito: 0,
          credito: documento.totalGeral,
        });
      }
    } else if (tipoContabil === 'recibo') {
      const contaTesouraria = resolverContaTesourariaValidada(
        mapeamento,
        documento,
        mapeamentoRecebimento,
        contas,
        `recibo ${documento.numeroDocumento}`
      );
      const valorAdiantamentos = Math.min(documento.totalGeral, obterValorAdiantamentosNoDocumento(documento));
      const valorTesouraria = arredondarMoeda(documento.totalGeral - valorAdiantamentos);
      const contaAdiantamento = mapeamentoAdiantamento?.contaReceber || mapeamento.contaReceber;

      if (valorTesouraria > 0) {
        linhas.push({
          id: `linha_${Date.now()}_1`,
          contaId: contaObrigatoria(contaTesouraria, 'Conta de caixa/banco nao configurada'),
          descricao: `Recebimento ${documento.numeroDocumento}`,
          debito: valorTesouraria,
          credito: 0,
        });
      }

      if (valorAdiantamentos > 0) {
        linhas.push({
          id: `linha_${Date.now()}_adt`,
          contaId: contaObrigatoria(contaAdiantamento, 'Conta de adiantamentos de clientes nao configurada'),
          descricao: `Aplicacao de adiantamento ${documento.numeroDocumento}`,
          debito: valorAdiantamentos,
          credito: 0,
        });
      }
      const contaClientes = contaObrigatoria(mapeamento.contaReceber, 'Conta de clientes nao configurada');
      const liquidacoesFacturas = (documento.documentosRelacionados || [])
        .filter((relacionado) => ['fatura', 'factura', 'nota_debito'].includes(normalizarTipoDocumentoContabil(relacionado.tipo)))
        .map((relacionado) => ({
          ...relacionado,
          totalGeral: arredondarMoeda(Number(relacionado.totalGeral) || 0),
        }))
        .filter((relacionado) => relacionado.totalGeral > 0);
      let valorClientesRestante = arredondarMoeda(documento.totalGeral);

      if (liquidacoesFacturas.length > 0) {
        liquidacoesFacturas.forEach((relacionado, index) => {
          const valorLiquidado = Math.min(valorClientesRestante, relacionado.totalGeral);
          if (valorLiquidado <= 0) return;

          linhas.push({
            id: `linha_${Date.now()}_liq_${index}`,
            contaId: contaClientes,
            descricao: `Liquidacao ${relacionado.numeroDocumento} pelo recibo ${documento.numeroDocumento}`,
            debito: 0,
            credito: valorLiquidado,
          });
          valorClientesRestante = arredondarMoeda(valorClientesRestante - valorLiquidado);
        });
      }

      if (valorClientesRestante > 0.01 || liquidacoesFacturas.length === 0) {
        linhas.push({
          id: `linha_${Date.now()}_2`,
          contaId: contaClientes,
          descricao: liquidacoesFacturas.length > 0
            ? `Liquidacao residual pelo recibo ${documento.numeroDocumento}`
            : `Recebimento ${documento.numeroDocumento}`,
          debito: 0,
          credito: valorClientesRestante > 0.01 ? valorClientesRestante : documento.totalGeral,
        });
      }
    } else if (tipoContabil === 'adiantamento') {
      const contaTesouraria = resolverContaTesourariaValidada(
        mapeamento,
        documento,
        mapeamentoRecebimento,
        contas,
        `adiantamento ${documento.numeroDocumento}`
      );
      const contaAdiantamento = mapeamentoAdiantamento?.contaReceber || mapeamento.contaReceber;

      linhas.push({
        id: `linha_${Date.now()}_1`,
        contaId: contaObrigatoria(contaTesouraria, 'Conta de caixa/banco nao configurada'),
        descricao: `Adiantamento ${documento.numeroDocumento}`,
        debito: documento.totalGeral,
        credito: 0,
      });

      linhas.push({
        id: `linha_${Date.now()}_2`,
        contaId: contaObrigatoria(contaAdiantamento, 'Conta de adiantamentos de clientes nao configurada'),
        descricao: `Adiantamento ${documento.numeroDocumento}`,
        debito: 0,
        credito: documento.totalGeral,
      });
    } else if (tipoContabil === 'pagamento') {
      const contaTesouraria = resolverContaTesourariaValidada(
        mapeamento,
        documento,
        mapeamentoRecebimento,
        contas,
        `pagamento ${documento.numeroDocumento}`
      );
      const pagamentoEhAdiantamentoFornecedor = normalizarTextoDocumento(
        `${documento.tipoDocumentoOriginal || ''} ${documento.condicaoPagamento || ''}`
      ).includes('adiantamento');
      const contaAdiantamentoFornecedor =
        resolverContaLancavelPorId(contas, mapeamento.contaAdiantamentoFornecedor) ||
        encontrarContaLancavelPorCodigoOuPrefixo(contas, ['4.2.9']);
      const contaDebitoPagamento = pagamentoEhAdiantamentoFornecedor
        ? contaAdiantamentoFornecedor || mapeamento.contaPagar
        : mapeamento.contaPagar;
      const descricaoPagamento = pagamentoEhAdiantamentoFornecedor
        ? `Adiantamento a fornecedor ${documento.numeroDocumento}`
        : `Pagamento ${documento.numeroDocumento}`;

      // PAGAMENTO: Debito Fornecedores/Adiantamentos, Credito Caixa/Banco
      linhas.push({
        id: `linha_${Date.now()}_1`,
        contaId: contaObrigatoria(contaDebitoPagamento, 'Conta de fornecedores/adiantamentos nao configurada'),
        descricao: descricaoPagamento,
        debito: documento.totalGeral,
        credito: 0,
      });
      
      linhas.push({
        id: `linha_${Date.now()}_2`,
        contaId: contaObrigatoria(contaTesouraria, 'Conta de caixa/banco nao configurada'),
        descricao: descricaoPagamento,
        debito: 0,
        credito: documento.totalGeral,
      });
    }
    
    // Criar lançamento
    if (linhas.length < 2) {
      return { success: false, error: `Tipo de documento sem regra contabil: ${tipoContabil}` };
    }

    const { totalDebito, totalCredito } = calcularTotaisLinhasLancamento(linhas);
    if (Math.abs(totalDebito - totalCredito) > 0.01) {
      return {
        success: false,
        error: `Lancamento automatico nao balanceado para ${documento.numeroDocumento}`,
      };
    }

    const contasPorId = new Map<string, ContaContabil>(
      contas.map((conta: ContaContabil) => [conta.id, conta])
    );

    for (const linha of linhas) {
      const conta = contasPorId.get(linha.contaId);
      if (!conta) {
        return { success: false, error: `Conta nao encontrada para ${linha.descricao}` };
      }

      if (!conta.aceitaLancamentos) {
        return {
          success: false,
          error: `A conta ${conta.codigo} - ${conta.nome} e sintetica e nao aceita lancamentos`,
        };
      }
    }

    const entidadeNome = documento.entidadeNome || documento.fornecedorNome || documento.clienteNome || '';
    const descricaoBase =
      tipoContabil === 'compra'
        ? descricaoCompra
        : tipoContabil === 'nota_credito'
          ? descricaoNotaCredito
          : ['fatura', 'factura', 'factura_recibo', 'venda_dinheiro', 'nota_debito'].includes(tipoContabil)
            ? descricaoVenda
            : `Lancamento automatico - ${tipoContabil} ${documento.numeroDocumento}`;
    const descricaoDocumento = entidadeNome ? `${descricaoBase} - ${entidadeNome}` : descricaoBase;

    const lancamento: Omit<LancamentoContabil, 'id'> = {
      empresaId: documento.empresaId,
      diarioId: diario.id,
      origemLancamento: 'automatico',
      tipoDocumento: tipoContabil,
      documentoId: documento.documentoId,
      numeroDocumento: documento.numeroDocumento,
      clienteId: documento.clienteId,
      clienteNome: documento.clienteNome,
      fornecedorId: documento.fornecedorId,
      fornecedorNome: documento.fornecedorNome,
      entidadeNome,
      data: documento.data,
      referencia: '',
      descricao: descricaoDocumento,
      totalDebito,
      totalCredito,
      estado: 'rascunho',
      dataLancamento: undefined,
      criadoPor: 'sistema',
      linhas,
      observacoes: documento.documentosRelacionados?.length
        ? `Inclui documentos relacionados: ${documento.documentosRelacionados.map((item) => item.numeroDocumento).join(', ')}`
        : undefined,
    };

    if (documento.previsualizar) {
      const diarioNormalizado = normalizarDiario(diario);
      return {
        success: true,
        preview: true,
        data: {
          ...lancamento,
          diarioCodigo: diarioNormalizado.codigo,
          diarioNome: diarioNormalizado.nome,
          diarioTipo: diarioNormalizado.tipo,
          subtotal: documento.subtotal,
          totalIVA: documento.totalIVA,
          totalGeral: documento.totalGeral,
          documentosRelacionados: documento.documentosRelacionados || [],
        },
      };
    }
    
    const resultado = podeAtualizarLancamentoExistente
      ? await atualizarLancamento(lancamentoExistente.id, {
          ...lancamento,
          referencia: lancamentoExistente.referencia,
          estado: 'rascunho',
          criadoPor: lancamentoExistente.criadoPor || lancamento.criadoPor,
        })
      : await criarLancamento(lancamento);
    
    if (resultado.success) {
      console.log(
        `[CONTABILIDADE] ✅ Lançamento automático ${podeAtualizarLancamentoExistente ? 'atualizado' : 'criado'}: ${resultado.data.referencia}`
      );
    }
    
    return resultado;
  } catch (error: any) {
    console.error('[CONTABILIDADE] ❌ Erro ao gerar lançamento automático:', error);
    return { success: false, error: error.message };
  }
}

function obterValorMovimentoStockContabil(movimento: any): number {
  const valorTotal = Number(movimento?.valorTotal);
  if (Number.isFinite(valorTotal) && valorTotal > 0) {
    return arredondarMoeda(valorTotal);
  }

  const custoUnitario = Number(movimento?.custoUnitario);
  const quantidade = Number(movimento?.quantidade);
  if (Number.isFinite(custoUnitario) && custoUnitario > 0 && Number.isFinite(quantidade) && quantidade > 0) {
    return arredondarMoeda(custoUnitario * quantidade);
  }

  return 0;
}

function movimentoStockEhTransferencia(movimento: any): boolean {
  const texto = normalizarTextoDocumento([
    movimento?.motivo,
    movimento?.documentoTipo,
    movimento?.observacoes,
  ].join(' '));

  return Boolean(
    texto.includes('transferencia') ||
      (movimento?.armazemOrigem && movimento?.armazemDestino)
  );
}

function movimentoStockDocumentoCompraJaContabiliza(movimento: any): boolean {
  // ✅ NAO incluir movimento?.motivo aqui — no unico chamador este movimento
  // tem sempre motivo==='devolucao_fornecedor', o que fazia o texto conter
  // sempre "fornecedor" e a verificacao ser tautologicamente verdadeira,
  // independente do documento realmente ligado ao movimento.
  const texto = normalizarTextoDocumento([
    movimento?.documentoTipo,
    movimento?.documentoNumero,
  ].join(' '));

  return texto.includes('compra') || texto.includes('fornecedor') || texto.includes('nota credito');
}

function obterDescricaoMovimentoStock(movimento: any): string {
  const artigo = [movimento?.artigoCodigo, movimento?.artigoNome].filter(Boolean).join(' - ') || movimento?.artigoId || 'artigo';
  const quantidade = Number(movimento?.quantidade || 0);
  const documento = movimento?.documentoNumero ? ` (${movimento.documentoNumero})` : '';
  const armazem = movimento?.armazemCodigo || movimento?.armazemNome
    ? ` no armazem ${movimento?.armazemCodigo || movimento?.armazemNome}`
    : '';

  return `${movimento?.tipo === 'entrada' ? 'Entrada' : 'Saida'} de stock${documento}: ${artigo}, quantidade ${quantidade}${armazem}`;
}

export async function gerarLancamentoMovimentoStock(
  movimento: any,
  opcoes: { forcar?: boolean; criadoPor?: string } = {},
) {
  try {
    if (!movimento?.empresaId || !movimento?.id) {
      return { success: false, error: 'Movimento de stock invalido para contabilizacao' };
    }

    if (movimentoStockEhTransferencia(movimento)) {
      return { success: true, skipped: true, message: 'Transferencia entre armazens nao gera lancamento contabil' };
    }

    if (movimento.motivo === 'recepcao') {
      return { success: true, skipped: true, message: 'Recepcao de compra e contabilizada pelo documento de compra' };
    }

    if (movimento.motivo === 'devolucao_fornecedor' && movimentoStockDocumentoCompraJaContabiliza(movimento)) {
      return { success: true, skipped: true, message: 'Devolucao a fornecedor ligada a documento de compra/nota de credito' };
    }

    const valor = obterValorMovimentoStockContabil(movimento);
    if (valor <= 0) {
      // Numa venda o custo nao pode legitimamente ser zero — normalmente
      // significa artigo sem historico de compra/custo medio. Reportar como
      // falha (em vez de skip silencioso) para que anexarResultadoContabilidade
      // registe pendencia e o CMV nao fique subestimado sem ninguem notar.
      if (movimento.tipo === 'saida' && movimento.motivo === 'venda') {
        return {
          success: false,
          error: `Sem custo definido para ${obterDescricaoMovimentoStock(movimento)} — CMV nao lancado. Configure o preco de compra/custo medio do artigo.`,
        };
      }
      return { success: true, skipped: true, message: 'Movimento sem custo/valor para contabilizacao' };
    }

    const tipoDocumento = 'movimento_stock';
    const lancamentosExistentes = await kv.getByPrefix(`lancamento_${movimento.empresaId}_`);
    const lancamentoExistente = lancamentosExistentes.find((lancamento: LancamentoContabil) =>
      lancamento.documentoId === movimento.id &&
      normalizarTipoDocumentoContabil(lancamento.tipoDocumento) === tipoDocumento &&
      lancamento.estado !== 'estornado'
    ) as LancamentoContabil | undefined;

    if (lancamentoExistente) {
      return { success: true, data: lancamentoExistente, skipped: true, message: 'Lancamento contabil ja existe para este movimento de stock' };
    }

    const contas = await kv.getByPrefix(`conta_${movimento.empresaId}_`) as ContaContabil[];
    const findConta = (...codigos: string[]) =>
      encontrarContaLancavelPorCodigoOuPrefixo(contas, codigos) ||
      (contas.find((c: ContaContabil) => codigos.includes(c.codigo)) as ContaContabil | undefined)?.id;

    const mapeamentos = await obterMapeamentosParaLancamentoAutomatico(movimento.empresaId);
    const mapeamentoStock = encontrarMapeamento(mapeamentos, 'movimento_stock');
    const mapeamentoVenda = encontrarMapeamento(mapeamentos, 'fatura');
    const mapeamentoCompra = encontrarMapeamento(mapeamentos, 'compra');

    if (mapeamentoStock?.automatico === false && !opcoes.forcar) {
      return { success: true, skipped: true, message: 'Lancamento automatico de stock desativado' };
    }

    const contaEstoque = resolverContaLancavelPorId(contas, mapeamentoStock?.contaEstoque) ||
      resolverContaLancavelPorId(contas, mapeamentoCompra?.contaEstoque) ||
      resolverContaLancavelPorId(contas, mapeamentoCompra?.contaCompras) ||
      findConta('2.2.3', '31.01');
    const contaCMV = resolverContaLancavelPorId(contas, mapeamentoStock?.contaCMV) ||
      resolverContaLancavelPorId(contas, mapeamentoVenda?.contaCMV) ||
      findConta('6.1.1.2', '61.01');
    const contaPerdas = resolverContaLancavelPorId(contas, mapeamentoStock?.contaPerdasStock) ||
      findConta('6.8.4.2', '6.8.4', '6.1.1.2');
    const contaRegularizacao = resolverContaLancavelPorId(contas, mapeamentoStock?.contaRegularizacaoStock) ||
      findConta('7.4.1.1', '7.4.1');
    const contaPagar = resolverContaLancavelPorId(contas, mapeamentoCompra?.contaPagar) ||
      findConta('4.2.1', '22.01');

    const descricao = obterDescricaoMovimentoStock(movimento);
    let contaDebito: string | undefined;
    let contaCredito: string | undefined;
    let descricaoLancamento = descricao;

    if (movimento.tipo === 'saida' && movimento.motivo === 'venda') {
      contaDebito = contaCMV;
      contaCredito = contaEstoque;
      descricaoLancamento = `CMVMC - ${descricao}`;
    } else if (movimento.tipo === 'saida' && movimento.motivo === 'devolucao_fornecedor') {
      contaDebito = contaPagar;
      contaCredito = contaEstoque;
      descricaoLancamento = `Devolucao a fornecedor - ${descricao}`;
    } else if (movimento.tipo === 'saida') {
      contaDebito = contaPerdas || contaCMV;
      contaCredito = contaEstoque;
      descricaoLancamento = `Perda/consumo de stock - ${descricao}`;
    } else if (movimento.tipo === 'entrada' && movimento.motivo === 'devolucao_cliente') {
      contaDebito = contaEstoque;
      contaCredito = contaCMV;
      descricaoLancamento = `Reposicao por devolucao de cliente - ${descricao}`;
    } else if (movimento.tipo === 'entrada') {
      contaDebito = contaEstoque;
      contaCredito = contaRegularizacao;
      descricaoLancamento = `Regularizacao positiva de stock - ${descricao}`;
    }

    if (!contaDebito || !contaCredito) {
      return { success: true, skipped: true, message: `Movimento de stock sem regra contabil: ${movimento.tipo}/${movimento.motivo}` };
    }

    await inicializarDiarios(movimento.empresaId);
    const diarios = (await kv.getByPrefix(`diario_${movimento.empresaId}_`))
      .map((diario: Diario) => normalizarDiario(diario));
    const diario =
      (movimento.motivo === 'venda'
        ? diarios.find((item: Diario) => item.tipo === 'vendas')
        : undefined) ||
      diarios.find((item: Diario) => item.tipo === 'regularizacoes') ||
      diarios.find((item: Diario) => item.tipo === 'geral');

    if (!diario) {
      return { success: false, error: 'Diario de regularizacoes nao encontrado' };
    }

    const linhas: LinhaLancamento[] = [
      {
        id: `linha_${Date.now()}_stock_debito`,
        contaId: contaObrigatoria(contaDebito, 'Conta de debito de stock nao configurada'),
        descricao: descricaoLancamento,
        debito: valor,
        credito: 0,
      },
      {
        id: `linha_${Date.now()}_stock_credito`,
        contaId: contaObrigatoria(contaCredito, 'Conta de credito de stock nao configurada'),
        descricao: descricaoLancamento,
        debito: 0,
        credito: valor,
      },
    ];

    const contasPorId = new Map<string, ContaContabil>(contas.map((conta: ContaContabil) => [conta.id, conta]));
    for (const linha of linhas) {
      const conta = contasPorId.get(linha.contaId);
      if (!conta) {
        return { success: false, error: `Conta nao encontrada para ${linha.descricao}` };
      }
      if (!conta.aceitaLancamentos) {
        return { success: false, error: `A conta ${conta.codigo} - ${conta.nome} e sintetica e nao aceita lancamentos` };
      }
    }

    const lancamento: Omit<LancamentoContabil, 'id'> = {
      empresaId: movimento.empresaId,
      diarioId: diario.id,
      origemLancamento: 'automatico',
      tipoDocumento,
      documentoId: movimento.id,
      numeroDocumento: movimento.documentoNumero || movimento.id,
      data: String(movimento.dataMovimento || movimento.dataCriacao || new Date().toISOString()).slice(0, 10),
      referencia: '',
      descricao: descricaoLancamento,
      totalDebito: valor,
      totalCredito: valor,
      estado: 'rascunho',
      dataLancamento: undefined,
      criadoPor: opcoes.criadoPor || movimento.responsavel || 'sistema',
      linhas,
      observacoes: [
        movimento.documentoTipo ? `Documento origem: ${movimento.documentoTipo}` : '',
        movimento.armazemCodigo || movimento.armazemNome ? `Armazem: ${movimento.armazemCodigo || movimento.armazemNome}` : '',
        movimento.custoUnitario ? `Custo unitario: ${movimento.custoUnitario}` : '',
      ].filter(Boolean).join(' | ') || undefined,
    };

    return await criarLancamento(lancamento);
  } catch (error: any) {
    console.error('[CONTABILIDADE] Erro ao gerar lancamento de movimento de stock:', error);
    return { success: false, error: error.message };
  }
}
// ========================================
// RELATÓRIOS
// ========================================

export async function listarDocumentosFiscaisParaLancamento(
  empresaId: string,
  filtros: FiltrosDocumentosFiscais = {}
) {
  try {
    const tipoFiltro = (filtros.tipo || 'todos').trim().toLowerCase();
    const documentos: DocumentoFiscalLancamento[] = [];

    const incluirTipo = (tipo: string) =>
      tipoFiltro === 'todos' || tipoFiltro === '' || tipoFiltro === tipo;

    const facturasListaEmpresa = await facturas.listarFacturasPorEmpresa(empresaId);
    const facturasPorId = new Map<string, any>(
      facturasListaEmpresa.map((factura: any) => [factura.id, factura])
    );
    const criarFacturasRelacionadasDoRecibo = (recibo: any) =>
      (recibo.pagamentos || [])
        .filter((pagamento: any) => pagamento?.facturaId)
        .map((pagamento: any) => {
          const factura = facturasPorId.get(pagamento.facturaId);
          return {
            id: pagamento.facturaId,
            tipo: 'fatura' as TipoDocumentoContabil,
            tipoLabel: 'Factura',
            numeroDocumento:
              pagamento.facturaNumero ||
              factura?.numeroDocumento ||
              factura?.numero ||
              pagamento.facturaId,
            data: String(pagamento.facturaDataEmissao || obterDataDocumentoFiscal(factura || {})).slice(0, 10),
            totalGeral: Number(pagamento.valorPago || 0) || 0,
          };
        })
        .filter((documento: NonNullable<DocumentoFiscalLancamento['documentosRelacionados']>[number]) => documento.totalGeral > 0);

    const recibosListaEmpresa = await recibos.listarRecibosPorEmpresa(empresaId);
    const recibosAtivos = recibosListaEmpresa.filter((recibo: any) => documentoFiscalAtivo(recibo));
    const recibosPorFacturaId = new Map<string, Array<{ recibo: any; valorPago: number }>>();

    for (const recibo of recibosAtivos) {
      for (const pagamento of recibo.pagamentos || []) {
        if (!pagamento?.facturaId) continue;

        const lista = recibosPorFacturaId.get(pagamento.facturaId) || [];
        lista.push({
          recibo,
          valorPago: Number(pagamento.valorPago || 0) || 0,
        });
        recibosPorFacturaId.set(pagamento.facturaId, lista);
      }
    }

    if (incluirTipo('factura') || incluirTipo('factura_recibo')) {
      for (const factura of facturasListaEmpresa) {
        if (!documentoFiscalAtivo(factura)) continue;
        if (documentoEhProformaContabil(factura.tipoDocumento)) continue;

        const tipoFactura = normalizarTipoDocumentoContabil(factura.tipoDocumento || 'factura');
        const tipoFinal = tipoFactura === 'factura_recibo' ? 'factura_recibo' : 'fatura';
        const filtroFinal = tipoFinal === 'factura_recibo' ? 'factura_recibo' : 'factura';
        if (!incluirTipo(filtroFinal)) continue;

        const documentoLancamento = criarDocumentoFiscalLancamento(
          factura,
          tipoFinal,
          tipoFinal === 'factura_recibo' ? 'Factura-Recibo' : 'Factura'
        );

        const recibosAssociados = recibosPorFacturaId.get(factura.id) || [];
        if (recibosAssociados.length > 0) {
          documentoLancamento.documentosRelacionados = recibosAssociados.map(({ recibo, valorPago }) => ({
            id: recibo.id,
            tipo: 'recibo',
            tipoLabel: 'Recibo',
            numeroDocumento: recibo.numeroDocumento,
            data: obterDataDocumentoFiscal(recibo),
            totalGeral: valorPago,
          }));
          documentoLancamento.totalRecebido = recibosAssociados.reduce(
            (sum, item) => sum + item.valorPago,
            0
          );
        }

        documentos.push(documentoLancamento);
      }
    }

    if (incluirTipo('recibo')) {
      for (const recibo of recibosAtivos) {
        const documentoLancamento = criarDocumentoFiscalLancamento(recibo, 'recibo', 'Recibo');
        const facturasRelacionadas = criarFacturasRelacionadasDoRecibo(recibo);

        if (facturasRelacionadas.length > 0) {
          documentoLancamento.documentosRelacionados = facturasRelacionadas;
          documentoLancamento.totalRecebido = facturasRelacionadas.reduce(
            (sum, factura) => sum + factura.totalGeral,
            0
          );
        }

        documentos.push(documentoLancamento);
      }
    }

    if (incluirTipo('venda_dinheiro')) {
      const resultadoVD = await vendasDinheiro.getVDsByEmpresa(empresaId);
      if (!resultadoVD.success) {
        return resultadoVD;
      }

      for (const vd of resultadoVD.data || []) {
        if (!documentoFiscalAtivo(vd)) continue;
        documentos.push(criarDocumentoFiscalLancamento(vd, 'venda_dinheiro', 'Venda a Dinheiro'));
      }
    }

    if (incluirTipo('nota_credito') || incluirTipo('nota_debito')) {
      const resultadoNotas = await notas.getNotasByEmpresa(empresaId);
      if (!resultadoNotas.success) {
        return resultadoNotas;
      }

      for (const nota of resultadoNotas.data || []) {
        if (!documentoFiscalAtivo(nota)) continue;
        const tipoNota = normalizarTipoDocumentoContabil(nota.tipo);
        const filtroNota = tipoNota === 'nota_credito' ? 'nota_credito' : 'nota_debito';
        if (!incluirTipo(filtroNota)) continue;
        documentos.push(
          criarDocumentoFiscalLancamento(
            nota,
            filtroNota,
            filtroNota === 'nota_credito' ? 'Nota de Credito' : 'Nota de Debito'
          )
        );
      }
    }

    if (incluirTipo('adiantamento')) {
      const resultadoAdiantamentos = await adiantamentos.getAdiantamentosByEmpresa(empresaId);
      if (!resultadoAdiantamentos.success) {
        return resultadoAdiantamentos;
      }

      for (const adiantamento of resultadoAdiantamentos.data || []) {
        if (!documentoFiscalAtivo(adiantamento)) continue;
        documentos.push(criarDocumentoFiscalLancamento(adiantamento, 'adiantamento', 'Adiantamento'));
      }
    }

    if (incluirTipo('compra') || incluirTipo('pagamento')) {
      const fornecedoresLista = await fornecedores.listarFornecedores(empresaId);
      const fornecedoresPorId = new Map(
        (fornecedoresLista || []).map((fornecedor: any) => [fornecedor.id, fornecedor])
      );
      const pagamentosLista = await compras.listarPagamentosCompra(empresaId);
      const pagamentosPorCompraId = new Map<string, any[]>();

      for (const pagamento of pagamentosLista) {
        if (!pagamento?.compraId || !documentoFiscalAtivo(pagamento)) continue;
        const lista = pagamentosPorCompraId.get(pagamento.compraId) || [];
        lista.push(pagamento);
        pagamentosPorCompraId.set(pagamento.compraId, lista);
      }

      if (incluirTipo('compra')) {
        const comprasLista = await compras.listarCompras(empresaId);
        for (const compra of comprasLista) {
          if (!documentoFiscalAtivo(compra)) continue;
          if ((compra as any).geraLancamentoContabil === false) continue;
          if (compras.obterPerfilDocumentoCompra((compra as any).tipoDocumento).geraLancamentoContabil === false) continue;

          const fornecedor = fornecedoresPorId.get(compra.fornecedorId) as any;
          const documentoLancamento = criarDocumentoFiscalLancamento(
            {
              ...compra,
              fornecedorNome: fornecedor
                ? `${fornecedor.codigo ? `${fornecedor.codigo} - ` : ''}${fornecedor.nome}`
                : compra.fornecedorId,
            },
            'compra',
            `Compra ${compra.tipoDocumento || ''}`.trim()
          );

          const pagamentosAssociados = pagamentosPorCompraId.get(compra.id) || [];
          if (pagamentosAssociados.length > 0) {
            documentoLancamento.documentosRelacionados = pagamentosAssociados.map((pagamento) => ({
              id: pagamento.id,
              tipo: 'pagamento',
              tipoLabel: 'Pagamento Fornecedor',
              numeroDocumento: pagamento.numeroDocumento,
              data: obterDataDocumentoFiscal(pagamento),
              totalGeral: Number(pagamento.valor || 0) || 0,
            }));
            documentoLancamento.totalRecebido = pagamentosAssociados.reduce(
              (sum, pagamento) => sum + (Number(pagamento.valor || 0) || 0),
              0
            );
          }

          documentos.push(documentoLancamento);
        }
      }

      if (incluirTipo('pagamento')) {
        for (const pagamento of pagamentosLista) {
          if (!documentoFiscalAtivo(pagamento)) continue;
          if ((pagamento as any).contabilizarSeparado === false) continue;

          const fornecedor = fornecedoresPorId.get(pagamento.fornecedorId) as any;
          documentos.push(
            criarDocumentoFiscalLancamento(
              {
                ...pagamento,
                totalGeral: pagamento.valor,
                subtotal: pagamento.valor,
                totalIVA: 0,
                fornecedorNome: fornecedor
                  ? `${fornecedor.codigo ? `${fornecedor.codigo} - ` : ''}${fornecedor.nome}`
                  : pagamento.fornecedorId,
              },
              'pagamento',
              'Pagamento Fornecedor'
            )
          );
        }
      }
    }

    // ✅ Antes esta ferramenta so enumerava documentos fiscais (facturas,
    // recibos, VD, notas, compras) — nunca os movimentos de stock/CMV
    // gerados a parte por gerarLancamentoMovimentoStock. Um CMV em falta ou
    // orfao (ex: apos editar uma factura) ficava invisivel aqui, dando falsa
    // confianca de que os livros estavam OK antes de construir relatorios
    // fiscais automaticos em cima destes dados.
    if (incluirTipo('movimento_stock')) {
      const movimentosListaEmpresa = await movimentosStock.listarMovimentos(empresaId);
      for (const movimento of movimentosListaEmpresa as any[]) {
        if (movimento.movimentoOriginalId) continue;
        if (movimento.revertidoEm || movimento.movimentoReversaoId) continue;
        if (movimentoStockEhTransferencia(movimento)) continue;
        if (movimento.motivo === 'recepcao') continue;
        if (movimento.motivo === 'devolucao_fornecedor' && movimentoStockDocumentoCompraJaContabiliza(movimento)) continue;

        const valorCMV = obterValorMovimentoStockContabil(movimento);
        if (valorCMV <= 0) continue;

        documentos.push(
          criarDocumentoFiscalLancamento(
            {
              ...movimento,
              data: movimento.dataMovimento || movimento.dataCriacao,
              numeroDocumento: movimento.documentoNumero || movimento.id,
              totalGeral: valorCMV,
              subtotal: valorCMV,
              totalIVA: 0,
            },
            'movimento_stock',
            'Movimento de Stock (CMV)',
          ),
        );
      }
    }

    const documentosFiltrados = documentos
      .filter((documento) => documento.totalGeral > 0)
      .filter((documento) => filtrarPorPeriodo(documento.data, filtros.dataInicio, filtros.dataFim))
      .filter((documento) => numeroIncluiFiltro(documento.numeroDocumento, filtros.numeroDocumento));

    const lancamentos = await kv.getByPrefix(`lancamento_${empresaId}_`);
    const documentosComEstado = documentosFiltrados.map((documento) => {
      const lancamentoExistente = lancamentos.find((lancamento: LancamentoContabil) =>
        lancamento.documentoId === documento.id && lancamento.estado !== 'estornado'
      ) as LancamentoContabil | undefined;

      return {
        ...documento,
        lancamentoExistente: lancamentoExistente
          ? {
              id: lancamentoExistente.id,
              referencia: lancamentoExistente.referencia,
              estado: lancamentoExistente.estado || 'rascunho',
            }
          : undefined,
      };
    });

    documentosComEstado.sort((a, b) =>
      new Date(b.data).getTime() - new Date(a.data).getTime()
    );

    return { success: true, data: documentosComEstado };
  } catch (error: any) {
    console.error('[CONTABILIDADE] Erro ao listar documentos fiscais:', error);
    return { success: false, error: error.message };
  }
}

function selecionarDocumentosAutomaticos(
  documentosDisponiveis: DocumentoFiscalLancamento[],
  filtros: FiltrosDocumentosFiscais = {}
) {
  const idsSelecionados = new Set(filtros.documentoIds || []);
  const documentosPorId = new Map(documentosDisponiveis.map((documento) => [documento.id, documento]));
  const idsParaProcessar = new Set<string>();

  if (idsSelecionados.size === 0) {
    documentosDisponiveis.forEach((documento) => idsParaProcessar.add(documento.id));
  } else {
    idsSelecionados.forEach((id) => idsParaProcessar.add(id));

    for (const id of idsSelecionados) {
      const documento = documentosPorId.get(id);
      for (const relacionado of documento?.documentosRelacionados || []) {
        const documentoRelacionado = documentosPorId.get(relacionado.id);
        if (documentoRelacionado && !documentoRelacionado.lancamentoExistente) {
          idsParaProcessar.add(relacionado.id);
        }
      }
    }
  }

  return documentosDisponiveis.filter((documento) => idsParaProcessar.has(documento.id));
}

function prioridadeDocumentoAutomatico(tipoDocumento: string) {
  const tipo = normalizarTipoDocumentoContabil(tipoDocumento);
  if (['fatura', 'factura', 'nota_debito', 'compra'].includes(tipo)) return 1;
  if (['nota_credito', 'factura_recibo', 'venda_dinheiro', 'adiantamento'].includes(tipo)) return 2;
  if (['recibo', 'pagamento'].includes(tipo)) return 3;
  return 4;
}

function ordenarDocumentosProcessamentoAutomatico(documentos: DocumentoFiscalLancamento[]) {
  return [...documentos].sort((a, b) => {
    const prioridade = prioridadeDocumentoAutomatico(a.tipoContabil) - prioridadeDocumentoAutomatico(b.tipoContabil);
    if (prioridade !== 0) return prioridade;

    const data = new Date(a.data).getTime() - new Date(b.data).getTime();
    if (data !== 0) return data;

    return a.numeroDocumento.localeCompare(b.numeroDocumento, 'pt', { numeric: true, sensitivity: 'base' });
  });
}

// ✅ movimento_stock (CMV) usa gerarLancamentoMovimentoStock, nao
// gerarLancamentoAutomatico — as suas linhas nao vem de documento.linhas
// (um MovimentoStock nao tem itens/linhas/pagamentos), por isso ao ligar
// estes documentos ao fluxo de preview/geracao em massa (que antes so
// conhecia tipos fiscais normais) e preciso despachar para a funcao certa.
// Nota: gerarLancamentoMovimentoStock nao tem um modo "previsualizar" puro
// (sem persistir) como gerarLancamentoAutomatico — se o movimento ainda nao
// tiver lancamento, o proprio preview acaba por cria-lo (em rascunho); se ja
// tiver, e devolvido tal como esta (idempotente, sem duplicar).
async function gerarOuPrevisualizarLancamentoDocumento(
  documento: DocumentoFiscalLancamento,
  empresaId: string,
  opcoes: { forcar?: boolean; previsualizar?: boolean },
) {
  if (documento.tipoContabil === 'movimento_stock') {
    const movimento = await movimentosStock.buscarMovimentoPorId(documento.id);
    if (!movimento) {
      return { success: false, error: 'Movimento de stock nao encontrado' };
    }
    return await gerarLancamentoMovimentoStock(movimento, { forcar: opcoes.forcar });
  }

  return await gerarLancamentoAutomatico({
    tipo: documento.tipoContabil,
    empresaId,
    documentoId: documento.id,
    numeroDocumento: documento.numeroDocumento,
    tipoDocumentoOriginal: documento.tipoDocumentoOriginal,
    condicaoPagamento: documento.condicaoPagamento,
    data: documento.data,
    clienteId: documento.clienteId,
    clienteNome: documento.clienteNome,
    fornecedorId: documento.fornecedorId,
    fornecedorNome: documento.fornecedorNome,
    entidadeNome: documento.entidadeNome,
    linhas: documento.linhas,
    totalGeral: documento.totalGeral,
    totalIVA: documento.totalIVA,
    subtotal: documento.subtotal,
    metodoPagamento: documento.metodoPagamento,
    contaBancaria: documento.contaBancaria,
    referenciaPagamento: documento.referenciaPagamento,
    documentosRelacionados: documento.documentosRelacionados,
    forcar: opcoes.forcar,
    previsualizar: opcoes.previsualizar,
  });
}

async function obterDocumentosParaProcessamentoAutomatico(
  empresaId: string,
  filtros: FiltrosDocumentosFiscais = {}
) {
  const filtrosConsulta = filtros.documentoIds?.length
    ? { tipo: 'todos' }
    : filtros;

  const resultadoDocumentos = await listarDocumentosFiscaisParaLancamento(empresaId, filtrosConsulta);
  if (!resultadoDocumentos.success) {
    return resultadoDocumentos;
  }

  return {
    success: true,
    data: ordenarDocumentosProcessamentoAutomatico(
      selecionarDocumentosAutomaticos(resultadoDocumentos.data || [], filtros)
    ),
  };
}

export async function gerarLancamentosDocumentosFiscais(
  empresaId: string,
  filtros: FiltrosDocumentosFiscais = {}
) {
  try {
    const resultadoDocumentos = await obterDocumentosParaProcessamentoAutomatico(empresaId, filtros);
    if (!resultadoDocumentos.success) {
      return resultadoDocumentos;
    }

    const documentos = resultadoDocumentos.data || [];

    const resultados = [];
    let criados = 0;
    let ignorados = 0;
    let erros = 0;

    for (const documento of documentos) {
      const resultado = await gerarOuPrevisualizarLancamentoDocumento(documento, empresaId, {
        forcar: true,
      });

      if (resultado.success && !resultado.skipped) criados++;
      if (resultado.success && resultado.skipped) ignorados++;
      if (!resultado.success) erros++;

      resultados.push({
        documento,
        ...resultado,
      });
    }

    const errosDetalhados = resultados
      .filter((resultado: any) => !resultado.success)
      .map((resultado: any) => `${resultado.documento?.numeroDocumento || 'Documento'}: ${resultado.error || resultado.message || 'Erro desconhecido'}`);

    return {
      success: erros === 0,
      data: resultados,
      resumo: {
        total: documentos.length,
        criados,
        ignorados,
        erros,
      },
      errosDetalhados,
      error: erros > 0
        ? `Alguns documentos nao foram lancados. ${errosDetalhados[0] || ''}`.trim()
        : undefined,
    };
  } catch (error: any) {
    console.error('[CONTABILIDADE] Erro ao gerar lancamentos por documentos:', error);
    return { success: false, error: error.message };
  }
}

export async function previsualizarLancamentosDocumentosFiscais(
  empresaId: string,
  filtros: FiltrosDocumentosFiscais = {}
) {
  try {
    const resultadoDocumentos = await obterDocumentosParaProcessamentoAutomatico(empresaId, filtros);
    if (!resultadoDocumentos.success) {
      return resultadoDocumentos;
    }

    const documentos = resultadoDocumentos.data || [];
    const lancamentosExistentes = await kv.getByPrefix<LancamentoContabil>(`lancamento_${empresaId}_`);
    const proximosPorAno = new Map<string, number>();

    const obterReferenciaPreview = (data?: string) => {
      const ano = obterAnoReferenciaLancamento(data);
      if (!proximosPorAno.has(ano)) {
        const ultimoNumero = lancamentosExistentes.reduce((maior, lancamento) => {
          return Math.max(maior, extrairNumeroReferenciaLancamento(lancamento.referencia, ano));
        }, 0);
        proximosPorAno.set(ano, ultimoNumero + 1);
      }

      const numero = proximosPorAno.get(ano) || 1;
      proximosPorAno.set(ano, numero + 1);
      return `LC/${ano}/${String(numero).padStart(4, '0')}`;
    };

    const resultados = [];
    let preparados = 0;
    let ignorados = 0;
    let erros = 0;

    for (const documento of documentos) {
      const resultado = await gerarOuPrevisualizarLancamentoDocumento(documento, empresaId, {
        forcar: true,
        previsualizar: true,
      });

      if (resultado.success && !resultado.skipped) {
        preparados++;
        if (resultado.data) {
          resultado.data.referencia = obterReferenciaPreview(resultado.data.data);
        }
      }
      if (resultado.success && resultado.skipped) ignorados++;
      if (!resultado.success) erros++;

      resultados.push({
        documento,
        ...resultado,
      });
    }

    const errosDetalhados = resultados
      .filter((resultado: any) => !resultado.success)
      .map((resultado: any) => `${resultado.documento?.numeroDocumento || 'Documento'}: ${resultado.error || resultado.message || 'Erro desconhecido'}`);

    return {
      success: erros === 0,
      data: resultados,
      resumo: {
        total: documentos.length,
        preparados,
        ignorados,
        erros,
      },
      errosDetalhados,
      error: erros > 0
        ? `Alguns documentos nao puderam ser preparados. ${errosDetalhados[0] || ''}`.trim()
        : undefined,
    };
  } catch (error: any) {
    console.error('[CONTABILIDADE] Erro ao previsualizar lancamentos por documentos:', error);
    return { success: false, error: error.message };
  }
}

export async function verificarInconsistenciasContabeis(
  empresaId: string,
  filtros: FiltrosDocumentosFiscais = {}
) {
  try {
    const documentosResult = await listarDocumentosFiscaisParaLancamento(empresaId, filtros);
    if (!documentosResult.success) {
      return documentosResult;
    }

    const documentos = documentosResult.data || [];
    const pendentes = documentos.filter((documento: DocumentoFiscalLancamento) => !documento.lancamentoExistente);
    const rascunhos = documentos.filter((documento: DocumentoFiscalLancamento) =>
      documento.lancamentoExistente?.estado === 'rascunho'
    );
    const estornados = documentos.filter((documento: DocumentoFiscalLancamento) =>
      documento.lancamentoExistente?.estado === 'estornado'
    );

    const preview = pendentes.length > 0
      ? await previsualizarLancamentosDocumentosFiscais(empresaId, {
          ...filtros,
          documentoIds: pendentes.map((documento: DocumentoFiscalLancamento) => documento.id),
        })
      : { success: true, data: [], resumo: { erros: 0 }, errosDetalhados: [] };

    const erros = Array.isArray(preview.data)
      ? preview.data.filter((resultado: any) => !resultado.success)
      : [];

    return {
      success: true,
      data: {
        pendentes,
        rascunhos,
        estornados,
        erros,
      },
      resumo: {
        totalDocumentos: documentos.length,
        semLancamento: pendentes.length,
        rascunhos: rascunhos.length,
        estornados: estornados.length,
        errosPreparacao: erros.length,
      },
      errosDetalhados: Array.isArray((preview as any).errosDetalhados) ? (preview as any).errosDetalhados : [],
    };
  } catch (error: any) {
    console.error('[CONTABILIDADE] Erro ao verificar inconsistencias contabeis:', error);
    return { success: false, error: error.message };
  }
}

export async function gerarBalancete(empresaId: string, dataInicio: string, dataFim: string) {
  console.log(`[CONTABILIDADE] Gerando balancete: ${dataInicio} a ${dataFim}`);
  
  try {
    const contas = await kv.getByPrefix(`conta_${empresaId}_`);
    const lancamentos = await kv.getByPrefix(`lancamento_${empresaId}_`);

    const normalizarData = (valor?: string) => {
      const texto = String(valor || '').trim();
      if (!texto) return '';
      if (/^\d{4}-\d{2}-\d{2}/.test(texto)) return texto.slice(0, 10);

      const data = new Date(texto);
      return Number.isNaN(data.getTime()) ? texto : data.toISOString().slice(0, 10);
    };

    const separarSaldo = (valor: number) => ({
      debito: valor > 0 ? valor : 0,
      credito: valor < 0 ? Math.abs(valor) : 0,
    });

    const lancamentosLancados = lancamentos.filter((l: LancamentoContabil) => {
      const dataLancamento = normalizarData(l.data);
      return (
        l.estado === 'lancado' &&
        Boolean(dataLancamento) &&
        dataLancamento <= dataFim &&
        Array.isArray(l.linhas)
      );
    });
    
    const balancete = contas.map((conta: ContaContabil) => {
      let movimentoAnteriorDebito = 0;
      let movimentoAnteriorCredito = 0;
      let debito = 0;
      let credito = 0;
      
      lancamentosLancados.forEach((lancamento: LancamentoContabil) => {
        const dataLancamento = normalizarData(lancamento.data);

        lancamento.linhas.forEach((linha: LinhaLancamento) => {
          if (linha.contaId === conta.id || linha.contaCodigo === conta.codigo) {
            if (dataLancamento < dataInicio) {
              movimentoAnteriorDebito += Number(linha.debito) || 0;
              movimentoAnteriorCredito += Number(linha.credito) || 0;
            } else if (dataLancamento >= dataInicio && dataLancamento <= dataFim) {
              debito += Number(linha.debito) || 0;
              credito += Number(linha.credito) || 0;
            }
          }
        });
      });
      
      const saldoInicial = (Number(conta.saldoInicial) || 0) + movimentoAnteriorDebito - movimentoAnteriorCredito;
      const saldoFinal = saldoInicial + debito - credito;
      const saldoInicialPartido = separarSaldo(saldoInicial);
      const saldoFinalPartido = separarSaldo(saldoFinal);
      
      return {
        contaId: conta.id,
        contaCodigo: conta.codigo,
        contaNome: conta.nome,
        classe: conta.classe,
        nivel: conta.nivel,
        tipo: conta.tipo,
        saldoInicial,
        saldoInicialDebito: saldoInicialPartido.debito,
        saldoInicialCredito: saldoInicialPartido.credito,
        debito,
        credito,
        saldo: saldoFinal,
        saldoFinal,
        saldoFinalDebito: saldoFinalPartido.debito,
        saldoFinalCredito: saldoFinalPartido.credito,
      };
    });
    
    const balanceteComMovimento = balancete.filter(item => 
      item.debito !== 0 || item.credito !== 0 || item.saldoInicial !== 0 || item.saldoFinal !== 0
    );
    
    balanceteComMovimento.sort((a, b) => compararCodigosContas(a.contaCodigo, b.contaCodigo));

    const totais = balanceteComMovimento.reduce(
      (acc, item) => ({
        saldoInicialDebito: acc.saldoInicialDebito + item.saldoInicialDebito,
        saldoInicialCredito: acc.saldoInicialCredito + item.saldoInicialCredito,
        debito: acc.debito + item.debito,
        credito: acc.credito + item.credito,
        saldoFinalDebito: acc.saldoFinalDebito + item.saldoFinalDebito,
        saldoFinalCredito: acc.saldoFinalCredito + item.saldoFinalCredito,
      }),
      {
        saldoInicialDebito: 0,
        saldoInicialCredito: 0,
        debito: 0,
        credito: 0,
        saldoFinalDebito: 0,
        saldoFinalCredito: 0,
      }
    );
    
    return {
      success: true,
      data: balanceteComMovimento,
      totais: {
        ...totais,
        diferencaMovimentos: totais.debito - totais.credito,
        diferencaSaldoFinal: totais.saldoFinalDebito - totais.saldoFinalCredito,
      },
      periodo: { dataInicio, dataFim },
    };
  } catch (error: any) {
    console.error('[CONTABILIDADE] ❌ Erro ao gerar balancete:', error);
    return { success: false, error: error.message };
  }
}

export async function gerarDemonstracaoResultados(empresaId: string, dataInicio: string, dataFim: string) {
  console.log(`[CONTABILIDADE] Gerando demonstração de resultados: ${dataInicio} a ${dataFim}`);
  
  try {
    const contas = await kv.getByPrefix(`conta_${empresaId}_`);
    const lancamentos = await kv.getByPrefix(`lancamento_${empresaId}_`);
    
    const lancamentosPeriodo = lancamentos.filter((l: LancamentoContabil) => 
      l.estado === 'lancado' &&
      l.data >= dataInicio &&
      l.data <= dataFim
    );
    
    let rendimentos = 0;
    let gastos = 0;
    
    // Calcular rendimentos (classe 7) e gastos (classe 6)
    contas.forEach((conta: ContaContabil) => {
      let saldo = 0;
      
      lancamentosPeriodo.forEach((lancamento: LancamentoContabil) => {
        lancamento.linhas.forEach((linha: LinhaLancamento) => {
          if (linha.contaId === conta.id) {
            saldo += linha.credito - linha.debito; // Rendimentos têm saldo credor
          }
        });
      });
      
      if (conta.classe === 7) {
        rendimentos += saldo;
      } else if (conta.classe === 6) {
        gastos += Math.abs(saldo); // Gastos são positivos
      }
    });
    
    const resultado = rendimentos - gastos;
    
    return {
      success: true,
      data: {
        rendimentos,
        gastos,
        resultado,
        periodo: { inicio: dataInicio, fim: dataFim },
      },
    };
  } catch (error: any) {
    console.error('[CONTABILIDADE] ❌ Erro ao gerar demonstração de resultados:', error);
    return { success: false, error: error.message };
  }
}

// ========================================
// MAPEAMENTOS CONTÁBEIS
// ========================================

export async function prepararFechoExercicio(empresaId: string, exercicioId: string, usuarioId?: string) {
  try {
    const exercicios = await exerciciosEconomicos.listarExerciciosEmpresa(empresaId);
    const exercicio = exercicios.find((item) => item.id === exercicioId);

    if (!exercicio) {
      return { success: false, error: 'Exercicio economico nao encontrado' };
    }

    if (exercicio.estado !== 'aberto') {
      return { success: false, error: 'Apenas exercicios abertos podem preparar fecho contabil' };
    }

    const lancamentosExistentes = await kv.getByPrefix<LancamentoContabil>(`lancamento_${empresaId}_`);
    const fechoExistente = lancamentosExistentes.find((lancamento) =>
      lancamento.documentoId === exercicio.id &&
      normalizarTipoDocumentoContabil(lancamento.tipoDocumento) === 'fecho_exercicio' &&
      lancamento.estado !== 'estornado'
    );

    if (fechoExistente) {
      return {
        success: true,
        skipped: true,
        message: 'Ja existe um lancamento de fecho para este exercicio',
        data: fechoExistente,
      };
    }

    const contas = await kv.getByPrefix<ContaContabil>(`conta_${empresaId}_`);
    const contasPorId = new Map<string, ContaContabil>(contas.map((conta) => [conta.id, conta]));
    const contaResultadoId = encontrarContaLancavelPorCodigoOuPrefixo(contas, ['8.8']);
    const contaTransitadosId = encontrarContaLancavelPorCodigoOuPrefixo(contas, ['5.9']);

    if (!contaResultadoId) {
      return { success: false, error: 'Conta 8.8 - Resultado liquido do periodo nao encontrada ou nao lancavel' };
    }

    if (!contaTransitadosId) {
      return { success: false, error: 'Conta 5.9 - Resultados transitados nao encontrada ou nao lancavel' };
    }

    const diarios = await kv.getByPrefix<Diario>(`diario_${empresaId}_`);
    const diarioGeral = diarios
      .map((diario) => normalizarDiario(diario))
      .find((diario) =>
        diario.ativo &&
        !diario.bloqueado &&
        (diario.tipo === 'geral' || String(diario.codigo || '').toUpperCase() === 'GER')
      );

    if (!diarioGeral) {
      return { success: false, error: 'Diario geral aberto nao encontrado para preparar o fecho' };
    }

    const balancete = await gerarBalancete(empresaId, exercicio.dataInicio, exercicio.dataFim);
    if (!balancete.success) {
      return balancete;
    }

    const linhas: Array<Partial<LinhaLancamento>> = [];
    let totalRendimentos = 0;
    let totalGastos = 0;
    const arredondar = (valor: number) => Math.round((Number(valor) || 0) * 100) / 100;
    const adicionarLinha = (contaId: string, descricao: string, debito: number, credito: number) => {
      const valorDebito = arredondar(debito);
      const valorCredito = arredondar(credito);
      if (valorDebito <= 0 && valorCredito <= 0) return;
      linhas.push({ contaId, descricao, debito: valorDebito, credito: valorCredito });
    };

    for (const item of balancete.data || []) {
      const conta = contasPorId.get(item.contaId);
      if (!conta || conta.aceitaLancamentos === false) continue;

      const codigo = String(item.contaCodigo || conta.codigo || '');
      const saldo = arredondar(Number(item.saldoFinal ?? item.saldo ?? 0));
      if (Math.abs(saldo) < 0.01) continue;

      if (codigo.startsWith('7')) {
        totalRendimentos += -saldo;
        const valor = Math.abs(saldo);
        if (saldo < 0) {
          adicionarLinha(conta.id, `Fecho do exercicio ${exercicio.ano} - ${conta.nome}`, valor, 0);
          adicionarLinha(contaResultadoId, `Apuramento de rendimentos ${exercicio.ano}`, 0, valor);
        } else {
          adicionarLinha(contaResultadoId, `Anulacao de saldo de rendimentos ${exercicio.ano}`, valor, 0);
          adicionarLinha(conta.id, `Fecho do exercicio ${exercicio.ano} - ${conta.nome}`, 0, valor);
        }
      }

      if (codigo.startsWith('6')) {
        totalGastos += saldo;
        const valor = Math.abs(saldo);
        if (saldo > 0) {
          adicionarLinha(contaResultadoId, `Apuramento de gastos ${exercicio.ano}`, valor, 0);
          adicionarLinha(conta.id, `Fecho do exercicio ${exercicio.ano} - ${conta.nome}`, 0, valor);
        } else {
          adicionarLinha(conta.id, `Fecho do exercicio ${exercicio.ano} - ${conta.nome}`, valor, 0);
          adicionarLinha(contaResultadoId, `Anulacao de saldo de gastos ${exercicio.ano}`, 0, valor);
        }
      }
    }

    const resultadoPeriodo = arredondar(totalRendimentos - totalGastos);
    if (Math.abs(resultadoPeriodo) >= 0.01) {
      if (resultadoPeriodo > 0) {
        adicionarLinha(contaResultadoId, `Transferencia do resultado liquido ${exercicio.ano}`, resultadoPeriodo, 0);
        adicionarLinha(contaTransitadosId, `Resultado liquido do exercicio ${exercicio.ano}`, 0, resultadoPeriodo);
      } else {
        const perda = Math.abs(resultadoPeriodo);
        adicionarLinha(contaTransitadosId, `Resultado liquido negativo do exercicio ${exercicio.ano}`, perda, 0);
        adicionarLinha(contaResultadoId, `Transferencia do resultado liquido ${exercicio.ano}`, 0, perda);
      }
    }

    if (linhas.length < 2) {
      return { success: false, error: 'Nao existem rendimentos ou gastos lancados para preparar o fecho' };
    }

    const resultado = await criarLancamento({
      empresaId,
      diarioId: diarioGeral.id,
      data: exercicio.dataFim,
      descricao: `Apuramento e fecho do exercicio ${exercicio.ano}`,
      numeroDocumento: `FECHO/${exercicio.ano}`,
      tipoDocumento: 'fecho_exercicio',
      documentoId: exercicio.id,
      origemLancamento: 'manual',
      estado: 'rascunho',
      observacoes: usuarioId ? `Preparado por ${usuarioId}` : 'Preparado automaticamente pelo sistema',
      linhas,
      totalDebito: 0,
      totalCredito: 0,
    } as any);

    if (!resultado.success) {
      return resultado;
    }

    return {
      success: true,
      data: resultado.data,
      resumo: {
        rendimentos: arredondar(totalRendimentos),
        gastos: arredondar(totalGastos),
        resultado: resultadoPeriodo,
      },
    };
  } catch (error: any) {
    console.error('[CONTABILIDADE] Erro ao preparar fecho do exercicio:', error);
    return { success: false, error: error.message };
  }
}

const estadosModeloFiscal: EstadoModeloFiscal[] = [
  'rascunho',
  'preenchido',
  'fechado',
  'aprovado',
  'substituido',
  'submetido',
  'arquivado',
];

const estadosBloqueadosModeloFiscal: EstadoModeloFiscal[] = ['fechado', 'aprovado', 'substituido', 'submetido', 'arquivado'];

const chaveModeloFiscal = (empresaId: string, ano: number | string, modeloId: string) =>
  `modelo_fiscal_${empresaId}_${ano}_${modeloId}`;

const prefixoModeloFiscal = (empresaId: string, ano: number | string, modeloId?: string) =>
  `modelo_fiscal_${empresaId}_${ano}_${modeloId ? `${modeloId}_` : ''}`;

const normalizarTokenModeloFiscal = (valor: string) =>
  String(valor || '')
    .trim()
    .toLowerCase()
    .normalize('NFD')
    .replace(/[\u0300-\u036f]/g, '')
    .replace(/[^a-z0-9]+/g, '-')
    .replace(/^-+|-+$/g, '') || 'modelo';

const chaveModeloFiscalPeriodo = (
  empresaId: string,
  ano: number | string,
  modeloId: string,
  dataInicio: string,
  dataFim: string,
) =>
  `${prefixoModeloFiscal(empresaId, ano, modeloId)}${normalizarTokenModeloFiscal(dataInicio)}_${normalizarTokenModeloFiscal(dataFim)}_${Date.now().toString(36)}`;

const modeloFiscalEstaBloqueado = (estado?: EstadoModeloFiscal) =>
  estadosBloqueadosModeloFiscal.includes(normalizarEstadoModeloFiscal(estado));

const normalizarEstadoModeloFiscal = (estado: unknown): EstadoModeloFiscal => {
  const valor = String(estado || '').trim().toLowerCase() as EstadoModeloFiscal;
  return estadosModeloFiscal.includes(valor) ? valor : 'rascunho';
};

async function gerarHashConteudo(valor: unknown) {
  const texto = JSON.stringify(valor ?? {});
  const bytes = new TextEncoder().encode(texto);
  const hash = await crypto.subtle.digest('SHA-256', bytes);
  return Array.from(new Uint8Array(hash)).map((byte) => byte.toString(16).padStart(2, '0')).join('');
}

export async function listarModelosFiscais(empresaId: string, ano?: number | string) {
  try {
    const prefixo = ano ? `modelo_fiscal_${empresaId}_${ano}_` : `modelo_fiscal_${empresaId}_`;
    const modelos = (await kv.getByPrefix<ModeloFiscalContabil>(prefixo))
      .sort((a, b) =>
        Number(b.ano || 0) - Number(a.ano || 0) ||
        new Date(b.atualizadoEm || b.criadoEm || 0).getTime() - new Date(a.atualizadoEm || a.criadoEm || 0).getTime() ||
        String(a.modeloCodigo || a.modeloId).localeCompare(String(b.modeloCodigo || b.modeloId), 'pt', { numeric: true, sensitivity: 'base' })
      );
    return { success: true, data: modelos };
  } catch (error: any) {
    console.error('[CONTABILIDADE] Erro ao listar modelos fiscais:', error);
    return { success: false, error: error.message };
  }
}

export async function obterModeloFiscal(empresaId: string, ano: number | string, modeloId: string) {
  try {
    const modeloLegacy = await kv.get(chaveModeloFiscal(empresaId, ano, modeloId)) as ModeloFiscalContabil | undefined;
    const modelosPeriodo = (await kv.getByPrefix<ModeloFiscalContabil>(prefixoModeloFiscal(empresaId, ano, modeloId)))
      .filter((modelo) => modelo.modeloId === modeloId)
      .sort((a, b) =>
        Number(!modeloFiscalEstaBloqueado(b.estado)) - Number(!modeloFiscalEstaBloqueado(a.estado)) ||
        new Date(b.atualizadoEm || b.criadoEm || 0).getTime() - new Date(a.atualizadoEm || a.criadoEm || 0).getTime()
      );
    const modelo = modelosPeriodo[0] || modeloLegacy;
    return { success: true, data: modelo || null };
  } catch (error: any) {
    console.error('[CONTABILIDADE] Erro ao obter modelo fiscal:', error);
    return { success: false, error: error.message };
  }
}

export async function salvarModeloFiscal(empresaId: string, dados: Partial<ModeloFiscalContabil>, usuarioId?: string) {
  try {
    const ano = Number(dados.ano);
    const modeloId = String(dados.modeloId || '').trim();
    const modeloCodigo = String(dados.modeloCodigo || modeloId || '').trim();
    const modeloNome = String(dados.modeloNome || modeloCodigo || '').trim();

    if (!empresaId || !ano || !modeloId || !modeloCodigo || !modeloNome) {
      return { success: false, error: 'Empresa, ano e modelo fiscal sao obrigatorios' };
    }

    const dataInicio = exerciciosEconomicos.normalizarDataISO(dados.periodo?.dataInicio || `${ano}-01-01`);
    const dataFim = exerciciosEconomicos.normalizarDataISO(dados.periodo?.dataFim || `${ano}-12-31`);
    if (dataInicio > dataFim) {
      return { success: false, error: 'Periodo do modelo fiscal invalido' };
    }

    const exercicio = await exerciciosEconomicos.garantirExercicioParaData(empresaId, dataInicio, usuarioId);
    if (exercicio.estado === 'bloqueado') {
      return { success: false, error: `O exercicio ${exercicio.nome} esta bloqueado. Nao e possivel alterar modelos fiscais.` };
    }

    const idInformado = String(dados.id || '').trim();
    const rascunhosDoPeriodo = idInformado
      ? []
      : (await kv.getByPrefix<ModeloFiscalContabil>(prefixoModeloFiscal(empresaId, ano, modeloId)))
        .filter((item) =>
          item.modeloId === modeloId &&
          item.periodo?.dataInicio === dataInicio &&
          item.periodo?.dataFim === dataFim &&
          !modeloFiscalEstaBloqueado(item.estado)
        )
        .sort((a, b) => new Date(b.atualizadoEm || b.criadoEm || 0).getTime() - new Date(a.atualizadoEm || a.criadoEm || 0).getTime());
    const id = idInformado || rascunhosDoPeriodo[0]?.id || chaveModeloFiscalPeriodo(empresaId, ano, modeloId, dataInicio, dataFim);
    const existente = await kv.get(id) as ModeloFiscalContabil | undefined;
    if (existente && existente.empresaId !== empresaId) {
      return { success: false, error: 'Modelo fiscal nao encontrado para esta empresa' };
    }
    if (existente && modeloFiscalEstaBloqueado(existente.estado)) {
      return {
        success: false,
        error: 'Esta declaracao fiscal ja esta fechada e nao pode ser alterada. Crie uma declaracao de substituicao.',
      };
    }

    const agora = new Date().toISOString();
    const estado = normalizarEstadoModeloFiscal(dados.estado || existente?.estado || 'rascunho');
    const camposManuais = dados.camposManuais && typeof dados.camposManuais === 'object' ? dados.camposManuais : {};
    const camposGerados = Array.isArray(dados.camposGerados) ? dados.camposGerados : [];
    const hashConteudo = await gerarHashConteudo({
      empresaId,
      ano,
      modeloId,
      periodo: { dataInicio, dataFim },
      camposManuais,
      camposGerados,
      estado,
    });
    const versao = (Number(existente?.versao) || 0) + 1;

    const historico = [
      ...(Array.isArray(existente?.historico) ? existente!.historico : []),
      {
        data: agora,
        usuarioId,
        acao: existente ? 'actualizar_modelo_fiscal' : 'criar_modelo_fiscal',
        estado,
        versao,
        hashConteudo,
      },
    ].slice(-100);

    const modelo: ModeloFiscalContabil = {
      ...(existente || {}),
      id,
      empresaId,
      ano,
      modeloId,
      modeloCodigo,
      modeloNome,
      natureza: dados.natureza || existente?.natureza,
      periodo: { dataInicio, dataFim },
      estado,
      versao,
      versaoModelo: dados.versaoModelo || existente?.versaoModelo || `${ano}.1`,
      fonteTemplate: dados.fonteTemplate || existente?.fonteTemplate || 'modelo_oficial_at_campos_editaveis',
      camposManuais,
      camposGerados,
      hashConteudo,
      criadoEm: existente?.criadoEm || agora,
      criadoPor: existente?.criadoPor || usuarioId,
      atualizadoEm: agora,
      atualizadoPor: usuarioId,
      aprovadoEm: estado === 'aprovado' && existente?.estado !== 'aprovado' ? agora : existente?.aprovadoEm,
      aprovadoPor: estado === 'aprovado' && existente?.estado !== 'aprovado' ? usuarioId : existente?.aprovadoPor,
      fechadoEm: estado === 'fechado' && existente?.estado !== 'fechado' ? agora : existente?.fechadoEm,
      fechadoPor: estado === 'fechado' && existente?.estado !== 'fechado' ? usuarioId : existente?.fechadoPor,
      submetidoEm: estado === 'submetido' && existente?.estado !== 'submetido' ? agora : existente?.submetidoEm,
      submetidoPor: estado === 'submetido' && existente?.estado !== 'submetido' ? usuarioId : existente?.submetidoPor,
      historico,
    };

    await kv.set(id, modelo);
    return { success: true, data: modelo };
  } catch (error: any) {
    console.error('[CONTABILIDADE] Erro ao salvar modelo fiscal:', error);
    return { success: false, error: error.message };
  }
}

export async function alterarEstadoModeloFiscal(
  empresaId: string,
  ano: number | string,
  modeloId: string,
  estado: EstadoModeloFiscal,
  usuarioId?: string,
) {
  const legado = await kv.get(chaveModeloFiscal(empresaId, ano, modeloId)) as ModeloFiscalContabil | undefined;
  const recente = (await kv.getByPrefix<ModeloFiscalContabil>(prefixoModeloFiscal(empresaId, ano, modeloId)))
    .filter((modelo) => modelo.modeloId === modeloId)
    .sort((a, b) =>
      Number(!modeloFiscalEstaBloqueado(b.estado)) - Number(!modeloFiscalEstaBloqueado(a.estado)) ||
      new Date(b.atualizadoEm || b.criadoEm || 0).getTime() - new Date(a.atualizadoEm || a.criadoEm || 0).getTime()
    )[0];
  const existente = recente || legado;
  if (!existente) {
    return { success: false, error: 'Modelo fiscal ainda nao foi guardado' };
  }

  return await salvarModeloFiscal(empresaId, {
    ...existente,
    estado,
  }, usuarioId);
}

export async function fecharDefinitivamenteExercicio(empresaId: string, exercicioId: string, usuarioId?: string) {
  try {
    const exercicios = await exerciciosEconomicos.listarExerciciosEmpresa(empresaId);
    const exercicio = exercicios.find((item) => item.id === exercicioId);

    if (!exercicio) {
      return { success: false, error: 'Exercicio economico nao encontrado' };
    }

    if (exercicio.estado === 'bloqueado') {
      return { success: true, skipped: true, message: 'Exercicio ja esta bloqueado definitivamente', data: exercicio };
    }

    const inconsistencias = await verificarInconsistenciasContabeis(empresaId);
    if (inconsistencias.success) {
      const resumo = inconsistencias.resumo || {};
      const existemPendencias =
        Number(resumo.semLancamento || 0) > 0 ||
        Number(resumo.rascunhos || 0) > 0 ||
        Number(resumo.errosPreparacao || 0) > 0;
      if (existemPendencias) {
        return {
          success: false,
          error: 'Nao e possivel fechar definitivamente: existem documentos ou lancamentos contabilisticos pendentes.',
          data: inconsistencias,
        };
      }
    }

    const preparar = await prepararFechoExercicio(empresaId, exercicioId, usuarioId);
    if (!preparar.success) {
      return preparar;
    }

    const lancamentos = await kv.getByPrefix<LancamentoContabil>(`lancamento_${empresaId}_`);
    let lancamentoFecho = lancamentos.find((lancamento) =>
      lancamento.documentoId === exercicio.id &&
      normalizarTipoDocumentoContabil(lancamento.tipoDocumento) === 'fecho_exercicio' &&
      lancamento.estado !== 'estornado'
    );

    if (!lancamentoFecho) {
      return { success: false, error: 'Lancamento de fecho nao encontrado apos preparacao' };
    }

    if (lancamentoFecho.estado === 'rascunho') {
      const lancar = await lancarLancamento(lancamentoFecho.id);
      if (!lancar.success) return lancar;
      lancamentoFecho = lancar.data;
    }

    if (lancamentoFecho.estado !== 'lancado') {
      return { success: false, error: 'Lancamento de fecho precisa estar lancado antes do bloqueio definitivo' };
    }

    const modelos = await listarModelosFiscais(empresaId, exercicio.ano);
    const modelosPendentes = modelos.success
      ? (modelos.data || []).filter((modelo) => !modeloFiscalEstaBloqueado(modelo.estado))
      : [];

    const actualizados = await exerciciosEconomicos.alterarEstadoExercicioEmpresa(
      empresaId,
      exercicioId,
      'bloqueado',
      usuarioId,
      `Fecho definitivo efectuado em ${new Date().toISOString()}`,
    );

    const registoFecho = {
      id: `fecho_definitivo_${empresaId}_${exercicio.ano}`,
      empresaId,
      exercicioId,
      ano: exercicio.ano,
      lancamentoFechoId: lancamentoFecho.id,
      lancamentoFechoReferencia: lancamentoFecho.referencia,
      fechadoEm: new Date().toISOString(),
      fechadoPor: usuarioId,
      modelosFiscais: modelos.success ? (modelos.data || []).map((modelo) => ({
        modeloId: modelo.modeloId,
        codigo: modelo.modeloCodigo,
        estado: modelo.estado,
        versao: modelo.versao,
        hashConteudo: modelo.hashConteudo,
      })) : [],
      modelosPendentes: modelosPendentes.map((modelo) => modelo.modeloCodigo || modelo.modeloId),
      observacoes: modelosPendentes.length
        ? 'Fecho bloqueado com modelos fiscais ainda nao fechados/submetidos; validar antes da entrega oficial.'
        : 'Fecho definitivo concluido sem modelos fiscais pendentes.',
    };

    await kv.set(registoFecho.id, registoFecho);

    return {
      success: true,
      data: {
        exercicios: actualizados,
        exercicio: actualizados.find((item) => item.id === exercicioId),
        lancamentoFecho,
        registoFecho,
      },
      warnings: modelosPendentes.length
        ? [`Existem ${modelosPendentes.length} modelo(s) fiscal(is) ainda nao fechados/submetidos.`]
        : [],
    };
  } catch (error: any) {
    console.error('[CONTABILIDADE] Erro no fecho definitivo do exercicio:', error);
    return { success: false, error: error.message };
  }
}

// Campos do mapeamento que referenciam uma conta do plano de contas — validados
// em criarMapeamento para nao deixar gravar uma referencia a uma conta que ja
// nao existe ou que deixou de aceitar lancamentos (ex: por ter ganho
// subcontas entretanto), o que so seria descoberto mais tarde, ao falhar o
// lancamento automatico de uma factura/compra real.
const CAMPOS_CONTA_MAPEAMENTO: Array<{ campo: keyof MapeamentoContabil; rotulo: string }> = [
  { campo: 'contaVendas', rotulo: 'Conta de vendas' },
  { campo: 'contaCompras', rotulo: 'Conta de compras' },
  { campo: 'contaEstoque', rotulo: 'Conta de estoque' },
  { campo: 'contaReceber', rotulo: 'Conta de clientes' },
  { campo: 'contaPagar', rotulo: 'Conta de fornecedores' },
  { campo: 'contaIVALiquidado', rotulo: 'Conta de IVA liquidado' },
  { campo: 'contaIVADedutivel', rotulo: 'Conta de IVA dedutivel' },
  { campo: 'contaCMV', rotulo: 'Conta de CMV' },
  { campo: 'contaPerdasStock', rotulo: 'Conta de perdas de stock' },
  { campo: 'contaRegularizacaoStock', rotulo: 'Conta de regularizacao de stock' },
  { campo: 'contaAdiantamentoFornecedor', rotulo: 'Conta de adiantamento a fornecedor' },
  { campo: 'contaCaixa', rotulo: 'Conta caixa' },
  { campo: 'contaBanco', rotulo: 'Conta banco' },
];

export async function criarMapeamento(dados: Omit<MapeamentoContabil, 'id'>) {
  try {
    const contasEmpresa = await kv.getByPrefix<ContaContabil>(`conta_${dados.empresaId}_`);
    for (const { campo, rotulo } of CAMPOS_CONTA_MAPEAMENTO) {
      const contaId = dados[campo] as string | undefined;
      if (!contaId) continue;
      const conta = contasEmpresa.find((item) => item.id === contaId);
      if (!conta) {
        return { success: false, error: `${rotulo}: conta seleccionada nao foi encontrada.` };
      }
      if (conta.aceitaLancamentos === false) {
        return { success: false, error: `${rotulo}: a conta "${conta.codigo} - ${conta.nome}" nao aceita lancamentos (tem subcontas). Escolha uma subconta.` };
      }
    }

    const existentes = await kv.getByPrefixWithKeys<MapeamentoContabil>(`mapeamento_${dados.empresaId}_`);
    const existente = existentes.find(({ value }) => value.tipoDocumento === dados.tipoDocumento);

    const mapeamento: MapeamentoContabil = {
      ...(existente?.value || {}),
      ...dados,
      id: existente?.value.id || `mapeamento_${dados.empresaId}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
      automatico: dados.automatico ?? existente?.value.automatico ?? false,
    };
    
    await kv.set(mapeamento.id, mapeamento);
    console.log(`[CONTABILIDADE] ✅ Mapeamento criado para: ${mapeamento.tipoDocumento}`);
    
    return { success: true, data: mapeamento };
  } catch (error: any) {
    return { success: false, error: error.message };
  }
}

export async function obterMapeamentos(empresaId: string) {
  try {
    const mapeamentos = await kv.getByPrefix(`mapeamento_${empresaId}_`);
    return { success: true, data: mapeamentos };
  } catch (error: any) {
    return { success: false, error: error.message };
  }
}

export async function inicializarMapeamentosBase(empresaId: string) {
  console.log(`[CONTABILIDADE] Inicializando mapeamentos base para empresa: ${empresaId}`);
  
  try {
    // Buscar contas necessárias
    const contas = await kv.getByPrefix(`conta_${empresaId}_`);
    
    const findConta = (...codigos: string[]) =>
      encontrarContaLancavelPorCodigoOuPrefixo(contas as ContaContabil[], codigos) ||
      (contas.find((c: ContaContabil) => codigos.includes(c.codigo)) as ContaContabil)?.id;
    
    const mapeamentosBase: Omit<MapeamentoContabil, 'id'>[] = [
      {
        empresaId,
        tipoDocumento: 'fatura',
        automatico: false,
        contaVendas: findConta('7.1.1', '71.01'),
        contaReceber: findConta('4.1.1', '21.01'),
        contaIVALiquidado: findConta('4.4.3.3.1', '24.01'),
        contaCMV: findConta('6.1.1.2', '61.01'),
        contaEstoque: findConta('2.2.3', '31.01'),
      },
      {
        empresaId,
        tipoDocumento: 'compra',
        automatico: false,
        contaCompras: findConta('2.2.3', '31.01'),
        contaPagar: findConta('4.2.1', '22.01'),
        contaIVADedutivel: findConta('4.4.3.2.1', '24.02'),
        contaEstoque: findConta('2.2.3', '31.01'),
      },
      {
        empresaId,
        tipoDocumento: 'recibo',
        automatico: false,
        contaCaixa: findConta('1.1', '11.01'),
        contaBanco: findConta('1.2.1', '12.01', '1.2', '12'),
        contaReceber: findConta('4.1.1', '21.01'),
      },
      {
        empresaId,
        tipoDocumento: 'adiantamento',
        automatico: false,
        contaCaixa: findConta('1.1', '11.01'),
        contaBanco: findConta('1.2.1', '12.01', '1.2', '12'),
        contaReceber: findConta('4.1.9', '21.09', '4.1.1'),
      },
      {
        empresaId,
        tipoDocumento: 'pagamento',
        automatico: false,
        contaCaixa: findConta('1.1', '11.01'),
        contaBanco: findConta('1.2.1', '12.01', '1.2', '12'),
        contaPagar: findConta('4.2.1', '22.01'),
        contaAdiantamentoFornecedor: findConta('4.2.9'),
      },
      {
        empresaId,
        tipoDocumento: 'movimento_stock',
        automatico: true,
        contaEstoque: findConta('2.2.3', '31.01'),
        contaCMV: findConta('6.1.1.2', '61.01'),
        contaPerdasStock: findConta('6.8.4.2', '6.8.4', '6.1.1.2'),
        contaRegularizacaoStock: findConta('7.4.1.1', '7.4.1'),
      },
      {
        empresaId,
        tipoDocumento: 'salarios',
        automatico: true,
        contaGastoRemuneracoes: findConta('6.2.2'),
        contaGastoEncargosSociais: findConta('6.2.3'),
        contaINSSPagar: findConta('4.4.9'),
        contaIRPSPagar: findConta('4.4.2.1'),
        contaOutrosDescontosPessoal: findConta('4.6.2.9'),
        contaRemuneracoesPagar: findConta('4.6.2.2'),
      },
    ];
    
    const mapeamentosExistentes = await kv.getByPrefixWithKeys<MapeamentoContabil>(`mapeamento_${empresaId}_`);
    if (mapeamentosExistentes.length > 0) {
      await apagarChavesEmLotes(mapeamentosExistentes.map(({ key }) => key));
    }
    const mapeamentosCriados = [];
    for (const mapeamentoBase of mapeamentosBase) {
      const resultado = await criarMapeamento(mapeamentoBase);
      if (resultado.success) {
        mapeamentosCriados.push(resultado.data);
      }
    }
    
    console.log(`[CONTABILIDADE] Mapeamentos substituidos: ${mapeamentosCriados.length}`);
    return {
      success: true,
      data: mapeamentosCriados,
      created: mapeamentosCriados.length,
      replaced: mapeamentosExistentes.length,
    };
  } catch (error: any) {
    console.error('[CONTABILIDADE] ❌ Erro ao inicializar mapeamentos:', error);
    return { success: false, error: error.message };
  }
}
