import * as kv from "./kv_store.tsx";
import * as compras from "./compras.tsx";
import * as empresas from "./empresas.tsx";
import * as fornecedores from "./fornecedores.tsx";
import { isValidNuit, normalizeNuit } from "./nuit.tsx";

export type TipoDocumentoInterempresa = "factura" | "factura_recibo" | "venda_dinheiro";
export type EstadoCompraInterempresa = "pendente" | "aceite" | "rejeitado" | "ignorado";

export interface LinhaCompraInterempresa {
  id: string;
  artigoOrigemId?: string;
  codigo: string;
  descricao: string;
  tipoArtigo?: "produto" | "servico";
  unidade: string;
  quantidade: number;
  precoUnitario: number;
  iva: number;
  ivaIncluso: boolean;
  desconto: number;
  subtotal: number;
}

export interface CompraInterempresaPendente {
  id: string;
  empresaOrigemId: string;
  empresaOrigemNome: string;
  empresaOrigemNuit: string;
  empresaDestinoId: string;
  clienteOrigemId?: string;
  clienteOrigemNome?: string;
  clienteOrigemNuit?: string;
  fornecedorId?: string;
  documentoOrigemId: string;
  documentoOrigemTipo: TipoDocumentoInterempresa;
  documentoOrigemNumero: string;
  tipoDocumentoCompra: compras.TipoDocumentoCompra;
  dataEmissao: string;
  dataVencimento?: string;
  moeda?: string;
  linhas: LinhaCompraInterempresa[];
  subtotal: number;
  descontoGlobal: number;
  totalIVA: number;
  totalGeral: number;
  observacoes?: string;
  estado: EstadoCompraInterempresa;
  poId?: string;
  poNumero?: string;
  compraId?: string;
  divergencias?: string[];
  criadoEm: string;
  atualizadoEm?: string;
  aceiteEm?: string;
  aceitePor?: string;
  rejeitadoEm?: string;
  rejeitadoPor?: string;
  motivoRejeicao?: string;
}

function chaveCompraInterempresa(empresaDestinoId: string, id: string) {
  return `compra_interempresa:${empresaDestinoId}:${id}`;
}

function criarIdCompraInterempresa(empresaOrigemId: string, tipo: string, documentoId: string, empresaDestinoId: string) {
  return `${empresaOrigemId}:${tipo}:${documentoId}:${empresaDestinoId}`
    .replace(/[^a-zA-Z0-9:_-]+/g, "_")
    .slice(0, 180);
}

function normalizarTexto(valor?: string) {
  return (valor || "")
    .normalize("NFD")
    .replace(/[\u0300-\u036f]/g, "")
    .trim()
    .toLowerCase();
}

function mapearTipoDocumentoCompra(tipo: TipoDocumentoInterempresa): compras.TipoDocumentoCompra {
  if (tipo === "factura_recibo") return "Factura-Recibo";
  if (tipo === "venda_dinheiro") return "VD";
  return "Factura";
}

function numero(valor: unknown): number {
  const n = Number(valor || 0);
  return Number.isFinite(n) ? n : 0;
}

function obterNuitCliente(documento: any) {
  return normalizeNuit(
    documento?.cliente?.nuit ||
      documento?.clienteNuit ||
      documento?.nuit ||
      ""
  );
}

function mapearLinhasDocumentoVenda(documento: any): LinhaCompraInterempresa[] {
  const itens = Array.isArray(documento?.itens) ? documento.itens : [];

  return itens
    .filter((item: any) => {
      const tipo = item?.tipo;
      return tipo === undefined || tipo === "normal" || tipo === "artigo" || tipo === "produto" || tipo === "servico";
    })
    .map((item: any, index: number) => {
      const quantidade = numero(item.quantidade);
      const precoUnitario = numero(item.precoUnitario ?? item.valorUnitario);
      const desconto = numero(item.desconto);
      const iva = numero(item.imposto ?? item.iva);
      const subtotal = numero(item.total) || quantidade * precoUnitario;

      return {
        id: item.id || `linha_${index + 1}`,
        artigoOrigemId: item.artigoId,
        codigo: String(item.codigo || item.artigoCodigo || `LINHA-${index + 1}`).trim().toUpperCase(),
        descricao: String(item.descricao || item.nome || item.codigo || `Linha ${index + 1}`).trim(),
        tipoArtigo: item.tipoArtigo || item.artigoTipo || (item.artigoId ? "produto" : "servico"),
        unidade: String(item.unidade || "Un"),
        quantidade,
        precoUnitario,
        iva,
        ivaIncluso: Boolean(item.ivaIncluso),
        desconto,
        subtotal,
      };
    })
    .filter((linha) => linha.codigo && linha.descricao && linha.quantidade > 0);
}

async function garantirFornecedorDestino(documento: CompraInterempresaPendente) {
  const nuitFornecedor = normalizeNuit(documento.empresaOrigemNuit);
  if (!isValidNuit(nuitFornecedor)) {
    return { success: false, error: "NUIT do fornecedor interempresa invalido" };
  }

  const existente = await fornecedores.buscarFornecedorPorNuit(documento.empresaDestinoId, nuitFornecedor);
  if (existente) {
    return { success: true, data: existente };
  }

  return await fornecedores.criarFornecedor({
    empresaId: documento.empresaDestinoId,
    nome: documento.empresaOrigemNome || "Fornecedor interempresa",
    nomeComercial: documento.empresaOrigemNome || "Fornecedor interempresa",
    nuit: nuitFornecedor,
    email: "",
    telefone: "",
    endereco: "",
    cidade: "",
    provincia: "",
    pais: "Moçambique",
    observacoes: "Fornecedor criado automaticamente por integração interempresa.",
    ativo: true,
  } as any);
}

function compararComPO(documento: CompraInterempresaPendente, po?: compras.Compra | null) {
  const divergencias: string[] = [];
  if (!po) return divergencias;

  if (po.fornecedorId && documento.fornecedorId && po.fornecedorId !== documento.fornecedorId) {
    divergencias.push("Fornecedor da PO diferente do fornecedor do documento recebido.");
  }

  const linhasPO = new Map((po.linhas || []).map((linha) => [normalizarTexto(linha.codigo), linha]));
  for (const linha of documento.linhas) {
    const linhaPO = linhasPO.get(normalizarTexto(linha.codigo));
    if (!linhaPO) {
      divergencias.push(`Artigo ${linha.codigo} nao existe na PO.`);
      continue;
    }

    if (Math.abs(numero(linhaPO.quantidade) - linha.quantidade) > 0.0001) {
      divergencias.push(`Quantidade diferente em ${linha.codigo}: PO ${linhaPO.quantidade}, recebido ${linha.quantidade}.`);
    }

    if (Math.abs(numero(linhaPO.precoUnitario) - linha.precoUnitario) > 0.01) {
      divergencias.push(`Preco diferente em ${linha.codigo}: PO ${linhaPO.precoUnitario}, recebido ${linha.precoUnitario}.`);
    }
  }

  return divergencias;
}

export async function preverDivergenciasCompraInterempresa(
  empresaDestinoId: string,
  id: string,
  opcoes: { poId?: string } = {},
): Promise<{ success: boolean; data?: { divergencias: string[]; poId?: string; poNumero?: string }; error?: string }> {
  const documento = await buscarCompraInterempresa(empresaDestinoId, id);
  if (!documento) {
    return { success: false, error: "Compra interempresa nao encontrada" };
  }

  if (!opcoes.poId) {
    return { success: true, data: { divergencias: [] } };
  }

  const po = await compras.buscarCompraPorId(opcoes.poId);
  if (!po || po.empresaId !== empresaDestinoId) {
    return { success: false, error: "PO nao encontrada para esta empresa" };
  }

  const fornecedor = await fornecedores.buscarFornecedorPorNuit(empresaDestinoId, documento.empresaOrigemNuit);
  const divergencias = compararComPO({ ...documento, fornecedorId: fornecedor?.id }, po);

  return {
    success: true,
    data: {
      divergencias,
      poId: po.id,
      poNumero: po.numeroDocumento,
    },
  };
}

export async function criarOuAtualizarCompraInterempresaDeVenda(
  documentoVenda: any,
  tipoDocumento: TipoDocumentoInterempresa,
): Promise<{ success: boolean; skipped?: boolean; data?: CompraInterempresaPendente; error?: string }> {
  try {
    const empresaOrigemId = documentoVenda?.empresaId;
    const documentoOrigemId = documentoVenda?.id || documentoVenda?.numeroDocumento;
    const documentoOrigemNumero = documentoVenda?.numeroDocumento;
    const clienteNuit = obterNuitCliente(documentoVenda);

    if (!empresaOrigemId || !documentoOrigemId || !documentoOrigemNumero || !isValidNuit(clienteNuit)) {
      return { success: true, skipped: true };
    }

    const empresaDestino = await empresas.buscarEmpresaPorNuit(clienteNuit);
    if (!empresaDestino || empresaDestino.id === empresaOrigemId || empresaDestino.status !== "ativa") {
      return { success: true, skipped: true };
    }

    const empresaOrigem = await empresas.buscarEmpresaPorId(empresaOrigemId);
    if (!empresaOrigem) {
      return { success: true, skipped: true };
    }

    const linhas = mapearLinhasDocumentoVenda(documentoVenda);
    if (linhas.length === 0) {
      return { success: true, skipped: true };
    }

    const id = criarIdCompraInterempresa(empresaOrigemId, tipoDocumento, documentoOrigemId, empresaDestino.id);
    const chave = chaveCompraInterempresa(empresaDestino.id, id);
    const existente = await kv.get(chave) as CompraInterempresaPendente | null;
    if (existente && existente.estado === "aceite") {
      return { success: true, data: existente };
    }

    const agora = new Date().toISOString();
    const pendente: CompraInterempresaPendente = {
      ...(existente || {}),
      id,
      empresaOrigemId,
      empresaOrigemNome: empresaOrigem.nomeComercial || empresaOrigem.nomeEmpresa,
      empresaOrigemNuit: normalizeNuit(empresaOrigem.nuit),
      empresaDestinoId: empresaDestino.id,
      clienteOrigemId: documentoVenda?.clienteId,
      clienteOrigemNome: documentoVenda?.cliente?.nome,
      clienteOrigemNuit: clienteNuit,
      documentoOrigemId,
      documentoOrigemTipo: tipoDocumento,
      documentoOrigemNumero,
      tipoDocumentoCompra: mapearTipoDocumentoCompra(tipoDocumento),
      dataEmissao: String(documentoVenda?.dataEmissao || new Date().toISOString()).slice(0, 10),
      dataVencimento: documentoVenda?.dataVencimento,
      moeda: documentoVenda?.moeda || "MZN",
      linhas,
      subtotal: numero(documentoVenda?.subtotal),
      descontoGlobal: numero(documentoVenda?.descontos),
      totalIVA: numero(documentoVenda?.totalImpostos),
      totalGeral: numero(documentoVenda?.totalGeral),
      observacoes: documentoVenda?.observacoes,
      estado: "pendente",
      criadoEm: existente?.criadoEm || agora,
      atualizadoEm: agora,
    };

    await kv.set(chave, pendente);
    return { success: true, data: pendente };
  } catch (error: any) {
    console.error("[Interempresa] Erro ao criar compra pendente:", error);
    return { success: false, error: error.message || String(error) };
  }
}

export async function listarComprasInterempresaPendentes(empresaDestinoId: string): Promise<CompraInterempresaPendente[]> {
  const documentos = await kv.getByPrefix<CompraInterempresaPendente>(`compra_interempresa:${empresaDestinoId}:`);
  return documentos
    .filter((documento) => documento && documento.empresaDestinoId === empresaDestinoId)
    .sort((a, b) => (b.criadoEm || "").localeCompare(a.criadoEm || ""));
}

export async function buscarCompraInterempresa(empresaDestinoId: string, id: string): Promise<CompraInterempresaPendente | null> {
  return await kv.get(chaveCompraInterempresa(empresaDestinoId, id)) as CompraInterempresaPendente | null;
}

export async function aceitarCompraInterempresa(
  empresaDestinoId: string,
  id: string,
  opcoes: {
    armazemId?: string;
    poId?: string;
    aceitePor?: string;
  } = {},
): Promise<{ success: boolean; data?: CompraInterempresaPendente; compra?: compras.Compra; error?: string }> {
  const documento = await buscarCompraInterempresa(empresaDestinoId, id);
  if (!documento) {
    return { success: false, error: "Compra interempresa nao encontrada" };
  }

  if (documento.estado === "aceite" && documento.compraId) {
    const compra = await compras.buscarCompraPorId(documento.compraId);
    return { success: true, data: documento, compra: compra || undefined };
  }

  const fornecedorResult = await garantirFornecedorDestino(documento);
  if (!fornecedorResult.success || !fornecedorResult.data) {
    return { success: false, error: fornecedorResult.error || "Falha ao garantir fornecedor" };
  }

  const fornecedor = fornecedorResult.data;
  const po = opcoes.poId ? await compras.buscarCompraPorId(opcoes.poId) : null;
  if (opcoes.poId && (!po || po.empresaId !== empresaDestinoId)) {
    return { success: false, error: "PO nao encontrada para esta empresa" };
  }

  const divergencias = compararComPO({ ...documento, fornecedorId: fornecedor.id }, po);
  const linhasCompra = documento.linhas.map((linha) => ({
    id: linha.id,
    codigo: linha.codigo,
    descricao: linha.descricao,
    tipoArtigo: linha.tipoArtigo || "produto",
    quantidade: linha.quantidade,
    unidade: linha.unidade,
    precoUnitario: linha.precoUnitario,
    precoVenda: linha.precoUnitario,
    desconto: linha.desconto,
    iva: linha.iva,
    ivaIncluso: linha.ivaIncluso,
    subtotal: linha.subtotal,
    armazemId: opcoes.armazemId,
  }));

  const resultadoCompra = await compras.processarDocumentoCompra({
    empresaId: empresaDestinoId,
    fornecedorId: fornecedor.id,
    fornecedorNome: fornecedor.nome,
    fornecedorCodigo: fornecedor.codigo,
    fornecedorNuit: fornecedor.nuit,
    numeroDocumento: documento.documentoOrigemNumero,
    tipoDocumento: documento.tipoDocumentoCompra,
    dataEmissao: documento.dataEmissao,
    dataVencimento: documento.dataVencimento,
    linhas: linhasCompra as any,
    detalhesLinhas: linhasCompra.map((linha) => ({
      linhaId: linha.id,
      tipo: (linha as any).tipoArtigo || "produto",
      precoVenda: linha.precoUnitario,
      armazemId: opcoes.armazemId,
    })),
    subtotal: documento.subtotal,
    descontoGlobal: documento.descontoGlobal,
    totalIVA: documento.totalIVA,
    totalGeral: documento.totalGeral,
    observacoes: [
      `Documento recebido automaticamente de ${documento.empresaOrigemNome}.`,
      `Origem: ${documento.documentoOrigemTipo} ${documento.documentoOrigemNumero}.`,
      po ? `Associado a PO ${po.numeroDocumento}.` : "",
      documento.observacoes || "",
    ].filter(Boolean).join(" "),
    armazemId: opcoes.armazemId,
    criadoPor: opcoes.aceitePor || "Interempresa",
  });

  if (!resultadoCompra.success || !resultadoCompra.data) {
    return { success: false, error: resultadoCompra.error || "Falha ao criar compra interempresa" };
  }

  const atualizado: CompraInterempresaPendente = {
    ...documento,
    estado: "aceite",
    fornecedorId: fornecedor.id,
    poId: po?.id,
    poNumero: po?.numeroDocumento,
    compraId: resultadoCompra.data.id,
    divergencias,
    aceiteEm: new Date().toISOString(),
    aceitePor: opcoes.aceitePor,
    atualizadoEm: new Date().toISOString(),
  };

  await kv.set(chaveCompraInterempresa(empresaDestinoId, id), atualizado);
  return { success: true, data: atualizado, compra: resultadoCompra.data };
}

export async function rejeitarCompraInterempresa(
  empresaDestinoId: string,
  id: string,
  opcoes: { rejeitadoPor?: string; motivo?: string } = {},
): Promise<{ success: boolean; data?: CompraInterempresaPendente; error?: string }> {
  const documento = await buscarCompraInterempresa(empresaDestinoId, id);
  if (!documento) {
    return { success: false, error: "Compra interempresa nao encontrada" };
  }

  if (documento.estado === "aceite") {
    return { success: false, error: "Nao e possivel rejeitar uma compra interempresa ja aceite" };
  }

  const atualizado: CompraInterempresaPendente = {
    ...documento,
    estado: "rejeitado",
    rejeitadoEm: new Date().toISOString(),
    rejeitadoPor: opcoes.rejeitadoPor,
    motivoRejeicao: opcoes.motivo,
    atualizadoEm: new Date().toISOString(),
  };

  await kv.set(chaveCompraInterempresa(empresaDestinoId, id), atualizado);
  return { success: true, data: atualizado };
}
