﻿import * as kv from './kv_store.tsx';
import { buscarArtigoPorId } from './artigos.tsx';
import { buscarArmazemPorId } from './armazens.tsx';
import { buscarStockArtigoArmazem, upsertStock, calcularNovoCustoMedio } from './stock-armazem.tsx';
import * as stockLotes from './stock-lotes.tsx';
import { createLogger } from './logger.tsx';
import { validarDataEmissaoCronologica } from './documento-cronologia.tsx';

const logger = createLogger('Transferencias');

export interface Transferencia {
  id: string;
  empresaId: string;
  numero: string;
  artigoId: string;
  artigoCodigo: string;
  artigoNome: string;
  quantidade: number;
  armazemOrigemId: string;
  armazemOrigemCodigo: string;
  armazemOrigemNome: string;
  armazemDestinoId: string;
  armazemDestinoCodigo: string;
  armazemDestinoNome: string;
  movimentoSaidaId: string;
  movimentoEntradaId: string;
  motivo?: string;
  observacoes?: string;
  responsavel?: string;
  responsavelId?: string;
  responsavelNome?: string;
  responsavelEmail?: string;
  criadoPor?: string;
  criadoPorNome?: string;
  criadoPorEmail?: string;
  status: 'pendente' | 'concluida' | 'cancelada';
  modoTransferencia?: 'lotes' | 'legado';
  lotesTransferidos?: Array<{
    loteOrigemId: string;
    loteDestinoId: string;
    quantidade: number;
    lote?: string;
    dataValidade?: string;
  }>;
  dataCriacao: string;
  dataTransferencia: string;
  dataConclusao?: string;
  dataCancelamento?: string;
}

interface MovimentoTransferencia {
  id: string;
  empresaId: string;
  artigoId: string;
  artigoCodigo: string;
  artigoNome: string;
  tipo: 'entrada' | 'saida';
  quantidade: number;
  motivo: string;
  armazemId: string;
  armazemCodigo: string;
  armazemNome: string;
  documentoTipo: string;
  documentoNumero: string;
  observacoes?: string;
  responsavel: string;
  responsavelId?: string;
  responsavelNome?: string;
  responsavelEmail?: string;
  criadoPor?: string;
  criadoPorNome?: string;
  criadoPorEmail?: string;
  dataCriacao: string;
  dataMovimento: string;
  lotesConsumidos?: Array<{
    loteId: string;
    lote?: string;
    dataValidade?: string;
    quantidade: number;
  }>;
}

async function gerarProximoNumero(empresaId: string): Promise<string> {
  const transferencias = await listarTransferencias(empresaId);
  const maiorSequencia = transferencias.reduce((max, transferencia) => {
    const match = transferencia.numero.match(/^TRANSF-(\d+)$/);
    const sequencia = match ? Number.parseInt(match[1], 10) : 0;
    return Number.isFinite(sequencia) && sequencia > max ? sequencia : max;
  }, 0);
  const numeroAtual = maiorSequencia + 1;
  return `TRANSF-${numeroAtual.toString().padStart(4, '0')}`;
}

function gerarIdMovimento(sufixo: string): string {
  return `${Date.now()}-${sufixo}-${Math.random().toString(36).substr(2, 9)}`;
}

async function salvarMovimentoTransferencia(movimento: MovimentoTransferencia): Promise<void> {
  await kv.set(`movimento_stock:${movimento.id}`, movimento);
}

async function transferirPorLotes(dados: {
  empresaId: string;
  artigoId: string;
  armazemOrigemId: string;
  armazemDestinoId: string;
  quantidade: number;
}): Promise<{
  success: boolean;
  modo?: 'lotes';
  lotesTransferidos?: Transferencia['lotesTransferidos'];
  error?: string;
}> {
  const lotesConsumiveis = (await stockLotes.listarLotes(dados.empresaId, {
    artigoId: dados.artigoId,
    armazemId: dados.armazemOrigemId,
    incluirExpirados: false,
    somenteDisponivel: true,
  })).filter((lote) => lote.status !== 'bloqueado');

  if (lotesConsumiveis.length === 0) {
    return { success: true, modo: undefined, lotesTransferidos: [] } as any;
  }

  const totalDisponivel = lotesConsumiveis.reduce((total, lote) => total + lote.quantidadeDisponivel, 0);
  if (totalDisponivel < dados.quantidade) {
    return {
      success: false,
      error: `Stock insuficiente em lotes validos no armazem de origem. Disponivel: ${totalDisponivel}, solicitado: ${dados.quantidade}`,
    };
  }

  let restante = dados.quantidade;
  const lotesTransferidos: NonNullable<Transferencia['lotesTransferidos']> = [];
  const lotesMovidos: Array<{ loteDestinoId: string; quantidade: number }> = [];

  try {
    for (const lote of lotesConsumiveis) {
      if (restante <= 0) break;

      const quantidadeMover = Math.min(restante, lote.quantidadeDisponivel);
      if (quantidadeMover <= 0) continue;

      const resultado = await stockLotes.transferirLoteEntreArmazens({
        empresaId: dados.empresaId,
        loteId: lote.id,
        armazemDestinoId: dados.armazemDestinoId,
        quantidade: quantidadeMover,
      });

      if (!resultado.success || !resultado.loteDestino) {
        throw new Error(resultado.error || 'Erro ao transferir lote entre armazens');
      }

      lotesTransferidos.push({
        loteOrigemId: lote.id,
        loteDestinoId: resultado.loteDestino.id,
        quantidade: quantidadeMover,
        lote: lote.lote,
        dataValidade: lote.dataValidade,
      });
      lotesMovidos.push({ loteDestinoId: resultado.loteDestino.id, quantidade: quantidadeMover });
      restante -= quantidadeMover;
    }

    if (restante > 0) {
      throw new Error(`Transferencia incompleta por lotes. Restante: ${restante}`);
    }

    return {
      success: true,
      modo: 'lotes',
      lotesTransferidos,
    };
  } catch (error) {
    for (const loteMovido of lotesMovidos.reverse()) {
      await stockLotes.transferirLoteEntreArmazens({
        empresaId: dados.empresaId,
        loteId: loteMovido.loteDestinoId,
        armazemDestinoId: dados.armazemOrigemId,
        quantidade: loteMovido.quantidade,
      });
    }

    return {
      success: false,
      error: error instanceof Error ? error.message : 'Erro ao transferir lotes',
    };
  }
}

async function transferirSemLotes(dados: {
  empresaId: string;
  artigoId: string;
  armazemOrigemId: string;
  armazemDestinoId: string;
  quantidade: number;
}): Promise<{ success: boolean; modo?: 'legado'; error?: string }> {
  const stockOrigem = await buscarStockArtigoArmazem(dados.empresaId, dados.artigoId, dados.armazemOrigemId);
  const stockDestino = await buscarStockArtigoArmazem(dados.empresaId, dados.artigoId, dados.armazemDestinoId);

  const quantidadeDisponivelOrigem = stockOrigem?.quantidadeDisponivel || 0;
  if (quantidadeDisponivelOrigem < dados.quantidade) {
    return {
      success: false,
      error: `Stock insuficiente no armazem de origem. Disponivel: ${quantidadeDisponivelOrigem}, solicitado: ${dados.quantidade}`,
    };
  }

  await upsertStock({
    empresaId: dados.empresaId,
    artigoId: dados.artigoId,
    armazemId: dados.armazemOrigemId,
    quantidade: (stockOrigem?.quantidade || 0) - dados.quantidade,
  });

  // ✅ O destino herda o custo do armazem de origem (media ponderada com o
  // que ja tinha), em vez de ficar sem custoMedio passado — o que fazia
  // upsertStock criar o registo novo sempre com custoMedio=0 quando o artigo
  // ainda nao existia nesse armazem.
  const custoMedioDestino = calcularNovoCustoMedio(
    stockDestino?.quantidade || 0,
    stockDestino?.custoMedio || 0,
    dados.quantidade,
    stockOrigem?.custoMedio || 0,
  );

  await upsertStock({
    empresaId: dados.empresaId,
    artigoId: dados.artigoId,
    armazemId: dados.armazemDestinoId,
    quantidade: (stockDestino?.quantidade || 0) + dados.quantidade,
    custoMedio: custoMedioDestino,
  });

  return { success: true, modo: 'legado' };
}

export async function criarTransferencia(dados: {
  empresaId: string;
  artigoId: string;
  quantidade: number;
  armazemOrigemId: string;
  armazemDestinoId: string;
  motivo?: string;
  observacoes?: string;
  responsavel?: string;
  responsavelId?: string;
  responsavelNome?: string;
  responsavelEmail?: string;
  criadoPor?: string;
  criadoPorNome?: string;
  criadoPorEmail?: string;
  usuarioAuditoriaId?: string;
  usuarioAuditoriaNome?: string;
  usuarioAuditoriaEmail?: string;
  dataTransferencia: string;
}): Promise<{ success: boolean; transferencia?: Transferencia; error?: string }> {
  try {
    logger.info('[Transferencias] Iniciando criacao de transferencia:', dados);

    if (dados.armazemOrigemId === dados.armazemDestinoId) {
      return { success: false, error: 'Armazem de origem e destino devem ser diferentes' };
    }

    if (dados.quantidade <= 0) {
      return { success: false, error: 'Quantidade invalida para transferencia' };
    }

    const artigo = await buscarArtigoPorId(dados.artigoId);
    if (!artigo) {
      return { success: false, error: 'Artigo nao encontrado' };
    }

    if (artigo.tipo === 'servico') {
      return { success: false, error: 'Servicos nao podem ser transferidos entre armazens' };
    }

    const armazemOrigem = await buscarArmazemPorId(dados.armazemOrigemId, dados.empresaId);
    if (!armazemOrigem) {
      return { success: false, error: 'Armazem de origem nao encontrado' };
    }

    const armazemDestino = await buscarArmazemPorId(dados.armazemDestinoId, dados.empresaId);
    if (!armazemDestino) {
      return { success: false, error: 'Armazem de destino nao encontrado' };
    }

    const armazemOrigemIdCanonical = armazemOrigem.id;
    const armazemDestinoIdCanonical = armazemDestino.id;

    const stockOrigem = await buscarStockArtigoArmazem(dados.empresaId, dados.artigoId, armazemOrigemIdCanonical);
    const quantidadeDisponivel = stockOrigem?.quantidadeDisponivel || 0;

    if (quantidadeDisponivel < dados.quantidade) {
      return {
        success: false,
        error: `Stock insuficiente no armazem de origem. Disponivel: ${quantidadeDisponivel}, solicitado: ${dados.quantidade}`,
      };
    }

    const transferenciasExistentes = await listarTransferencias(dados.empresaId);
    validarDataEmissaoCronologica(
      { dataTransferencia: dados.dataTransferencia },
      transferenciasExistentes,
      {
        rotuloDocumento: 'Transferência',
        dataField: 'dataTransferencia',
        numeroField: 'numero',
      },
    );

    const transferenciaCriada = Date.now();
    const transferenciaCriadaISO = new Date(transferenciaCriada).toISOString();
    const transferenciaId = `${transferenciaCriada}-${Math.random().toString(36).substr(2, 9)}`;
    const numero = await gerarProximoNumero(dados.empresaId);
    const responsavel = dados.responsavelNome || dados.responsavel || dados.usuarioAuditoriaNome || dados.criadoPorNome || 'Sistema';
    const responsavelId = dados.responsavelId || dados.usuarioAuditoriaId || dados.criadoPor;
    const responsavelEmail = dados.responsavelEmail || dados.usuarioAuditoriaEmail || dados.criadoPorEmail;

    const resultadoLotes = await transferirPorLotes({
      empresaId: dados.empresaId,
      artigoId: dados.artigoId,
      armazemOrigemId: armazemOrigemIdCanonical,
      armazemDestinoId: armazemDestinoIdCanonical,
      quantidade: dados.quantidade,
    });

    let modoTransferencia: Transferencia['modoTransferencia'];
    let lotesTransferidos: Transferencia['lotesTransferidos'];

    if (!resultadoLotes.success) {
      return { success: false, error: resultadoLotes.error };
    }

    if (resultadoLotes.modo === 'lotes') {
      modoTransferencia = 'lotes';
      lotesTransferidos = resultadoLotes.lotesTransferidos;
    } else {
      const resultadoLegado = await transferirSemLotes({
        empresaId: dados.empresaId,
        artigoId: dados.artigoId,
        armazemOrigemId: armazemOrigemIdCanonical,
        armazemDestinoId: armazemDestinoIdCanonical,
        quantidade: dados.quantidade,
      });

      if (!resultadoLegado.success) {
        return { success: false, error: resultadoLegado.error };
      }

      modoTransferencia = 'legado';
      lotesTransferidos = [];
    }

    const movimentoSaidaId = gerarIdMovimento('saida');
    const movimentoEntradaId = gerarIdMovimento('entrada');

    await salvarMovimentoTransferencia({
      id: movimentoSaidaId,
      empresaId: dados.empresaId,
      artigoId: dados.artigoId,
      artigoCodigo: artigo.codigo,
      artigoNome: artigo.nome,
      tipo: 'saida',
      quantidade: dados.quantidade,
      motivo: 'transferencia_saida',
      armazemId: armazemOrigemIdCanonical,
      armazemCodigo: armazemOrigem.codigo,
      armazemNome: armazemOrigem.nome,
      documentoTipo: 'Transferencia',
      documentoNumero: numero,
      observacoes: dados.observacoes || `Transferencia para ${armazemDestino.nome}`,
      responsavel,
      responsavelId,
      responsavelNome: responsavel,
      responsavelEmail,
      criadoPor: dados.criadoPor || dados.usuarioAuditoriaId,
      criadoPorNome: dados.criadoPorNome || dados.usuarioAuditoriaNome || responsavel,
      criadoPorEmail: dados.criadoPorEmail || dados.usuarioAuditoriaEmail,
      dataCriacao: transferenciaCriadaISO,
      dataMovimento: dados.dataTransferencia,
      lotesConsumidos: lotesTransferidos?.map((lote) => ({
        loteId: lote.loteOrigemId,
        lote: lote.lote,
        dataValidade: lote.dataValidade,
        quantidade: lote.quantidade,
      })),
    });

    await salvarMovimentoTransferencia({
      id: movimentoEntradaId,
      empresaId: dados.empresaId,
      artigoId: dados.artigoId,
      artigoCodigo: artigo.codigo,
      artigoNome: artigo.nome,
      tipo: 'entrada',
      quantidade: dados.quantidade,
      motivo: 'transferencia_entrada',
      armazemId: armazemDestinoIdCanonical,
      armazemCodigo: armazemDestino.codigo,
      armazemNome: armazemDestino.nome,
      documentoTipo: 'Transferencia',
      documentoNumero: numero,
      observacoes: dados.observacoes || `Transferencia de ${armazemOrigem.nome}`,
      responsavel,
      responsavelId,
      responsavelNome: responsavel,
      responsavelEmail,
      criadoPor: dados.criadoPor || dados.usuarioAuditoriaId,
      criadoPorNome: dados.criadoPorNome || dados.usuarioAuditoriaNome || responsavel,
      criadoPorEmail: dados.criadoPorEmail || dados.usuarioAuditoriaEmail,
      dataCriacao: transferenciaCriadaISO,
      dataMovimento: dados.dataTransferencia,
      lotesConsumidos: lotesTransferidos?.map((lote) => ({
        loteId: lote.loteDestinoId,
        lote: lote.lote,
        dataValidade: lote.dataValidade,
        quantidade: lote.quantidade,
      })),
    });

    const transferencia: Transferencia = {
      id: transferenciaId,
      empresaId: dados.empresaId,
      numero,
      artigoId: dados.artigoId,
      artigoCodigo: artigo.codigo,
      artigoNome: artigo.nome,
      quantidade: dados.quantidade,
      armazemOrigemId: armazemOrigemIdCanonical,
      armazemOrigemCodigo: armazemOrigem.codigo,
      armazemOrigemNome: armazemOrigem.nome,
      armazemDestinoId: armazemDestinoIdCanonical,
      armazemDestinoCodigo: armazemDestino.codigo,
      armazemDestinoNome: armazemDestino.nome,
      movimentoSaidaId,
      movimentoEntradaId,
      motivo: dados.motivo,
      observacoes: dados.observacoes,
      responsavel,
      responsavelId,
      responsavelNome: responsavel,
      responsavelEmail,
      criadoPor: dados.criadoPor || dados.usuarioAuditoriaId,
      criadoPorNome: dados.criadoPorNome || dados.usuarioAuditoriaNome || responsavel,
      criadoPorEmail: dados.criadoPorEmail || dados.usuarioAuditoriaEmail,
      status: 'concluida',
      modoTransferencia,
      lotesTransferidos,
      dataCriacao: transferenciaCriadaISO,
      dataTransferencia: dados.dataTransferencia,
      dataConclusao: transferenciaCriadaISO,
    };

    await kv.set(`transferencia:${dados.empresaId}:${transferenciaId}`, transferencia);

    logger.info('[Transferencias] Transferencia criada com sucesso:', transferencia.numero);
    return { success: true, transferencia };
  } catch (error) {
    logger.error('[Transferencias] Erro ao criar transferencia:', error);
    return { success: false, error: error instanceof Error ? error.message : String(error) };
  }
}

export async function listarTransferencias(
  empresaId: string,
  filtros?: {
    artigoId?: string;
    armazemOrigemId?: string;
    armazemDestinoId?: string;
    status?: string;
    dataInicio?: string;
    dataFim?: string;
  }
): Promise<Transferencia[]> {
  try {
    const prefix = `transferencia:${empresaId}:`;
    const resultados = await kv.getByPrefixWithKeys(prefix);

    let transferencias = resultados
      .map((r: any) => r.value as Transferencia)
      .filter((t: Transferencia) => t && t.id);

    if (filtros) {
      if (filtros.artigoId) transferencias = transferencias.filter((t) => t.artigoId === filtros.artigoId);
      if (filtros.armazemOrigemId) transferencias = transferencias.filter((t) => t.armazemOrigemId === filtros.armazemOrigemId);
      if (filtros.armazemDestinoId) transferencias = transferencias.filter((t) => t.armazemDestinoId === filtros.armazemDestinoId);
      if (filtros.status) transferencias = transferencias.filter((t) => t.status === filtros.status);
      if (filtros.dataInicio) transferencias = transferencias.filter((t) => t.dataTransferencia >= filtros.dataInicio!);
      if (filtros.dataFim) transferencias = transferencias.filter((t) => t.dataTransferencia <= filtros.dataFim!);
    }

    transferencias.sort((a, b) => new Date(b.dataTransferencia).getTime() - new Date(a.dataTransferencia).getTime());
    return transferencias;
  } catch (error) {
    logger.error('[Transferencias] Erro ao listar transferencias:', error);
    return [];
  }
}

export async function buscarTransferenciaPorId(id: string): Promise<Transferencia | null> {
  try {
    const transferencias = await kv.getByPrefix('transferencia:');
    const transferencia = transferencias
      .map((r: any) => r.value as Transferencia)
      .find((t: Transferencia) => t.id === id);

    return transferencia || null;
  } catch (error) {
    logger.error('[Transferencias] Erro ao buscar transferencia:', error);
    return null;
  }
}

export async function cancelarTransferencia(id: string): Promise<{ success: boolean; error?: string }> {
  try {
    const transferencia = await buscarTransferenciaPorId(id);

    if (!transferencia) {
      return { success: false, error: 'Transferencia nao encontrada' };
    }

    if (transferencia.status === 'cancelada') {
      return { success: false, error: 'Transferencia ja foi cancelada' };
    }

    const dataCancelamento = new Date().toISOString();
    const responsavel = transferencia.responsavel || 'Sistema';

    if (transferencia.modoTransferencia === 'lotes' && transferencia.lotesTransferidos && transferencia.lotesTransferidos.length > 0) {
      const lotesRevertidos: Array<{ loteOrigemId: string; quantidade: number }> = [];

      try {
        for (const lote of transferencia.lotesTransferidos) {
          const resultado = await stockLotes.transferirLoteEntreArmazens({
            empresaId: transferencia.empresaId,
            loteId: lote.loteDestinoId,
            armazemDestinoId: transferencia.armazemOrigemId,
            quantidade: lote.quantidade,
          });

          if (!resultado.success || !resultado.loteDestino) {
            throw new Error(resultado.error || 'Erro ao reverter transferencia por lotes');
          }

          lotesRevertidos.push({
            loteOrigemId: resultado.loteDestino.id,
            quantidade: lote.quantidade,
          });
        }
      } catch (error) {
        for (const lote of lotesRevertidos.reverse()) {
          await stockLotes.transferirLoteEntreArmazens({
            empresaId: transferencia.empresaId,
            loteId: lote.loteOrigemId,
            armazemDestinoId: transferencia.armazemDestinoId,
            quantidade: lote.quantidade,
          });
        }

        return { success: false, error: error instanceof Error ? error.message : String(error) };
      }
    } else {
      const stockDestino = await buscarStockArtigoArmazem(transferencia.empresaId, transferencia.artigoId, transferencia.armazemDestinoId);
      const quantidadeDisponivelDestino = stockDestino?.quantidadeDisponivel || 0;

      if (quantidadeDisponivelDestino < transferencia.quantidade) {
        return {
          success: false,
          error: `Nao e possivel cancelar a transferencia porque o armazem de destino tem apenas ${quantidadeDisponivelDestino} unidades disponiveis para retorno.`
        };
      }

      const stockOrigem = await buscarStockArtigoArmazem(transferencia.empresaId, transferencia.artigoId, transferencia.armazemOrigemId);
      await upsertStock({
        empresaId: transferencia.empresaId,
        artigoId: transferencia.artigoId,
        armazemId: transferencia.armazemOrigemId,
        quantidade: (stockOrigem?.quantidade || 0) + transferencia.quantidade,
      });

      await upsertStock({
        empresaId: transferencia.empresaId,
        artigoId: transferencia.artigoId,
        armazemId: transferencia.armazemDestinoId,
        quantidade: (stockDestino?.quantidade || 0) - transferencia.quantidade,
      });
    }

    await salvarMovimentoTransferencia({
      id: gerarIdMovimento('estorno-origem'),
      empresaId: transferencia.empresaId,
      artigoId: transferencia.artigoId,
      artigoCodigo: transferencia.artigoCodigo,
      artigoNome: transferencia.artigoNome,
      tipo: 'entrada',
      quantidade: transferencia.quantidade,
      motivo: 'ajuste_positivo',
      armazemId: transferencia.armazemOrigemId,
      armazemCodigo: transferencia.armazemOrigemCodigo,
      armazemNome: transferencia.armazemOrigemNome,
      documentoTipo: 'Estorno Transferencia',
      documentoNumero: transferencia.numero,
      observacoes: `Cancelamento da transferencia ${transferencia.numero}`,
      responsavel,
      dataCriacao: dataCancelamento,
      dataMovimento: dataCancelamento.split('T')[0],
    });

    await salvarMovimentoTransferencia({
      id: gerarIdMovimento('estorno-destino'),
      empresaId: transferencia.empresaId,
      artigoId: transferencia.artigoId,
      artigoCodigo: transferencia.artigoCodigo,
      artigoNome: transferencia.artigoNome,
      tipo: 'saida',
      quantidade: transferencia.quantidade,
      motivo: 'ajuste_negativo',
      armazemId: transferencia.armazemDestinoId,
      armazemCodigo: transferencia.armazemDestinoCodigo,
      armazemNome: transferencia.armazemDestinoNome,
      documentoTipo: 'Estorno Transferencia',
      documentoNumero: transferencia.numero,
      observacoes: `Cancelamento da transferencia ${transferencia.numero}`,
      responsavel,
      dataCriacao: dataCancelamento,
      dataMovimento: dataCancelamento.split('T')[0],
    });

    transferencia.status = 'cancelada';
    transferencia.dataCancelamento = dataCancelamento;
    await kv.set(`transferencia:${transferencia.empresaId}:${transferencia.id}`, transferencia);

    return { success: true };
  } catch (error) {
    logger.error('[Transferencias] Erro ao cancelar transferencia:', error);
    return { success: false, error: error instanceof Error ? error.message : String(error) };
  }
}
