import type {
  Balance,
  FeeEstimation,
  TransactionIntent,
  TransactionValidation,
} from "@ledgerhq/coin-module-framework/api/index";
import {
  AmountRequired,
  FeeTooHigh,
  InvalidAddress,
  NotEnoughBalance,
  RecipientRequired,
} from "@ledgerhq/ledger-wallet-framework/errors";
import { isRecipientValidForTokenTransfer, validateAddress } from "../network/addresses";

function validateRecipient(
  intent: TransactionIntent,
  isTokenTransfer: boolean,
  errors: Record<string, Error>,
): void {
  if (!intent.recipient) {
    errors.recipient = new RecipientRequired();
    return;
  }

  const recipientValid = validateAddress(intent.recipient);
  if (
    !recipientValid.isValid ||
    (isTokenTransfer && !isRecipientValidForTokenTransfer(intent.recipient))
  ) {
    errors.recipient = new InvalidAddress("", { currencyName: "Filecoin" });
  }
}

function validateTokenAmount(
  intent: TransactionIntent,
  balances: Balance[],
  estimatedFees: bigint,
  errors: Record<string, Error>,
): bigint {
  // Find the matching token balance by lowercase assetReference
  const assetRef =
    "assetReference" in intent.asset ? intent.asset.assetReference.toLowerCase() : "";
  const tokenBalance = balances.find(
    b => "assetReference" in b.asset && b.asset.assetReference.toLowerCase() === assetRef,
  );

  const tokenAvailable = tokenBalance?.value ?? 0n;
  const amount = intent.useAllAmount ? tokenAvailable : intent.amount;

  if (amount <= 0n) {
    errors.amount = intent.useAllAmount ? new NotEnoughBalance() : new AmountRequired();
  } else if (amount > tokenAvailable) {
    errors.amount = new NotEnoughBalance();
  }

  // Token transfers still require native FIL to cover gas fees
  const nativeBal = balances.find(b => b.asset.type === "native");
  const nativeAvailable = nativeBal?.value ?? 0n;
  if (estimatedFees > 0n && estimatedFees > nativeAvailable) {
    errors.amount = new NotEnoughBalance();
  }

  return amount;
}

function validateNativeAmount(
  intent: TransactionIntent,
  balances: Balance[],
  estimatedFees: bigint,
  errors: Record<string, Error>,
  warnings: Record<string, Error>,
): bigint {
  const nativeBalance = balances.find(b => b.asset.type === "native");
  const available = nativeBalance?.value ?? 0n;

  let amount = intent.amount;
  if (intent.useAllAmount) {
    amount = available > estimatedFees ? available - estimatedFees : 0n;
  }

  if (amount <= 0n) {
    errors.amount = intent.useAllAmount ? new NotEnoughBalance() : new AmountRequired();
  } else if (amount + estimatedFees > available) {
    errors.amount = new NotEnoughBalance();
  }

  // Fee-too-high warning: warn when fee exceeds balance (not a hard error)
  if (estimatedFees > 0n && estimatedFees > available) {
    warnings.feeTooHigh = new FeeTooHigh();
  }

  return amount;
}

export async function validateIntent(
  intent: TransactionIntent,
  balances: Balance[],
  customFees?: FeeEstimation,
): Promise<TransactionValidation> {
  const errors: Record<string, Error> = {};
  const warnings: Record<string, Error> = {};

  const estimatedFees = customFees?.value ?? 0n;
  const isTokenTransfer = intent.asset.type !== "native";

  validateRecipient(intent, isTokenTransfer, errors);

  const amount = isTokenTransfer
    ? validateTokenAmount(intent, balances, estimatedFees, errors)
    : validateNativeAmount(intent, balances, estimatedFees, errors, warnings);

  const totalSpent = isTokenTransfer ? amount : amount + estimatedFees;

  return {
    errors,
    warnings,
    estimatedFees,
    amount,
    totalSpent,
  };
}
