import type { Transaction } from "@ledgerhq/coin-bitcoin/types";
import { formatCurrencyUnit } from "@ledgerhq/coin-framework/currencies/formatCurrencyUnit";
import type { Account, AccountLike, FeeStrategy } from "@ledgerhq/types-live";
import { BigNumber } from "bignumber.js";
import { useMemo } from "react";
import type { Transaction as SendFlowTransaction, TransactionStatus } from "../../generated/types";
import { getUTXOStatus } from "./logic";
import {
  bitcoinPickingStrategy,
  type BitcoinAccount,
  type BitcoinOutput,
  type Transaction as BitcoinTransaction,
  type TransactionStatus as BitcoinTransactionStatus,
} from "./types";

export const useFeesStrategy = (a: Account, t: Transaction): FeeStrategy[] => {
  const networkInfo = t.networkInfo;

  if (!networkInfo) return [];

  const strategies = networkInfo.feeItems.items
    .map(feeItem => {
      return {
        label: feeItem.speed,
        amount: feeItem.feePerByte,
        unit: a.currency.units[a.currency.units.length - 1], // Should be sat
      };
    })
    .reverse();
  return strategies;
};

function isBitcoinBasedAccount(account: AccountLike): account is BitcoinAccount {
  return "bitcoinResources" in account && account.bitcoinResources !== undefined;
}

function hasUtxoStrategy(tx: SendFlowTransaction): tx is BitcoinTransaction {
  return "utxoStrategy" in tx && tx.utxoStrategy != null;
}

function isBitcoinTransactionStatus(s: TransactionStatus): s is BitcoinTransactionStatus {
  return "txInputs" in s;
}

export type PickingStrategyOption = Readonly<{
  value: number;
  labelKey: string;
}>;

export type UtxoRowDisplayData = Readonly<{
  utxo: BitcoinOutput;
  titleLabel: string;
  formattedValue: string;
  excluded: boolean;
  exclusionReason: "pickPendingUtxo" | "userExclusion" | undefined;
  isUsedInTx: boolean;
  unconfirmed: boolean;
  isLastSelected: boolean;
  disabled: boolean;
  confirmations: number;
}>;

export type UseBitcoinUtxoDisplayDataParams = Readonly<{
  account: AccountLike;
  transaction: SendFlowTransaction;
  status: TransactionStatus;
  locale: string;
}>;

export type BitcoinUtxoDisplayData = Readonly<{
  pickingStrategyOptions: readonly PickingStrategyOption[];
  pickingStrategyValue: number;
  totalExcludedUTXOS: number;
  totalSpent: BigNumber;
  utxoRows: readonly UtxoRowDisplayData[];
}>;

// Object.keys returns string[]; cast needed for keyof typeof
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
const pickingStrategyKeys = Object.keys(
  bitcoinPickingStrategy,
) as (keyof typeof bitcoinPickingStrategy)[];

const pickingStrategyOptions: readonly PickingStrategyOption[] = pickingStrategyKeys.map(key => ({
  value: bitcoinPickingStrategy[key],
  labelKey: `bitcoin.pickingStrategyLabels.${String(key)}`,
}));

type UtxoStatus = ReturnType<typeof getUTXOStatus>;

function utxoToRowDisplayData(
  utxo: BitcoinOutput,
  utxoStatus: UtxoStatus,
  context: Readonly<{
    rowIndex: number;
    txInputs: BitcoinTransactionStatus["txInputs"];
    totalExcludedUTXOS: number;
    utxosLength: number;
    blockHeight: number;
    accountUnit: BitcoinAccount["currency"]["units"][0];
    locale: string;
  }>,
): UtxoRowDisplayData {
  const exclusionReason = utxoStatus.excluded ? utxoStatus.reason : undefined;
  const isUsedInTx = (context.txInputs ?? []).some(
    input => input.previousOutputIndex === utxo.outputIndex && input.previousTxHash === utxo.hash,
  );
  const unconfirmed = exclusionReason === "pickPendingUtxo";
  const isLastSelected =
    !utxoStatus.excluded && context.totalExcludedUTXOS + 1 === context.utxosLength;
  const disabled = unconfirmed || isLastSelected;
  const confirmations = utxo.blockHeight ? Math.max(0, context.blockHeight - utxo.blockHeight) : 0;
  const formattedValue = formatCurrencyUnit(context.accountUnit, utxo.value, {
    showCode: true,
    disableRounding: true,
    locale: context.locale,
  });

  const address = utxo.address ?? "";
  const addressLabel = address ? `${address.slice(0, 8)}...${address.slice(-4)}` : "";
  const titleLabel = `#${context.rowIndex + 1} ${addressLabel}`.trim();

  return {
    utxo,
    titleLabel,
    formattedValue,
    excluded: utxoStatus.excluded,
    exclusionReason,
    isUsedInTx,
    unconfirmed,
    isLastSelected,
    disabled,
    confirmations,
  };
}

/**
 * Fetches all parameters needed to display Bitcoin UTXO rows and the picking strategy selector,
 * derived from account bitcoin resources, transaction utxoStrategy, and status.
 * Returns null when the account is not Bitcoin-based or the transaction has no utxoStrategy.
 */
export function useBitcoinUtxoDisplayData({
  account,
  transaction,
  status,
  locale,
}: UseBitcoinUtxoDisplayDataParams): BitcoinUtxoDisplayData | null {
  return useMemo(() => {
    if (!isBitcoinBasedAccount(account)) return null;
    if (!hasUtxoStrategy(transaction)) return null;
    if (!isBitcoinTransactionStatus(status)) return null;

    const bitcoinAccount = account;
    const bitcoinResources = bitcoinAccount.bitcoinResources;
    if (!bitcoinResources?.utxos?.length) return null;

    const accountUnit = bitcoinAccount.currency.units[0];
    const { utxoStrategy } = transaction;
    const utxos = bitcoinResources.utxos;
    const blockHeight = bitcoinAccount.blockHeight ?? 0;

    const utxoStatuses = utxos.map(u => getUTXOStatus(u, utxoStrategy));
    const totalExcludedUTXOS = utxoStatuses.filter(s => s.excluded).length;

    const txInputs = status.txInputs ?? [];

    const utxoRows: UtxoRowDisplayData[] = utxos.map((utxo, rowIndex) =>
      utxoToRowDisplayData(utxo, utxoStatuses[rowIndex], {
        rowIndex,
        txInputs,
        totalExcludedUTXOS,
        utxosLength: utxos.length,
        blockHeight,
        accountUnit,
        locale,
      }),
    );

    return {
      pickingStrategyOptions,
      pickingStrategyValue: utxoStrategy.strategy,
      totalExcludedUTXOS,
      totalSpent: status.totalSpent ?? new BigNumber(0),
      utxoRows,
    };
  }, [account, locale, status, transaction]);
}
