﻿import * as kv from "./kv_store.tsx";

import { validarDataEmissaoCronologica } from "./documento-cronologia.tsx";
import * as exerciciosEconomicos from "./exercicios-economicos.tsx";
import * as clientes from "./clientes.tsx";
import {
  executarComBloqueioNumeracao,
  libertarReservaNumero,
  libertarReservaNumeroDoDocumento,
  restaurarReservaNumeroDoDocumento,
  reservarNumeroSequencial,
  type EscopoNumeroDocumento,
} from "./document-numbering.tsx";
import { aplicarTotaisFiscais } from "./validacao-fiscal.tsx";
import {
  arquivarDocumentoEliminado,
  type ContextoEliminacaoDocumento,
} from "./document-deletion-audit.tsx";

// ========== INTERFACES ==========
interface ItemCotacao {
  id: string;
  tipo?: 'artigo' | 'texto' | 'vazio' | 'motivo_isencao';
  artigoId?: string;
  codigo: string;
  descricao: string;
  quantidade: number;
  precoUnitario: number;
  imposto: number;
  ivaIncluso?: boolean;
  motivoIsencao?: string;
  codigoMotivoIsencao?: string;
  desconto: number;
  total: number;
}

interface Cliente {
  codigo: string;
  nome: string;
  nuit: string;
  telefone?: string;
  email?: string;
  endereco?: string;
  cidade?: string;
  provincia?: string;
  fax?: string;
}

interface Cotacao {
  id: string;
  empresaId: string;
  empresaSnapshot?: Record<string, unknown>;
  exercicioEconomicoId?: string;
  anoExercicio?: number;
  numeroDocumento: string;
  dataEmissao: string;
  dataValidade: string;
  estado: 'pendente' | 'aprovada' | 'rejeitada' | 'convertida' | 'expirada' | 'anulada';
  clienteId: string;
  cliente: Cliente;
  moeda: string;
  condicaoPagamento: string;
  itens: ItemCotacao[];
  subtotal: number;
  totalImpostos: number;
  descontos: number;
  totalGeral: number;
  observacoes?: string;
  dataCriacao: string;
  dataAtualizacao?: string;
  // âœ… Campos de auditoria - NOME do usuário que criou/editou
  criadoPor?: string; // Nome do usuário que criou o documento
  criadoPorId?: string; // ID do usuário que criou o documento
  editadoPor?: string; // Nome do usuário que editou o documento (última edição)
  editadoPorId?: string; // ID do usuário que editou o documento
  // âœ… Novos campos para controle de bloqueio
  documentosOriginados?: string[]; // IDs de facturas/adiantamentos criados a partir desta cotação
  dataAnulacao?: string;
  motivoAnulacao?: string;
  // âœ… NOVO: Controle de adiantamentos
  adiantamentos?: {
    id: string;
    valor: number;
    data: string;
  }[];
  totalAdiantado?: number; // Soma de todos os adiantamentos
  // âœ… NOVO: Estado financeiro calculado
  estadoFinanceiro?: {
    tipo: 'normal' | 'adiantamento_parcial' | 'adiantamento_total' | 'adiantamento_excedente';
    percentual: number;
    valorRestante: number;
  };
}

// ========== FUNÃ‡Ã•ES DE COTAÃ‡Ã•ES ==========

function extrairOrdemCotacao(cotacao: Partial<Cotacao>) {
  const numeroDocumento = String(cotacao.numeroDocumento || "");
  const formatoCurto = numeroDocumento.match(/COT\s*\/\s*(\d{4})\s*\/\s*(\d+)/i);
  const formatoFiscal = numeroDocumento.match(/(\d+)\s*\/\s*(\d{4})$/);

  const anoNumero = formatoCurto
    ? Number(formatoCurto[1])
    : formatoFiscal
      ? Number(formatoFiscal[2])
      : 0;
  const sequencia = formatoCurto
    ? Number(formatoCurto[2])
    : formatoFiscal
      ? Number(formatoFiscal[1])
      : 0;
  const anoData =
    Number(cotacao.anoExercicio) ||
    Number(String(cotacao.dataEmissao || "").slice(0, 4)) ||
    0;
  const ano = anoNumero || anoData || new Date(cotacao.dataEmissao || 0).getFullYear() || 0;
  const data = new Date(cotacao.dataCriacao || cotacao.dataEmissao || 0).getTime();

  return {
    ano: Number.isFinite(ano) ? ano : 0,
    sequencia: Number.isFinite(sequencia) ? sequencia : 0,
    data: Number.isFinite(data) ? data : 0,
  };
}

function compararOrdemCotacao(a: Partial<Cotacao>, b: Partial<Cotacao>) {
  const ordemA = extrairOrdemCotacao(a);
  const ordemB = extrairOrdemCotacao(b);

  if (ordemA.ano !== ordemB.ano) return ordemA.ano - ordemB.ano;
  if (ordemA.sequencia !== ordemB.sequencia) return ordemA.sequencia - ordemB.sequencia;
  return ordemA.data - ordemB.data;
}

/**
 * Listar cotaçÃµes de uma empresa
 */
export async function listarCotacoesPorEmpresa(empresaId: string): Promise<Cotacao[]> {
  const cotacoes = await kv.getByPrefix(`cotacao:${empresaId}:`);
  const cotacoesValidas = Array.isArray(cotacoes)
    ? cotacoes.filter((cotacao: any) => cotacao && typeof cotacao === 'object')
    : [];

  const normalizarChaveCliente = (valor?: string) =>
    String(valor || '')
      .trim()
      .toLowerCase()
      .normalize('NFD')
      .replace(/[\u0300-\u036f]/g, '')
      .replace(/\s+/g, ' ');

  const clientesPorId = new Map<string, any>();
  const clientesPorNome = new Map<string, any>();
  try {
    const clientesEmpresa = await clientes.listarClientes(empresaId);
    clientesEmpresa.forEach((cliente: any) => {
      if (cliente?.id) clientesPorId.set(cliente.id, cliente);
      const chaveNome = normalizarChaveCliente(cliente?.nome);
      if (chaveNome && (!clientesPorNome.has(chaveNome) || cliente?.nuit)) {
        clientesPorNome.set(chaveNome, cliente);
      }
    });
  } catch (error) {
    console.warn('[Cotacoes] Falha ao carregar clientes para enriquecer listagem:', error);
  }

  const cotacoesComEstadoFinanceiro = cotacoesValidas.map((cotacao: Cotacao) => {
    const clienteSnapshot = cotacao.cliente || ({} as Cliente);
    const nomeParaResolver = clienteSnapshot.nome || (cotacao as any).clienteNome || '';
    const clienteCadastro =
      (cotacao.clienteId ? clientesPorId.get(cotacao.clienteId) : null) ||
      clientesPorNome.get(normalizarChaveCliente(nomeParaResolver)) ||
      null;
    const cliente = {
      ...clienteSnapshot,
      codigo: clienteSnapshot.codigo || clienteCadastro?.codigo || '',
      nome: clienteSnapshot.nome || clienteCadastro?.nome || (cotacao as any).clienteNome || '',
      nuit: clienteSnapshot.nuit || (cotacao as any).clienteNuit || clienteCadastro?.nuit || '',
      telefone: clienteSnapshot.telefone || clienteCadastro?.telefone || '',
      email: clienteSnapshot.email || clienteCadastro?.email || '',
      endereco: clienteSnapshot.endereco || clienteCadastro?.endereco || '',
      cidade: clienteSnapshot.cidade || clienteCadastro?.cidade || '',
      provincia: clienteSnapshot.provincia || clienteCadastro?.provincia || '',
    };

    try {
      return {
        ...cotacao,
        cliente,
        clienteNome: cliente.nome,
        clienteNuit: cliente.nuit,
        estadoFinanceiro: calcularEstadoFinanceiro(cotacao),
      };
    } catch (error) {
      console.warn('[Cotacoes] Falha ao calcular estado financeiro; usando estado normal:', cotacao?.id, error);
      return {
        ...cotacao,
        cliente,
        clienteNome: cliente.nome,
        clienteNuit: cliente.nuit,
        estadoFinanceiro: {
          tipo: 'normal' as const,
          percentual: 0,
          valorRestante: Number(cotacao?.totalGeral) || 0,
        },
      };
    }
  });

  const timestampSeguro = (cotacao: Cotacao) => {
    const data = new Date(cotacao.dataCriacao || cotacao.dataEmissao || 0).getTime();
    return Number.isFinite(data) ? data : 0;
  };

  return cotacoesComEstadoFinanceiro.sort((a: Cotacao, b: Cotacao) =>
    timestampSeguro(b) - timestampSeguro(a)
  );
}
/**
 * Buscar cotação por ID
 */
export async function buscarCotacaoPorId(id: string): Promise<Cotacao | null> {
  return await kv.get(id);
}

/**
 * Criar nova cotação
 */
export async function criarCotacao(data: Omit<Cotacao, 'id' | 'dataCriacao' | 'dataAtualizacao'>): Promise<Cotacao> {
  data = await aplicarTotaisFiscais(data) as typeof data;
  const exercicio = await exerciciosEconomicos.validarExercicioAberto(
    data.empresaId,
    data.dataEmissao,
    "cotacao",
  );

  const cotacoesExistentes = await listarCotacoesPorEmpresa(data.empresaId);
  const reservaNumero = await reservarNumeroSequencial<Cotacao>({
    empresaId: data.empresaId,
    modulo: "faturacao-vendas",
    tipoDocumento: "cotacao",
    ano: exercicio.ano,
    numeroPretendido: data.numeroDocumento,
    documentosExistentes: cotacoesExistentes,
    extrairSequencia: (cotacao, ano) => {
      const ordem = extrairOrdemCotacao(cotacao);
      return ordem.ano === ano ? ordem.sequencia : null;
    },
    formatarNumero: (sequencia, ano) => `Cotação nº ${sequencia}/${ano}`,
  });

  data = {
    ...data,
    numeroDocumento: reservaNumero.numeroDocumento,
  };

  const numeroJaExiste = cotacoesExistentes.some(c => 
    c.numeroDocumento.toLowerCase() === data.numeroDocumento.toLowerCase()
  );
  
  if (numeroJaExiste) {
    throw new Error(`âŒ Cotação duplicada! Já existe uma cotação com o número "${data.numeroDocumento}"`);
  }

  validarDataEmissaoCronologica(data, cotacoesExistentes, {
    rotuloDocumento: 'Cotação',
  });

  const id = `cotacao:${data.empresaId}:${crypto.randomUUID()}`;
  
  console.log('ðŸ“ [criarCotacao] Dados recebidos:', {
    criadoPor: data.criadoPor,
    criadoPorId: data.criadoPorId,
    empresaId: data.empresaId
  });
  
  const novaCotacao: Cotacao = {
    ...data,
    exercicioEconomicoId: exercicio.id,
    anoExercicio: exercicio.ano,
    empresaSnapshot: data.empresaSnapshot,
    id,
    dataCriacao: new Date().toISOString(),
  };
  
  console.log('ðŸ’¾ [criarCotacao] Cotação a ser salva:', {
    id: novaCotacao.id,
    criadoPor: novaCotacao.criadoPor,
    criadoPorId: novaCotacao.criadoPorId
  });

  try {
    await kv.set(id, novaCotacao);
  } catch (error) {
    await libertarReservaNumero(reservaNumero.reservaKey).catch(() => null);
    throw error;
  }
  
  return novaCotacao;
}

/**
 * Atualizar cotação existente
 */
export async function atualizarCotacao(id: string, data: Partial<Cotacao>): Promise<Cotacao> {
  const cotacaoExistente = await kv.get(id);
  
  if (!cotacaoExistente) {
    throw new Error('Cotação não encontrada');
  }

  if (data.itens) {
    const validacaoEdicao = podeEditarCotacaoComAdiantamentos(cotacaoExistente);
    if (!validacaoEdicao.pode) {
      throw new Error(validacaoEdicao.motivo || 'Esta cotação não pode ser editada.');
    }
    data = await aplicarTotaisFiscais({ ...cotacaoExistente, ...data }) as Partial<Cotacao>;
  }

  const dataEmissao = data.dataEmissao || cotacaoExistente.dataEmissao;
  const exercicio = await exerciciosEconomicos.validarExercicioAberto(
    cotacaoExistente.empresaId,
    dataEmissao,
    "cotacao",
  );

  // ðŸ”’ VALIDAÃ‡ÃƒO: Verificar se o novo número já existe (exceto o próprio documento)
  if (data.numeroDocumento) {
    const cotacoesExistentes = await listarCotacoesPorEmpresa(cotacaoExistente.empresaId);
    const numeroJaExiste = cotacoesExistentes.some(c => 
      c.id !== id && // Ignorar o próprio documento
      c.numeroDocumento.toLowerCase() === data.numeroDocumento.toLowerCase()
    );
    
    if (numeroJaExiste) {
      throw new Error(`âŒ Cotação duplicada! Já existe outra cotação com o número "${data.numeroDocumento}"`);
    }
  }
  const cotacaoAtualizada: Cotacao = {
    ...cotacaoExistente,
    ...data,
    exercicioEconomicoId: exercicio.id,
    anoExercicio: exercicio.ano,
    empresaSnapshot: cotacaoExistente.empresaSnapshot || data.empresaSnapshot,
    id, // Garantir que o ID não muda
    empresaId: cotacaoExistente.empresaId, // Garantir que empresaId não muda
    dataCriacao: cotacaoExistente.dataCriacao, // Manter data de criação original
    dataAtualizacao: new Date().toISOString(),
  };

  await kv.set(id, cotacaoAtualizada);
  
  return cotacaoAtualizada;
}

/**
 * Eliminar cotação.
 *
 * Decisão de negócio: eliminar continua disponível (gerido pela permissão
 * "deletar", já aplicada automaticamente pelo middleware global de rotas),
 * mas só para a ÚLTIMA cotação da sequência, sem adiantamentos nem
 * documentos originados — nunca deixa buracos na numeração nem apaga um
 * documento que já teve efeito noutro lado. O documento nunca é destruído:
 * é movido para uma tabela de lixo (arquivarDocumentoEliminado), consultável
 * no futuro, antes de ser removido da listagem principal. Anular continua a
 * ser a única forma de invalidar uma cotação que já não seja a última da
 * sequência ou que precise de ficar visível com o motivo registado.
 */
export async function excluirCotacao(
  id: string,
  contexto: ContextoEliminacaoDocumento = {},
): Promise<Cotacao> {
  const cotacao = await buscarCotacaoPorId(id);

  if (!cotacao) {
    throw new Error('Cotação não encontrada');
  }

  const validacao = await podeEliminarCotacaoComAdiantamentos(cotacao);
  if (!validacao.pode) {
    throw new Error(validacao.motivo || 'Esta cotação não pode ser eliminada.');
  }

  const ordem = extrairOrdemCotacao(cotacao);
  const escopo: EscopoNumeroDocumento = {
    empresaId: cotacao.empresaId,
    modulo: 'faturacao-vendas',
    tipoDocumento: 'cotacao',
    ano: ordem.ano,
  };

  return executarComBloqueioNumeracao(escopo, async () => {
    const actual = await buscarCotacaoPorId(id);
    if (!actual) throw new Error('Cotação não encontrada. A lista pode estar desactualizada.');

    const validacaoActual = await podeEliminarCotacaoComAdiantamentos(actual);
    if (!validacaoActual.pode) {
      throw new Error(validacaoActual.motivo || 'Esta cotação não pode ser eliminada.');
    }

    const ordemActual = extrairOrdemCotacao(actual);
    if (ordemActual.sequencia > 0) {
      await libertarReservaNumeroDoDocumento(escopo, ordemActual.sequencia);
    }

    try {
      await arquivarDocumentoEliminado({
        chaveOriginal: actual.id,
        empresaId: actual.empresaId,
        tipoDocumento: 'cotacao',
        numeroDocumento: actual.numeroDocumento,
        documento: actual as unknown as Record<string, unknown>,
        contexto,
      });

      await kv.del(actual.id);

      console.warn(`Cotação ${actual.numeroDocumento} eliminada; número libertado para reutilização.`);

      return { ...actual, eliminadoFisicamente: true } as Cotacao;
    } catch (error) {
      await restaurarReservaNumeroDoDocumento(escopo, ordemActual.sequencia, actual.numeroDocumento).catch(() => null);
      throw error;
    }
  });
}

/**
 * Buscar cotaçÃµes por cliente
 */
export async function buscarCotacoesPorCliente(empresaId: string, clienteId: string): Promise<Cotacao[]> {
  const todasCotacoes = await listarCotacoesPorEmpresa(empresaId);
  return todasCotacoes.filter((cot: Cotacao) => cot.clienteId === clienteId);
}

/**
 * Alterar estado da cotação
 */
export async function alterarEstadoCotacao(
  id: string, 
  novoEstado: 'pendente' | 'aprovada' | 'rejeitada' | 'convertida' | 'expirada' | 'anulada'
): Promise<Cotacao> {
  return await atualizarCotacao(id, { estado: novoEstado });
}

/**
 * Verificar e atualizar cotaçÃµes expiradas
 */
export async function verificarCotacoesExpiradas(empresaId: string): Promise<number> {
  const hoje = new Date().toISOString().split('T')[0];
  const todasCotacoes = await listarCotacoesPorEmpresa(empresaId);
  let atualizadas = 0;
  
  for (const cotacao of todasCotacoes) {
    // Se está pendente ou aprovada E a data de validade passou
    if ((cotacao.estado === 'pendente' || cotacao.estado === 'aprovada') && 
        cotacao.dataValidade < hoje) {
      await alterarEstadoCotacao(cotacao.id, 'expirada');
      atualizadas++;
    }
  }
  
  return atualizadas;
}

/**
 * âœ… NOVA: Verificar se cotação pode ser editada
 */
export function podeEditarCotacao(cotacao: Cotacao): { pode: boolean; motivo?: string } {
  // Não pode editar se já originou documentos
  if (cotacao.documentosOriginados && cotacao.documentosOriginados.length > 0) {
    return { pode: false, motivo: 'Cotação já originou documentos (factura ou adiantamento)' };
  }
  
  // Não pode editar se está convertida
  if (cotacao.estado === 'convertida') {
    return { pode: false, motivo: 'Cotação já foi convertida' };
  }
  
  // Não pode editar se está anulada
  if (cotacao.estado === 'anulada') {
    return { pode: false, motivo: 'Cotação está anulada' };
  }
  
  return { pode: true };
}

/**
 * âœ… NOVA: Verificar se cotação pode ser eliminada
 */
export async function podeEliminarCotacao(cotacao: Cotacao): Promise<{ pode: boolean; motivo?: string }> {
  // Não pode eliminar se já originou documentos
  if (cotacao.documentosOriginados && cotacao.documentosOriginados.length > 0) {
    return { pode: false, motivo: 'Cotação já originou documentos (factura ou adiantamento)' };
  }
  
  // Não pode eliminar se está convertida
  if (cotacao.estado === 'convertida') {
    return { pode: false, motivo: 'Cotação já foi convertida' };
  }
  
  // Verificar se é a última cotação da sequência do mesmo exercício.
  // Não usar localeCompare: "18/2026" pode ficar depois de "100/2026" como texto.
  const todasCotacoes = await listarCotacoesPorEmpresa(cotacao.empresaId);
  const ordemCotacao = extrairOrdemCotacao(cotacao);
  const cotacoesBase = todasCotacoes.some((item) => item.id === cotacao.id)
    ? todasCotacoes
    : [...todasCotacoes, cotacao];
  const cotacoesDaSequencia = cotacoesBase
    .filter((item) =>
      extrairOrdemCotacao(item).ano === ordemCotacao.ano &&
      (item.id === cotacao.id || item.estado !== 'anulada'))
    .sort(compararOrdemCotacao);

  const indiceCotacao = cotacoesDaSequencia.findIndex(c => c.id === cotacao.id);
  if (indiceCotacao < 0) {
    return {
      pode: false,
      motivo: 'Não foi possível confirmar a posição desta cotação na sequência. Atualize a lista e tente novamente.',
    };
  }

  const ultimaCotacao = cotacoesDaSequencia[cotacoesDaSequencia.length - 1];
  const posteriores = cotacoesDaSequencia.slice(indiceCotacao + 1);

  if (cotacao.id !== ultimaCotacao.id) {
    return { 
      pode: false, 
      motivo: `Apenas a última cotação (${ultimaCotacao.numeroDocumento}) pode ser eliminada. Existem ${posteriores.length} cotação(ões) posterior(es).`
    };
  }
  
  return { pode: true };
}

/**
 * âœ… NOVA: Anular cotação
 */
/**
 * Verificar se cotação pode ser anulada.
 */
export function podeAnularCotacao(cotacao: Cotacao): { pode: boolean; motivo?: string } {
  if (cotacao.estado === 'anulada') {
    return { pode: false, motivo: 'Cotação já está anulada' };
  }

  if (cotacao.documentosOriginados && cotacao.documentosOriginados.length > 0) {
    return { pode: false, motivo: 'Não é possível anular cotação que já originou documentos' };
  }

  if (cotacao.estado === 'convertida') {
    return { pode: false, motivo: 'Não é possível anular cotação que já foi convertida' };
  }

  return { pode: true };
}

export async function anularCotacao(id: string, motivo: string): Promise<Cotacao> {
  const cotacao = await buscarCotacaoPorId(id);

  if (!cotacao) {
    throw new Error('Cotação não encontrada');
  }

  const validacao = podeAnularCotacao(cotacao);
  if (!validacao.pode) {
    throw new Error(validacao.motivo || 'Esta cotação não pode ser anulada.');
  }

  return await atualizarCotacao(id, {
    estado: 'anulada',
    dataAnulacao: new Date().toISOString(),
    motivoAnulacao: motivo,
  });
}

/**
 * âœ… NOVA: Registrar que cotação originou documento
 */
export async function registrarDocumentoOriginado(
  cotacaoId: string,
  documentoId: string,
  tipoDocumento: 'factura' | 'adiantamento'
): Promise<Cotacao> {
  // ✅ Sem bloqueio, dois documentos gerados quase em simultâneo a partir da mesma
  // cotação liam o array documentosOriginados antes de qualquer um escrever, e o
  // segundo write apagava a entrada do primeiro. O bloqueio serializa o
  // ler->modificar->escrever, e a releitura fresca dentro dele evita usar dados stale.
  return executarComBloqueioNumeracao(
    { empresaId: '', modulo: 'cotacao-vinculo', tipoDocumento: 'cotacao', serieId: cotacaoId, ano: 0 },
    async () => {
      const cotacao = await buscarCotacaoPorId(cotacaoId);

      if (!cotacao) {
        throw new Error('Cotação não encontrada');
      }

      const documentoOriginado = `${tipoDocumento}:${documentoId}`;
      const documentosOriginados = cotacao.documentosOriginados || [];

      if (!documentosOriginados.includes(documentoOriginado)) {
        documentosOriginados.push(documentoOriginado);
      }

      return await atualizarCotacao(cotacaoId, {
        estado: 'convertida',
        documentosOriginados,
      });
    },
  );
}

/**
 * ✅ Desfaz o registo de conversão quando o documento originado (Factura ou
 * Adiantamento) é eliminado — sem isto, uma Cotação ficava presa em
 * "convertida" (bloqueando edição/anulação) para sempre, mesmo depois de a
 * Factura que a converteu deixar de existir. Só reverte o estado se já não
 * houver mais nenhum documento originado a apontar para esta cotação (ex:
 * se houver Factura + Adiantamento originados e só um for eliminado).
 */
export async function desfazerConversao(
  cotacaoId: string,
  documentoId: string,
  tipoDocumento: 'factura' | 'adiantamento',
): Promise<Cotacao | null> {
  return executarComBloqueioNumeracao(
    { empresaId: '', modulo: 'cotacao-vinculo', tipoDocumento: 'cotacao', serieId: cotacaoId, ano: 0 },
    async () => {
      const cotacao = await buscarCotacaoPorId(cotacaoId);
      if (!cotacao) return null;

      const documentoOriginado = `${tipoDocumento}:${documentoId}`;
      const documentosOriginadosExistentes = cotacao.documentosOriginados || [];
      const documentosOriginados = documentosOriginadosExistentes.filter((item) => item !== documentoOriginado);

      if (documentosOriginados.length === documentosOriginadosExistentes.length) {
        return cotacao;
      }

      const dados: Partial<Cotacao> = { documentosOriginados };
      if (documentosOriginados.length === 0 && cotacao.estado === 'convertida') {
        dados.estado = 'aprovada';
      }

      return await atualizarCotacao(cotacaoId, dados);
    },
  );
}

/**
 * Compatibilidade para fluxos de conversão de cotação em documentos fiscais.
 */
export async function registrarConversao(
  cotacaoId: string,
  documentoId: string,
  tipoDocumento: 'factura' | 'adiantamento'
): Promise<Cotacao> {
  return await registrarDocumentoOriginado(cotacaoId, documentoId, tipoDocumento);
}

/**
 * âœ… NOVO: Registrar adiantamento na cotação
 */
export async function registrarAdiantamento(
  cotacaoId: string,
  adiantamentoId: string,
  valor: number
): Promise<Cotacao> {
  const cotacao = await buscarCotacaoPorId(cotacaoId);

  if (!cotacao) {
    throw new Error('Cotação não encontrada');
  }

  // ✅ Ver nota equivalente em facturas.registrarAdiantamento — um documento
  // ja convertido ou anulado nao aceita mais adiantamentos.
  const estadoCotacao = String((cotacao as any).estado || '').toLowerCase();
  if (estadoCotacao === 'convertida' || estadoCotacao === 'anulada') {
    throw new Error(
      `Nao e possivel registar um adiantamento nesta Cotacao: ja esta ${estadoCotacao === 'convertida' ? 'convertida noutro documento' : 'anulada'}.`,
    );
  }

  const adiantamentos = cotacao.adiantamentos || [];
  adiantamentos.push({
    id: adiantamentoId,
    valor,
    data: new Date().toISOString(),
  });
  
  const totalAdiantado = adiantamentos.reduce((sum, ad) => sum + ad.valor, 0);

  return await atualizarCotacao(cotacaoId, {
    adiantamentos,
    totalAdiantado,
  });
}

/**
 * ✅ Remove a ligação de um adiantamento eliminado desta cotação (chamado
 * por adiantamentos.excluirAdiantamento) — sem isto, eliminar o
 * adiantamento não desfazia o registo na Cotação, que continuava a mostrar
 * o adiantamento como aplicado mesmo depois de ele deixar de existir.
 * Devolve null (em vez de lançar) se a cotação já não existir, para uma
 * eliminação de adiantamento nunca falhar por causa de uma cotação
 * entretanto também eliminada.
 */
export async function removerAdiantamento(
  cotacaoId: string,
  adiantamentoId: string,
): Promise<Cotacao | null> {
  const cotacao = await buscarCotacaoPorId(cotacaoId);
  if (!cotacao) return null;

  const adiantamentosExistentes = cotacao.adiantamentos || [];
  const adiantamentos = adiantamentosExistentes.filter((ad) => ad.id !== adiantamentoId);
  if (adiantamentos.length === adiantamentosExistentes.length) {
    return cotacao;
  }

  const totalAdiantado = adiantamentos.reduce((sum, ad) => sum + ad.valor, 0);

  return await atualizarCotacao(cotacaoId, {
    adiantamentos,
    totalAdiantado,
  });
}

/**
 * âœ… NOVO: Calcular estado financeiro da cotação
 */
export function calcularEstadoFinanceiro(cotacao: Cotacao): {
  tipo: 'normal' | 'adiantamento_parcial' | 'adiantamento_total' | 'adiantamento_excedente';
  percentual: number;
  valorRestante: number;
} {
  // Se não tem adiantamentos, é normal
  if (!cotacao.totalAdiantado || cotacao.totalAdiantado === 0) {
    return {
      tipo: 'normal',
      percentual: 0,
      valorRestante: cotacao.totalGeral,
    };
  }
  
  const percentual = (cotacao.totalAdiantado / cotacao.totalGeral) * 100;
  const valorRestante = cotacao.totalGeral - cotacao.totalAdiantado;
  
  // Adiantamento excedente (mais de 100%)
  if (percentual > 100) {
    return {
      tipo: 'adiantamento_excedente',
      percentual: 100,
      valorRestante: 0,
    };
  }
  
  // Adiantamento total (100%)
  if (percentual >= 100) {
    return {
      tipo: 'adiantamento_total',
      percentual: 100,
      valorRestante: 0,
    };
  }
  
  // Adiantamento parcial (0% < x < 100%)
  if (percentual > 0) {
    return {
      tipo: 'adiantamento_parcial',
      percentual,
      valorRestante,
    };
  }
  
  return {
    tipo: 'normal',
    percentual: 0,
    valorRestante: cotacao.totalGeral,
  };
}

/**
 * âœ… NOVO: Verificar se cotação pode ser editada (com adiantamentos)
 */
export function podeEditarCotacaoComAdiantamentos(cotacao: Cotacao): { pode: boolean; motivo?: string } {
  // Verificação padrão primeiro
  const validacaoPadrao = podeEditarCotacao(cotacao);
  if (!validacaoPadrao.pode) {
    return validacaoPadrao;
  }
  
  // Verificar estado financeiro
  const estadoFinanceiro = calcularEstadoFinanceiro(cotacao);
  
  // Adiantamento Total ou Excedente: NÃƒO pode editar
  if (estadoFinanceiro.tipo === 'adiantamento_total') {
    return { 
      pode: false, 
      motivo: 'Não é possível editar esta cotação porque já possui adiantamento total (100% do valor já foi adiantado)' 
    };
  }
  
  if (estadoFinanceiro.tipo === 'adiantamento_excedente') {
    return { 
      pode: false, 
      motivo: 'Não é possível editar esta cotação porque possui adiantamento acima do valor (excedente registrado como crédito)' 
    };
  }
  
  // Adiantamento Parcial: PODE editar (com restriçÃµes, mas deixamos o frontend controlar)
  if (estadoFinanceiro.tipo === 'adiantamento_parcial') {
    return { 
      pode: true,
      motivo: `Edição permitida com restriçÃµes: já foram adiantados ${estadoFinanceiro.percentual.toFixed(1)}% do valor (${cotacao.totalAdiantado.toFixed(2)} ${cotacao.moeda})`
    };
  }
  
  return { pode: true };
}

/**
 * âœ… NOVO: Verificar se cotação pode ser eliminada (com adiantamentos)
 */
export async function podeEliminarCotacaoComAdiantamentos(cotacao: Cotacao): Promise<{ pode: boolean; motivo?: string }> {
  // Se tem qualquer adiantamento, NÃƒO pode eliminar
  if (cotacao.adiantamentos && cotacao.adiantamentos.length > 0) {
    const estadoFinanceiro = calcularEstadoFinanceiro(cotacao);
    let detalhes = '';
    
    if (estadoFinanceiro.tipo === 'adiantamento_parcial') {
      detalhes = ` (${estadoFinanceiro.percentual.toFixed(1)}% adiantado)`;
    } else if (estadoFinanceiro.tipo === 'adiantamento_total') {
      detalhes = ' (adiantamento total)';
    } else if (estadoFinanceiro.tipo === 'adiantamento_excedente') {
      detalhes = ' (adiantamento com excedente)';
    }
    
    return { 
      pode: false, 
      motivo: `Não é possível eliminar esta cotação porque já possui ${cotacao.adiantamentos.length} adiantamento(s) associado(s)${detalhes}` 
    };
  }
  
  // Validação padrão de sequência
  return await podeEliminarCotacao(cotacao);
}
