﻿import * as kv from "./kv_store.tsx";
import { createLogger } from "./logger.tsx";
import * as facturas from "./facturas.tsx";
import * as adiantamentos from "./adiantamentos.tsx";
import { validarDataEmissaoCronologica } from "./documento-cronologia.tsx";
import * as exerciciosEconomicos from "./exercicios-economicos.tsx";
import {
  assegurarSemReservaPosterior,
  executarComBloqueioNumeracao,
  libertarReservaNumero,
  libertarReservaNumeroDoDocumento,
  reservarNumeroSequencial,
  restaurarReservaNumeroDoDocumento,
  type EscopoNumeroDocumento,
} from "./document-numbering.tsx";
import {
  arquivarDocumentoEliminado,
  type ContextoEliminacaoDocumento,
} from "./document-deletion-audit.tsx";
import {
  assegurarSemLancamentoContabilDefinitivo,
  removerLancamentosContabeisRascunhoDoDocumento,
  restaurarLancamentosContabeisRascunho,
  verificarSemLancamentoContabilDefinitivo,
} from "./integridade-documentos.tsx";
import * as integracaoFiscal from "./integracao-fiscal.tsx";

const logger = createLogger('Recibos');

// ========== INTERFACES ==========
interface PagamentoFactura {
  id: string;
  facturaId: string;
  adiantamentoId?: string;
  tipoDocumento?: 'factura' | 'adiantamento';
  facturaNumero: string;
  adiantamentoNumero?: string;
  facturaDataEmissao: string;
  facturaTotal: number;
  valorPago: number;
  meioPagamento: 'dinheiro' | 'transferencia' | 'cheque' | 'outro';
  banco?: string;
  referencia?: string;
  dataEmissaoRecibo: string;
}

interface Cliente {
  codigo: string;
  nome: string;
  nuit: string;
  telefone?: string;
  email?: string;
  endereco?: string;
}

interface Recibo {
  id: string;
  empresaId: string;
  empresaSnapshot?: Record<string, unknown>;
  tipo: 'recibo';
  numeroDocumento: string;
  dataEmissao: string;
  exercicioEconomicoId?: string;
  anoExercicio?: number;
  estado: 'emitido' | 'anulado';
  dataAnulacao?: string;
  motivoAnulacao?: string;
  clienteId: string;
  cliente: Cliente;
  moeda: string;
  pagamentos: PagamentoFactura[];
  subtotal?: number;
  totalImpostos?: number;
  totalIsento?: number;
  totalGeral: number;
  observacoes?: string;
  meioPagamento?: 'numerario' | 'transferencia' | 'cheque' | 'deposito' | 'emola' | 'mpesa' | 'mkesh';
  banco?: string;
  contaBancaria?: string;
  numeroCheque?: string;
  referenciaTransferencia?: string;
  referenciaPagamento?: string;
  dataPagamento?: string;
  dataCriacao: string;
  dataAtualizacao?: string;
  criadoPor?: string;
  criadoPorId?: string;
  criadoPorNome?: string;
  criadoPorEmail?: string;
  usuarioAuditoriaId?: string;
  usuarioAuditoriaNome?: string;
  usuarioAuditoriaEmail?: string;
  editadoPor?: string;
  editadoPorId?: string;
  editadoPorNome?: string;
  editadoPorEmail?: string;
  atualizadoPor?: string;
  atualizadoPorNome?: string;
  atualizadoPorEmail?: string;
}

// ========== FUNÇÕES DE RECIBOS ==========

export async function listarRecibosPorEmpresa(empresaId: string): Promise<Recibo[]> {
  logger.info(`Buscando recibos com prefixo: recibo:${empresaId}:`);
  const recibos = await kv.getByPrefix(`recibo:${empresaId}:`);
  logger.info(`Encontrados ${recibos.length} recibos para empresa ${empresaId}`);
  return recibos.sort((a: Recibo, b: Recibo) =>
    new Date(b.dataCriacao).getTime() - new Date(a.dataCriacao).getTime()
  );
}

function obterAnoRecibo(recibo: Pick<Recibo, 'dataEmissao' | 'numeroDocumento'>): number {
  const anoNumero = Number(String(recibo.numeroDocumento || '').match(/\/(\d{4})/)?.[1]);
  if (Number.isFinite(anoNumero) && anoNumero > 0) return anoNumero;

  if (recibo.dataEmissao?.includes('-')) {
    const ano = Number.parseInt(recibo.dataEmissao.split('T')[0].split('-')[0], 10);
    if (Number.isFinite(ano) && ano > 0) return ano;
  }

  const dataEmissao = new Date(recibo.dataEmissao);
  return Number.isFinite(dataEmissao.getFullYear()) ? dataEmissao.getFullYear() : new Date().getFullYear();
}

function extrairSequenciaRecibo(recibo: Pick<Recibo, 'numeroDocumento'>, ano: number): number | null {
  const numeroDocumento = String(recibo.numeroDocumento || '').trim();
  const match = numeroDocumento.match(new RegExp(`^Recibo n[º°]\\s*(\\d+)\\/${ano}$`, 'i'));
  if (!match) return null;

  const sequencia = Number.parseInt(match[1], 10);
  return Number.isFinite(sequencia) && sequencia > 0 ? sequencia : null;
}

async function validarReciboEhUltimo(recibo: Recibo, acao: 'editado' | 'eliminado'): Promise<void> {
  const ano = obterAnoRecibo(recibo);
  const recibos = await listarRecibosPorEmpresa(recibo.empresaId);
  const recibosOrdenados = recibos
    .map((item) => ({
      recibo: item,
      sequencia: extrairSequenciaRecibo(item, ano),
    }))
    .filter((item): item is { recibo: Recibo; sequencia: number } =>
      item.sequencia !== null && String(item.recibo.estado || '').toLowerCase() !== 'anulado')
    .sort((a, b) => a.sequencia - b.sequencia);

  const indiceRecibo = recibosOrdenados.findIndex((item) => item.recibo.id === recibo.id);
  const ultimoRecibo = recibosOrdenados[recibosOrdenados.length - 1]?.recibo;

  if (ultimoRecibo && recibo.id !== ultimoRecibo.id) {
    throw new Error(
      `Apenas o ultimo recibo (${ultimoRecibo.numeroDocumento}) pode ser ${acao}. Existem ${recibosOrdenados.length - indiceRecibo - 1} recibo(s) posterior(es).`,
    );
  }
}

export async function buscarReciboPorId(id: string): Promise<Recibo | null> {
  const recibos = await kv.getByPrefix(`recibo:`);
  return recibos.find((r: Recibo) => r.id === id) || null;
}

export async function podeAlterarRecibo(
  id: string,
  acao: 'editado' | 'eliminado',
): Promise<{ pode: boolean; motivo?: string }> {
  const recibo = await buscarReciboPorId(id);
  if (!recibo) return { pode: false, motivo: 'Recibo nao encontrado.' };
  if (String(recibo.estado || '').toLowerCase() === 'anulado') {
    return { pode: false, motivo: 'Um recibo anulado e apenas consultado.' };
  }

  try {
    await validarReciboEhUltimo(recibo, acao);
    await exerciciosEconomicos.validarExercicioAberto(recibo.empresaId, recibo.dataEmissao, 'recibo');
  } catch (error) {
    return { pode: false, motivo: error instanceof Error ? error.message : String(error) };
  }

  return verificarSemLancamentoContabilDefinitivo({
    empresaId: recibo.empresaId,
    documentoId: recibo.id,
    numeroDocumento: recibo.numeroDocumento,
    rotuloDocumento: 'Recibo',
  });
}

async function recalcularEstadoFacturasRelacionadas(
  empresaId: string,
  facturaIds: Iterable<string>,
): Promise<void> {
  const idsUnicos = [...new Set(Array.from(facturaIds).filter(Boolean))];
  if (idsUnicos.length === 0) {
    return;
  }

  for (const facturaId of idsUnicos) {
    try {
      await facturas.recalcularEstadoFactura(facturaId);
      // ✅ Se recalculou com sucesso desta vez, limpar qualquer pendência anterior.
      await integracaoFiscal.resolverPendencia(empresaId, facturaId).catch(() => null);
    } catch (error) {
      logger.error(`Erro ao recalcular estado da factura ${facturaId}:`, error);
      // ✅ Não propagar o erro (a operação no recibo já foi concluída), mas registar
      // como pendência em vez de perder o erro só num log — sem isto a factura podia
      // ficar indefinidamente com um estado (ex: "paga") que já não corresponde à
      // realidade, sem nenhum rasto de que precisa de ser corrigida manualmente.
      await integracaoFiscal.registrarPendencia(
        { empresaId, id: facturaId, numeroDocumento: facturaId, tipoDocumento: 'factura' },
        [`Falha ao recalcular estado após alteração de recibo: ${error instanceof Error ? error.message : String(error)}`],
      ).catch((pendenciaError) => {
        logger.error(`Erro ao registar pendencia para factura ${facturaId}:`, pendenciaError);
      });
    }
  }
}

export async function preverProximoNumeroRecibo(empresaId: string, anoExercicio?: number): Promise<string> {
  const anoAtual = anoExercicio || new Date().getFullYear();
  const recibos = await listarRecibosPorEmpresa(empresaId);
  const regex = new RegExp(`^Recibo n[º°] (\\d+)/${anoAtual}$`, 'i');
  let maxNumero = 0;

  for (const recibo of recibos) {
    const match = recibo.numeroDocumento.match(regex);
    if (match) {
      const num = parseInt(match[1], 10);
      if (num > maxNumero) {
        maxNumero = num;
      }
    }
  }

  const proximoNumero = maxNumero + 1;
  return `Recibo nº ${proximoNumero}/${anoAtual} (Preview)`;
}

export async function gerarProximoNumeroRecibo(
  empresaId: string,
  anoExercicio?: number,
  numeroPretendido?: string,
): Promise<string> {
  const numeroPreview = numeroPretendido || await preverProximoNumeroRecibo(empresaId, anoExercicio);
  return numeroPreview.replace(/\s+\(preview\)$/i, '').trim();
}

export async function criarRecibo(dadosRecibo: any): Promise<Recibo> {
  const exercicio = await exerciciosEconomicos.validarExercicioAberto(
    dadosRecibo.empresaId,
    dadosRecibo.dataEmissao,
    'recibo',
  );
  const recibosExistentes = await listarRecibosPorEmpresa(dadosRecibo.empresaId);
  const reservaNumero = await reservarNumeroSequencial<Recibo>({
    empresaId: dadosRecibo.empresaId,
    modulo: "faturacao-vendas",
    tipoDocumento: "recibo",
    ano: exercicio.ano,
    numeroPretendido: dadosRecibo.numeroDocumento,
    documentosExistentes: recibosExistentes,
    carregarDocumentosExistentes: () => listarRecibosPorEmpresa(dadosRecibo.empresaId),
    extrairSequencia: (recibo, ano) => extrairSequenciaRecibo(recibo, ano),
    formatarNumero: (sequencia, ano) => `Recibo nº ${sequencia}/${ano}`,
  });
  const numeroDocumento = reservaNumero.numeroDocumento;

  const numeroJaExiste = recibosExistentes.some(r => 
    r.numeroDocumento.toLowerCase() === numeroDocumento.toLowerCase()
  );
  
  if (numeroJaExiste) {
    throw new Error(`Já existe um recibo com o número "${numeroDocumento}".`);
  }

  validarDataEmissaoCronologica(
    { ...dadosRecibo, numeroDocumento },
    recibosExistentes,
    { rotuloDocumento: 'Recibo' },
  );

  const id = `recibo-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
  const agora = new Date().toISOString();

  const novoRecibo: Recibo = {
    id,
    empresaId: dadosRecibo.empresaId,
    empresaSnapshot: dadosRecibo.empresaSnapshot,
    tipo: 'recibo',
    numeroDocumento,
    dataEmissao: dadosRecibo.dataEmissao,
    exercicioEconomicoId: exercicio.id,
    anoExercicio: exercicio.ano,
    estado: 'emitido',
    clienteId: dadosRecibo.clienteId,
    cliente: dadosRecibo.cliente,
    moeda: dadosRecibo.moeda || 'MZN',
    pagamentos: dadosRecibo.pagamentos || [],
    subtotal: dadosRecibo.subtotal,
    totalImpostos: dadosRecibo.totalImpostos,
    totalIsento: dadosRecibo.totalIsento,
    totalGeral: dadosRecibo.totalGeral || 0,
    observacoes: dadosRecibo.observacoes,
    // Campos adicionais para forma de pagamento geral do recibo
    meioPagamento: dadosRecibo.meioPagamento,
    banco: dadosRecibo.banco,
    contaBancaria: dadosRecibo.contaBancaria || dadosRecibo.banco || '',
    numeroCheque: dadosRecibo.numeroCheque,
    referenciaTransferencia: dadosRecibo.referenciaTransferencia,
    referenciaPagamento: dadosRecibo.referenciaPagamento || dadosRecibo.referenciaTransferencia || dadosRecibo.numeroCheque || '',
    dataPagamento: dadosRecibo.dataPagamento,
    dataCriacao: agora,
    // ✅ NOVO: Campos de auditoria - NOME do usuário que criou/editou
    criadoPor: dadosRecibo.criadoPor || dadosRecibo.usuarioAuditoriaId || dadosRecibo.criadoPorId,
    criadoPorId: dadosRecibo.criadoPorId || dadosRecibo.usuarioAuditoriaId || dadosRecibo.criadoPor,
    criadoPorNome: dadosRecibo.criadoPorNome || dadosRecibo.usuarioAuditoriaNome,
    criadoPorEmail: dadosRecibo.criadoPorEmail || dadosRecibo.usuarioAuditoriaEmail,
    usuarioAuditoriaId: dadosRecibo.usuarioAuditoriaId || dadosRecibo.criadoPorId || dadosRecibo.criadoPor,
    usuarioAuditoriaNome: dadosRecibo.usuarioAuditoriaNome || dadosRecibo.criadoPorNome,
    usuarioAuditoriaEmail: dadosRecibo.usuarioAuditoriaEmail || dadosRecibo.criadoPorEmail,
  };

  try {
    await kv.set(`recibo:${dadosRecibo.empresaId}:${id}`, novoRecibo);
  } catch (error) {
    await libertarReservaNumero(reservaNumero.reservaKey).catch(() => null);
    throw error;
  }

  const syncAdiantamentos = await adiantamentos.sincronizarAplicacoesRecibo({
    reciboId: id,
    reciboNumero: numeroDocumento,
    pagamentosNovos: dadosRecibo.pagamentos || [],
  });

  if (!syncAdiantamentos.success) {
    await kv.del(`recibo:${dadosRecibo.empresaId}:${id}`);
    await libertarReservaNumero(reservaNumero.reservaKey).catch(() => null);
    throw new Error(syncAdiantamentos.error || 'Não foi possível aplicar os adiantamentos do recibo.');
  }
  
  if (dadosRecibo.pagamentos && dadosRecibo.pagamentos.length > 0) {
    await recalcularEstadoFacturasRelacionadas(
      dadosRecibo.empresaId,
      dadosRecibo.pagamentos.map((pagamento: any) => pagamento.facturaId).filter(Boolean),
    );
  }
  
  return novoRecibo;
}

/**
 * Atualizar recibo
 */
export async function atualizarRecibo(id: string, dadosRecibo: any): Promise<Recibo> {
  const reciboExistente = await buscarReciboPorId(id);
  
  if (!reciboExistente) {
    throw new Error('Recibo não encontrado');
  }

  await assegurarSemLancamentoContabilDefinitivo({
    empresaId: reciboExistente.empresaId,
    documentoId: reciboExistente.id,
    numeroDocumento: reciboExistente.numeroDocumento,
    rotuloDocumento: 'Recibo',
  });
  await validarReciboEhUltimo(reciboExistente, 'editado');
  await exerciciosEconomicos.validarExercicioAberto(
    reciboExistente.empresaId,
    dadosRecibo.dataEmissao || reciboExistente.dataEmissao,
    'recibo',
  );

  if (dadosRecibo.clienteId && dadosRecibo.clienteId !== reciboExistente.clienteId) {
    throw new Error('O cliente não pode ser alterado na edição de um recibo.');
  }

  if (dadosRecibo.numeroDocumento && dadosRecibo.numeroDocumento !== reciboExistente.numeroDocumento) {
    throw new Error('O numero de um recibo emitido nao pode ser alterado.');
  }

  const recibosExistentes = await listarRecibosPorEmpresa(reciboExistente.empresaId);
  validarDataEmissaoCronologica(
    { ...reciboExistente, ...dadosRecibo, numeroDocumento: reciboExistente.numeroDocumento },
    recibosExistentes,
    { rotuloDocumento: 'Recibo', ignorarId: reciboExistente.id },
  );

  let mesEmissao, anoEmissao;
  if (reciboExistente.dataEmissao.includes('-')) {
    const parts = reciboExistente.dataEmissao.split('T')[0].split('-');
    mesEmissao = parseInt(parts[1], 10) - 1; // 0-11
    anoEmissao = parseInt(parts[0], 10);
  } else {
    const dataEmissao = new Date(reciboExistente.dataEmissao);
    mesEmissao = dataEmissao.getMonth();
    anoEmissao = dataEmissao.getFullYear();
  }
  
  const dataAtual = new Date();
  
  if (
    mesEmissao !== dataAtual.getMonth() ||
    anoEmissao !== dataAtual.getFullYear()
  ) {
    throw new Error('Só é possível editar recibos do mês actual.');
  }

  const reciboAtualizado: Recibo = {
    ...reciboExistente,
    empresaSnapshot: reciboExistente.empresaSnapshot || dadosRecibo.empresaSnapshot,
    numeroDocumento: reciboExistente.numeroDocumento,
    dataEmissao: dadosRecibo.dataEmissao,
    estado: 'emitido',
    clienteId: reciboExistente.clienteId, // Forçado a usar o original
    cliente: reciboExistente.cliente,     // Forçado a usar o original
    moeda: dadosRecibo.moeda,
    pagamentos: dadosRecibo.pagamentos || [],
    subtotal: dadosRecibo.subtotal,
    totalImpostos: dadosRecibo.totalImpostos,
    totalIsento: dadosRecibo.totalIsento,
    totalGeral: dadosRecibo.totalGeral || 0,
    observacoes: dadosRecibo.observacoes,
    // Campos adicionais para forma de pagamento geral do recibo
    meioPagamento: dadosRecibo.meioPagamento,
    banco: dadosRecibo.banco,
    contaBancaria: dadosRecibo.contaBancaria || dadosRecibo.banco || '',
    numeroCheque: dadosRecibo.numeroCheque,
    referenciaTransferencia: dadosRecibo.referenciaTransferencia,
    referenciaPagamento: dadosRecibo.referenciaPagamento || dadosRecibo.referenciaTransferencia || dadosRecibo.numeroCheque || '',
    dataPagamento: dadosRecibo.dataPagamento,
    dataAtualizacao: new Date().toISOString(),
    // ✅ NOVO: Campos de auditoria - NOME do usuário que criou/editou
    editadoPor: dadosRecibo.editadoPor || dadosRecibo.atualizadoPor || dadosRecibo.usuarioAuditoriaId,
    editadoPorId: dadosRecibo.editadoPorId || dadosRecibo.usuarioAuditoriaId || dadosRecibo.atualizadoPor,
    editadoPorNome: dadosRecibo.editadoPorNome || dadosRecibo.atualizadoPorNome || dadosRecibo.usuarioAuditoriaNome,
    editadoPorEmail: dadosRecibo.editadoPorEmail || dadosRecibo.atualizadoPorEmail || dadosRecibo.usuarioAuditoriaEmail,
    atualizadoPor: dadosRecibo.atualizadoPor || dadosRecibo.usuarioAuditoriaId || dadosRecibo.editadoPor,
    atualizadoPorNome: dadosRecibo.atualizadoPorNome || dadosRecibo.usuarioAuditoriaNome || dadosRecibo.editadoPorNome,
    atualizadoPorEmail: dadosRecibo.atualizadoPorEmail || dadosRecibo.usuarioAuditoriaEmail || dadosRecibo.editadoPorEmail,
    usuarioAuditoriaId: dadosRecibo.usuarioAuditoriaId || dadosRecibo.editadoPorId || dadosRecibo.atualizadoPor,
    usuarioAuditoriaNome: dadosRecibo.usuarioAuditoriaNome || dadosRecibo.editadoPorNome || dadosRecibo.atualizadoPorNome,
    usuarioAuditoriaEmail: dadosRecibo.usuarioAuditoriaEmail || dadosRecibo.editadoPorEmail || dadosRecibo.atualizadoPorEmail,
  };

  await kv.set(`recibo:${reciboExistente.empresaId}:${id}`, reciboAtualizado);

  const syncAdiantamentos = await adiantamentos.sincronizarAplicacoesRecibo({
    reciboId: id,
    reciboNumero: reciboAtualizado.numeroDocumento,
    pagamentosAnteriores: reciboExistente.pagamentos || [],
    pagamentosNovos: dadosRecibo.pagamentos || [],
  });

  if (!syncAdiantamentos.success) {
    await kv.set(`recibo:${reciboExistente.empresaId}:${id}`, reciboExistente);
    throw new Error(syncAdiantamentos.error || 'Não foi possível actualizar os adiantamentos do recibo.');
  }
  
  const facturasAntigas = (reciboExistente.pagamentos || []).map((pagamento: any) => pagamento.facturaId).filter(Boolean);
  const facturasNovas = (dadosRecibo.pagamentos || []).map((pagamento: any) => pagamento.facturaId).filter(Boolean);
  await recalcularEstadoFacturasRelacionadas(reciboExistente.empresaId, [...facturasAntigas, ...facturasNovas]);
  
  return reciboAtualizado;
}

/**
 * Eliminar recibo.
 *
 * Decisao de negocio: eliminar continua disponivel (gerido pela permissao
 * "deletar"), mas so para o ULTIMO recibo da sequencia, sem lancamento
 * contabilistico definitivo - nunca deixa buracos na numeracao fiscal. O
 * documento nunca e destruido: e movido para uma tabela de lixo
 * (arquivarDocumentoEliminado) antes de ser removido da listagem principal.
 * Anular continua a ser a unica forma de invalidar um recibo que nao seja
 * o ultimo da sequencia.
 */
export async function excluirRecibo(
  id: string,
  contexto: ContextoEliminacaoDocumento = {},
): Promise<Recibo> {
  const recibo = await buscarReciboPorId(id);

  if (!recibo) {
    throw new Error('Recibo não encontrado');
  }

  const ano = obterAnoRecibo(recibo);
  const sequencia = extrairSequenciaRecibo(recibo, ano);
  if (!sequencia) throw new Error('Não foi possível identificar a sequência do recibo.');

  const escopo: EscopoNumeroDocumento = {
    empresaId: recibo.empresaId,
    modulo: 'faturacao-vendas',
    tipoDocumento: 'recibo',
    ano,
  };

  return executarComBloqueioNumeracao(escopo, async () => {
    const actual = await buscarReciboPorId(id);
    if (!actual) throw new Error('Recibo não encontrado. A lista pode estar desactualizada.');

    await assegurarSemLancamentoContabilDefinitivo({
      empresaId: actual.empresaId,
      documentoId: actual.id,
      numeroDocumento: actual.numeroDocumento,
      rotuloDocumento: 'Recibo',
    });
    await validarReciboEhUltimo(actual, 'eliminado');
    await exerciciosEconomicos.validarExercicioAberto(actual.empresaId, actual.dataEmissao, 'recibo');

    const recibos = await listarRecibosPorEmpresa(actual.empresaId);
    const sequencias = recibos
      .map((item) => extrairSequenciaRecibo(item, ano))
      .filter((item): item is number => item !== null);
    await assegurarSemReservaPosterior(escopo, sequencia, sequencias);
    await libertarReservaNumeroDoDocumento(escopo, sequencia);

    let lancamentosRascunho: Array<{ key: string; value: any }> = [];
    let adiantamentosLibertados = false;
    let documentoEliminado = false;
    let arquivoEliminacaoKey: string | null = null;
    try {
      const chaveDocumento = `recibo:${actual.empresaId}:${id}`;
      arquivoEliminacaoKey = await arquivarDocumentoEliminado({
        chaveOriginal: chaveDocumento,
        empresaId: actual.empresaId,
        tipoDocumento: 'recibo',
        numeroDocumento: actual.numeroDocumento,
        documento: actual as unknown as Record<string, unknown>,
        contexto,
      });
      lancamentosRascunho = await removerLancamentosContabeisRascunhoDoDocumento({
        empresaId: actual.empresaId,
        documentoId: actual.id,
        numeroDocumento: actual.numeroDocumento,
      });

      const syncAdiantamentos = await adiantamentos.sincronizarAplicacoesRecibo({
        reciboId: id,
        reciboNumero: actual.numeroDocumento,
        pagamentosAnteriores: actual.pagamentos || [],
        pagamentosNovos: [],
      });
      if (!syncAdiantamentos.success) {
        throw new Error(syncAdiantamentos.error || 'Não foi possível libertar os adiantamentos do recibo.');
      }
      adiantamentosLibertados = true;

      await kv.del(chaveDocumento);
      documentoEliminado = true;
    } catch (error) {
      await restaurarReservaNumeroDoDocumento(escopo, sequencia, actual.numeroDocumento).catch(() => null);
      if (documentoEliminado) await kv.set(`recibo:${actual.empresaId}:${id}`, actual).catch(() => null);
      await restaurarLancamentosContabeisRascunho(lancamentosRascunho).catch(() => null);
      if (adiantamentosLibertados) {
        await adiantamentos.sincronizarAplicacoesRecibo({
          reciboId: id,
          reciboNumero: actual.numeroDocumento,
          pagamentosAnteriores: [],
          pagamentosNovos: actual.pagamentos || [],
        }).catch(() => null);
      }
      if (arquivoEliminacaoKey) await kv.del(arquivoEliminacaoKey).catch(() => null);
      throw error;
    }

    await recalcularEstadoFacturasRelacionadas(
      actual.empresaId,
      (actual.pagamentos || []).map((pagamento: any) => pagamento.facturaId).filter(Boolean),
    );

    logger.warn(`Recibo ${actual.numeroDocumento} eliminado; número libertado para reutilização.`);
    return { ...actual, eliminadoFisicamente: true } as Recibo;
  });
}

/**
 * Verificar se recibo pode ser anulado.
 *
 * Ao contrario de podeAlterarRecibo(id,'eliminado'), nao exige ser o ultimo
 * (validarReciboEhUltimo) - anular nao liberta o numero, por isso funciona
 * em qualquer posicao.
 */
export async function podeAnularRecibo(id: string): Promise<{ pode: boolean; motivo?: string }> {
  const recibo = await buscarReciboPorId(id);
  if (!recibo) return { pode: false, motivo: 'Recibo nao encontrado.' };
  if (recibo.estado === 'anulado') {
    return { pode: false, motivo: 'Recibo ja esta anulado.' };
  }

  try {
    await exerciciosEconomicos.validarExercicioAberto(recibo.empresaId, recibo.dataEmissao, 'recibo');
  } catch (error) {
    return { pode: false, motivo: error instanceof Error ? error.message : String(error) };
  }

  return assegurarSemLancamentoContabilDefinitivo({
    empresaId: recibo.empresaId,
    documentoId: recibo.id,
    numeroDocumento: recibo.numeroDocumento,
    rotuloDocumento: 'Recibo',
  }).then(() => ({ pode: true })).catch((error) => ({
    pode: false,
    motivo: error instanceof Error ? error.message : String(error),
  }));
}

/**
 * Verificar se recibo pode ser eliminado (removido para a lixeira).
 *
 * Ao contrario de podeAnularRecibo(), exige ser o ultimo da sequencia
 * (validarReciboEhUltimo) - eliminar liberta o numero para reutilizacao,
 * por isso so pode ser a cauda da sequencia.
 */
export async function podeEliminarRecibo(id: string): Promise<{ pode: boolean; motivo?: string }> {
  const recibo = await buscarReciboPorId(id);
  if (!recibo) return { pode: false, motivo: 'Recibo não encontrado.' };
  if (recibo.estado === 'anulado') {
    return { pode: false, motivo: 'Recibo já está anulado.' };
  }

  try {
    await exerciciosEconomicos.validarExercicioAberto(recibo.empresaId, recibo.dataEmissao, 'recibo');
    await assegurarSemLancamentoContabilDefinitivo({
      empresaId: recibo.empresaId,
      documentoId: recibo.id,
      numeroDocumento: recibo.numeroDocumento,
      rotuloDocumento: 'Recibo',
    });
    await validarReciboEhUltimo(recibo, 'eliminado');
  } catch (error) {
    return { pode: false, motivo: error instanceof Error ? error.message : String(error) };
  }

  return { pode: true };
}

/**
 * Anular recibo - substitui a eliminacao. Preserva o documento, o numero
 * e o historico; liberta as aplicacoes de adiantamento (como se o recibo
 * nunca tivesse liquidado nada) e reverte lancamentos rascunho, mesmo
 * padrao de vendasDinheiro.anularVD().
 */
export async function anularRecibo(id: string, motivo?: string): Promise<Recibo> {
  const recibo = await buscarReciboPorId(id);
  if (!recibo) {
    throw new Error('Recibo não encontrado');
  }

  if (recibo.estado === 'anulado') {
    throw new Error('Recibo já está anulado');
  }

  await assegurarSemLancamentoContabilDefinitivo({
    empresaId: recibo.empresaId,
    documentoId: recibo.id,
    numeroDocumento: recibo.numeroDocumento,
    rotuloDocumento: 'Recibo',
  });
  await exerciciosEconomicos.validarExercicioAberto(recibo.empresaId, recibo.dataEmissao, 'recibo');

  const agora = new Date().toISOString();
  const reciboAnulado: Recibo = {
    ...recibo,
    estado: 'anulado',
    dataAnulacao: agora,
    motivoAnulacao: motivo,
    dataAtualizacao: agora,
  };
  await kv.set(`recibo:${recibo.empresaId}:${id}`, reciboAnulado);

  try {
    const syncAdiantamentos = await adiantamentos.sincronizarAplicacoesRecibo({
      reciboId: id,
      reciboNumero: recibo.numeroDocumento,
      pagamentosAnteriores: recibo.pagamentos || [],
      pagamentosNovos: [],
    });
    if (!syncAdiantamentos.success) {
      throw new Error(syncAdiantamentos.error || 'Não foi possível libertar os adiantamentos do recibo.');
    }

    await removerLancamentosContabeisRascunhoDoDocumento({
      empresaId: recibo.empresaId,
      documentoId: recibo.id,
      numeroDocumento: recibo.numeroDocumento,
    });

    await recalcularEstadoFacturasRelacionadas(
      recibo.empresaId,
      (recibo.pagamentos || []).map((pagamento: any) => pagamento.facturaId).filter(Boolean),
    );
  } catch (error) {
    await kv.set(`recibo:${recibo.empresaId}:${id}`, recibo).catch(() => null);
    throw error;
  }

  logger.info(`Recibo ${recibo.numeroDocumento} anulado (motivo: ${motivo || "não informado"}).`);
  return reciboAnulado;
}

/**
 * Buscar recibos por cliente
 */
export async function buscarRecibosPorCliente(empresaId: string, clienteId: string): Promise<Recibo[]> {
  const todosRecibos = await listarRecibosPorEmpresa(empresaId);
  return todosRecibos.filter((rec: Recibo) => rec.clienteId === clienteId);
}


