import 'dart:async';

import 'package:decimal/decimal.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:stackwallet/models/send_view_auto_fill_data.dart';
import 'package:stackwallet/pages/address_book_views/address_book_view.dart';
import 'package:stackwallet/pages/send_view/confirm_transaction_view.dart';
import 'package:stackwallet/pages/send_view/sub_widgets/building_transaction_dialog.dart';
import 'package:stackwallet/pages/send_view/sub_widgets/firo_balance_selection_sheet.dart';
import 'package:stackwallet/pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/ui/fee_rate_type_state_provider.dart';
import 'package:stackwallet/providers/ui/preview_tx_button_state_provider.dart';
import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart';
import 'package:stackwallet/route_generator.dart';
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
import 'package:stackwallet/services/coins/manager.dart';
import 'package:stackwallet/utilities/address_utils.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/barcode_scanner_interface.dart';
import 'package:stackwallet/utilities/clipboard_interface.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/animated_text.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
import 'package:stackwallet/widgets/icon_widgets/addressbook_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/clipboard_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/qrcode_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
import 'package:stackwallet/widgets/stack_dialog.dart';
import 'package:stackwallet/widgets/stack_text_field.dart';
import 'package:stackwallet/widgets/textfield_icon_button.dart';

class SendView extends ConsumerStatefulWidget {
  const SendView({
    Key? key,
    required this.walletId,
    required this.coin,
    this.autoFillData,
    this.clipboard = const ClipboardWrapper(),
    this.barcodeScanner = const BarcodeScannerWrapper(),
  }) : super(key: key);

  static const String routeName = "/sendView";

  final String walletId;
  final Coin coin;
  final SendViewAutoFillData? autoFillData;
  final ClipboardInterface clipboard;
  final BarcodeScannerInterface barcodeScanner;

  @override
  ConsumerState<SendView> createState() => _SendViewState();
}

class _SendViewState extends ConsumerState<SendView> {
  late final String walletId;
  late final Coin coin;
  late final ClipboardInterface clipboard;
  late final BarcodeScannerInterface scanner;

  late TextEditingController sendToController;
  late TextEditingController cryptoAmountController;
  late TextEditingController baseAmountController;
  late TextEditingController noteController;
  late TextEditingController feeController;

  late final SendViewAutoFillData? _data;

  final _addressFocusNode = FocusNode();
  final _noteFocusNode = FocusNode();
  final _cryptoFocus = FocusNode();
  final _baseFocus = FocusNode();

  Decimal? _amountToSend;
  Decimal? _cachedAmountToSend;
  String? _address;

  String? _privateBalanceString;
  String? _publicBalanceString;

  bool _addressToggleFlag = false;

  bool _cryptoAmountChangeLock = false;
  late VoidCallback onCryptoAmountChanged;

  Decimal? _cachedBalance;

  void _cryptoAmountChanged() async {
    if (!_cryptoAmountChangeLock) {
      final String cryptoAmount = cryptoAmountController.text;
      if (cryptoAmount.isNotEmpty &&
          cryptoAmount != "." &&
          cryptoAmount != ",") {
        _amountToSend = cryptoAmount.contains(",")
            ? Decimal.parse(cryptoAmount.replaceFirst(",", "."))
            : Decimal.parse(cryptoAmount);
        if (_cachedAmountToSend != null &&
            _cachedAmountToSend == _amountToSend) {
          return;
        }
        _cachedAmountToSend = _amountToSend;
        Logging.instance.log("it changed $_amountToSend $_cachedAmountToSend",
            level: LogLevel.Info);

        final price =
            ref.read(priceAnd24hChangeNotifierProvider).getPrice(coin).item1;

        if (price > Decimal.zero) {
          final String fiatAmountString = Format.localizedStringAsFixed(
            value: _amountToSend! * price,
            locale: ref.read(localeServiceChangeNotifierProvider).locale,
            decimalPlaces: 2,
          );

          baseAmountController.text = fiatAmountString;
        }
      } else {
        _amountToSend = null;
        baseAmountController.text = "";
      }

      _updatePreviewButtonState(_address, _amountToSend);

      // if (_amountToSend == null) {
      //   setState(() {
      //     _calculateFeesFuture = calculateFees(0);
      //   });
      // } else {
      //   setState(() {
      //     _calculateFeesFuture =
      //         calculateFees(Format.decimalAmountToSatoshis(_amountToSend!));
      //   });
      // }
    }
  }

  String? _updateInvalidAddressText(String address, Manager manager) {
    if (_data != null && _data!.contactLabel == address) {
      return null;
    }
    if (address.isNotEmpty && !manager.validateAddress(address)) {
      return "Invalid address";
    }
    return null;
  }

  void _updatePreviewButtonState(String? address, Decimal? amount) {
    final isValidAddress = ref
        .read(walletsChangeNotifierProvider)
        .getManager(walletId)
        .validateAddress(address ?? "");
    ref.read(previewTxButtonStateProvider.state).state =
        (isValidAddress && amount != null && amount > Decimal.zero);
  }

  late Future<String> _calculateFeesFuture;

  Map<int, String> cachedFees = {};
  Map<int, String> cachedFiroPrivateFees = {};
  Map<int, String> cachedFiroPublicFees = {};

  Future<String> calculateFees(int amount) async {
    if (amount <= 0) {
      return "0";
    }

    if (coin == Coin.firo || coin == Coin.firoTestNet) {
      if (ref.read(publicPrivateBalanceStateProvider.state).state ==
          "Private") {
        if (cachedFiroPrivateFees[amount] != null) {
          return cachedFiroPrivateFees[amount]!;
        }
      } else {
        if (cachedFiroPublicFees[amount] != null) {
          return cachedFiroPublicFees[amount]!;
        }
      }
    } else if (cachedFees[amount] != null) {
      return cachedFees[amount]!;
    }

    final manager =
        ref.read(walletsChangeNotifierProvider).getManager(walletId);
    final feeObject = await manager.fees;

    late final int feeRate;

    switch (ref.read(feeRateTypeStateProvider.state).state) {
      case FeeRateType.fast:
        feeRate = feeObject.fast;
        break;
      case FeeRateType.average:
        feeRate = feeObject.medium;
        break;
      case FeeRateType.slow:
        feeRate = feeObject.slow;
        break;
    }

    int fee;

    if (coin == Coin.firo || coin == Coin.firoTestNet) {
      if (ref.read(publicPrivateBalanceStateProvider.state).state ==
          "Private") {
        fee = await manager.estimateFeeFor(amount, feeRate);

        cachedFiroPrivateFees[amount] = Format.satoshisToAmount(fee)
            .toStringAsFixed(Constants.decimalPlaces);

        return cachedFiroPrivateFees[amount]!;
      } else {
        fee = await (manager.wallet as FiroWallet)
            .estimateFeeForPublic(amount, feeRate);

        cachedFiroPublicFees[amount] = Format.satoshisToAmount(fee)
            .toStringAsFixed(Constants.decimalPlaces);

        return cachedFiroPublicFees[amount]!;
      }
    } else {
      fee = await manager.estimateFeeFor(amount, feeRate);
      cachedFees[amount] =
          Format.satoshisToAmount(fee).toStringAsFixed(Constants.decimalPlaces);

      return cachedFees[amount]!;
    }
  }

  Future<String?> _firoBalanceFuture(
      ChangeNotifierProvider<Manager> provider, String locale) async {
    final wallet = ref.read(provider).wallet as FiroWallet?;

    if (wallet != null) {
      Decimal? balance;
      if (ref.read(publicPrivateBalanceStateProvider.state).state ==
          "Private") {
        balance = await wallet.availablePrivateBalance();
      } else {
        balance = await wallet.availablePublicBalance();
      }

      return Format.localizedStringAsFixed(
          value: balance, locale: locale, decimalPlaces: 8);
    }

    return null;
  }

  @override
  void initState() {
    ref.refresh(feeSheetSessionCacheProvider);

    _calculateFeesFuture = calculateFees(0);
    _data = widget.autoFillData;
    walletId = widget.walletId;
    coin = widget.coin;
    clipboard = widget.clipboard;
    scanner = widget.barcodeScanner;

    sendToController = TextEditingController();
    cryptoAmountController = TextEditingController();
    baseAmountController = TextEditingController();
    noteController = TextEditingController();
    feeController = TextEditingController();

    onCryptoAmountChanged = _cryptoAmountChanged;
    cryptoAmountController.addListener(onCryptoAmountChanged);

    if (_data != null) {
      if (_data!.amount != null) {
        cryptoAmountController.text = _data!.amount!.toString();
      }
      sendToController.text = _data!.contactLabel;
      _address = _data!.address;
      _addressToggleFlag = true;
    }

    _cryptoFocus.addListener(() {
      if (!_cryptoFocus.hasFocus && !_baseFocus.hasFocus) {
        if (_amountToSend == null) {
          setState(() {
            _calculateFeesFuture = calculateFees(0);
          });
        } else {
          setState(() {
            _calculateFeesFuture =
                calculateFees(Format.decimalAmountToSatoshis(_amountToSend!));
          });
        }
      }
    });

    _baseFocus.addListener(() {
      if (!_cryptoFocus.hasFocus && !_baseFocus.hasFocus) {
        if (_amountToSend == null) {
          setState(() {
            _calculateFeesFuture = calculateFees(0);
          });
        } else {
          setState(() {
            _calculateFeesFuture =
                calculateFees(Format.decimalAmountToSatoshis(_amountToSend!));
          });
        }
      }
    });

    super.initState();
  }

  @override
  void dispose() {
    cryptoAmountController.removeListener(onCryptoAmountChanged);

    sendToController.dispose();
    cryptoAmountController.dispose();
    baseAmountController.dispose();
    noteController.dispose();
    feeController.dispose();

    _noteFocusNode.dispose();
    _addressFocusNode.dispose();
    _cryptoFocus.dispose();
    _baseFocus.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    debugPrint("BUILD: $runtimeType");
    final provider = ref.watch(walletsChangeNotifierProvider
        .select((value) => value.getManagerProvider(walletId)));
    final String locale = ref.watch(
        localeServiceChangeNotifierProvider.select((value) => value.locale));

    if (coin == Coin.firo || coin == Coin.firoTestNet) {
      ref.listen(publicPrivateBalanceStateProvider, (previous, next) {
        if (_amountToSend == null) {
          setState(() {
            _calculateFeesFuture = calculateFees(0);
          });
        } else {
          setState(() {
            _calculateFeesFuture =
                calculateFees(Format.decimalAmountToSatoshis(_amountToSend!));
          });
        }
      });
    }

    return Scaffold(
      backgroundColor: Theme.of(context).extension<StackColors>()!.background,
      appBar: AppBar(
        leading: AppBarBackButton(
          onPressed: () async {
            if (FocusScope.of(context).hasFocus) {
              FocusScope.of(context).unfocus();
              await Future<void>.delayed(const Duration(milliseconds: 50));
            }
            if (mounted) {
              Navigator.of(context).pop();
            }
          },
        ),
        title: Text(
          "Send ${coin.ticker}",
          style: STextStyles.navBarTitle(context),
        ),
      ),
      body: LayoutBuilder(
        builder: (builderContext, constraints) {
          return Padding(
            padding: const EdgeInsets.only(
              left: 12,
              top: 12,
              right: 12,
            ),
            child: SingleChildScrollView(
              child: ConstrainedBox(
                constraints: BoxConstraints(
                  // subtract top and bottom padding set in parent
                  minHeight: constraints.maxHeight - 24,
                ),
                child: IntrinsicHeight(
                  child: Padding(
                    padding: const EdgeInsets.symmetric(horizontal: 4),
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.stretch,
                      children: [
                        Container(
                          decoration: BoxDecoration(
                            color: Theme.of(context)
                                .extension<StackColors>()!
                                .popupBG,
                            borderRadius: BorderRadius.circular(
                              Constants.size.circularBorderRadius,
                            ),
                          ),
                          child: Padding(
                            padding: const EdgeInsets.all(12.0),
                            child: Row(
                              children: [
                                SvgPicture.asset(
                                  Assets.svg.iconFor(coin: coin),
                                  width: 22,
                                  height: 22,
                                ),
                                const SizedBox(
                                  width: 6,
                                ),
                                if (coin != Coin.firo &&
                                    coin != Coin.firoTestNet)
                                  Expanded(
                                    child: Text(
                                      ref.watch(provider
                                          .select((value) => value.walletName)),
                                      style: STextStyles.titleBold12(context),
                                      overflow: TextOverflow.ellipsis,
                                      maxLines: 1,
                                    ),
                                  ),
                                if (coin == Coin.firo ||
                                    coin == Coin.firoTestNet)
                                  Column(
                                    crossAxisAlignment:
                                        CrossAxisAlignment.start,
                                    children: [
                                      Text(
                                        ref.watch(provider.select(
                                            (value) => value.walletName)),
                                        style: STextStyles.titleBold12(context)
                                            .copyWith(fontSize: 14),
                                      ),
                                      // const SizedBox(
                                      //   height: 2,
                                      // ),
                                      Text(
                                        "${ref.watch(publicPrivateBalanceStateProvider.state).state} balance",
                                        style: STextStyles.label(context)
                                            .copyWith(fontSize: 10),
                                      ),
                                    ],
                                  ),
                                if (coin != Coin.firo &&
                                    coin != Coin.firoTestNet)
                                  const SizedBox(
                                    width: 10,
                                  ),
                                if (coin == Coin.firo ||
                                    coin == Coin.firoTestNet)
                                  const Spacer(),
                                FutureBuilder(
                                  future: (coin != Coin.firo &&
                                          coin != Coin.firoTestNet)
                                      ? ref.watch(provider.select(
                                          (value) => value.availableBalance))
                                      : ref
                                                  .watch(
                                                      publicPrivateBalanceStateProvider
                                                          .state)
                                                  .state ==
                                              "Private"
                                          ? (ref.watch(provider).wallet
                                                  as FiroWallet)
                                              .availablePrivateBalance()
                                          : (ref.watch(provider).wallet
                                                  as FiroWallet)
                                              .availablePublicBalance(),
                                  builder:
                                      (_, AsyncSnapshot<Decimal> snapshot) {
                                    if (snapshot.connectionState ==
                                            ConnectionState.done &&
                                        snapshot.hasData) {
                                      _cachedBalance = snapshot.data!;
                                    }

                                    if (_cachedBalance != null) {
                                      return GestureDetector(
                                        onTap: () {
                                          cryptoAmountController.text =
                                              _cachedBalance!.toStringAsFixed(
                                                  Constants.decimalPlaces);
                                        },
                                        child: Container(
                                          color: Colors.transparent,
                                          child: Column(
                                            crossAxisAlignment:
                                                CrossAxisAlignment.end,
                                            children: [
                                              Text(
                                                "${Format.localizedStringAsFixed(
                                                  value: _cachedBalance!,
                                                  locale: locale,
                                                  decimalPlaces: 8,
                                                )} ${coin.ticker}",
                                                style: STextStyles.titleBold12(
                                                        context)
                                                    .copyWith(
                                                  fontSize: 10,
                                                ),
                                                textAlign: TextAlign.right,
                                              ),
                                              Text(
                                                "${Format.localizedStringAsFixed(
                                                  value: _cachedBalance! *
                                                      ref.watch(
                                                          priceAnd24hChangeNotifierProvider
                                                              .select((value) =>
                                                                  value
                                                                      .getPrice(
                                                                          coin)
                                                                      .item1)),
                                                  locale: locale,
                                                  decimalPlaces: 2,
                                                )} ${ref.watch(prefsChangeNotifierProvider.select((value) => value.currency))}",
                                                style:
                                                    STextStyles.titleBold12_400(
                                                            context)
                                                        .copyWith(
                                                  fontSize: 8,
                                                ),
                                                textAlign: TextAlign.right,
                                              )
                                            ],
                                          ),
                                        ),
                                      );
                                    } else {
                                      return Column(
                                        crossAxisAlignment:
                                            CrossAxisAlignment.end,
                                        children: [
                                          AnimatedText(
                                            stringsToLoopThrough: const [
                                              "Loading balance   ",
                                              "Loading balance.  ",
                                              "Loading balance.. ",
                                              "Loading balance...",
                                            ],
                                            style: STextStyles.itemSubtitle(
                                                    context)
                                                .copyWith(
                                              fontSize: 10,
                                            ),
                                          ),
                                          const SizedBox(
                                            height: 2,
                                          ),
                                          AnimatedText(
                                            stringsToLoopThrough: const [
                                              "Loading balance   ",
                                              "Loading balance.  ",
                                              "Loading balance.. ",
                                              "Loading balance...",
                                            ],
                                            style: STextStyles.itemSubtitle(
                                                    context)
                                                .copyWith(
                                              fontSize: 8,
                                            ),
                                          )
                                        ],
                                      );
                                    }
                                  },
                                ),
                              ],
                            ),
                          ),
                        ),
                        const SizedBox(
                          height: 16,
                        ),
                        Text(
                          "Send to",
                          style: STextStyles.smallMed12(context),
                          textAlign: TextAlign.left,
                        ),
                        const SizedBox(
                          height: 8,
                        ),
                        ClipRRect(
                          borderRadius: BorderRadius.circular(
                            Constants.size.circularBorderRadius,
                          ),
                          child: TextField(
                            key: const Key("sendViewAddressFieldKey"),
                            controller: sendToController,
                            readOnly: false,
                            autocorrect: false,
                            enableSuggestions: false,
                            // inputFormatters: <TextInputFormatter>[
                            //   FilteringTextInputFormatter.allow(
                            //       RegExp("[a-zA-Z0-9]{34}")),
                            // ],
                            toolbarOptions: const ToolbarOptions(
                              copy: false,
                              cut: false,
                              paste: true,
                              selectAll: false,
                            ),
                            onChanged: (newValue) {
                              _address = newValue;
                              _updatePreviewButtonState(
                                  _address, _amountToSend);

                              setState(() {
                                _addressToggleFlag = newValue.isNotEmpty;
                              });
                            },
                            focusNode: _addressFocusNode,
                            style: STextStyles.field(context),
                            decoration: standardInputDecoration(
                              "Enter ${coin.ticker} address",
                              _addressFocusNode,
                              context,
                            ).copyWith(
                              contentPadding: const EdgeInsets.only(
                                left: 16,
                                top: 6,
                                bottom: 8,
                                right: 5,
                              ),
                              suffixIcon: Padding(
                                padding: sendToController.text.isEmpty
                                    ? const EdgeInsets.only(right: 8)
                                    : const EdgeInsets.only(right: 0),
                                child: UnconstrainedBox(
                                  child: Row(
                                    mainAxisAlignment:
                                        MainAxisAlignment.spaceAround,
                                    children: [
                                      _addressToggleFlag
                                          ? TextFieldIconButton(
                                              key: const Key(
                                                  "sendViewClearAddressFieldButtonKey"),
                                              onTap: () {
                                                sendToController.text = "";
                                                _address = "";
                                                _updatePreviewButtonState(
                                                    _address, _amountToSend);
                                                setState(() {
                                                  _addressToggleFlag = false;
                                                });
                                              },
                                              child: const XIcon(),
                                            )
                                          : TextFieldIconButton(
                                              key: const Key(
                                                  "sendViewPasteAddressFieldButtonKey"),
                                              onTap: () async {
                                                final ClipboardData? data =
                                                    await clipboard.getData(
                                                        Clipboard.kTextPlain);
                                                if (data?.text != null &&
                                                    data!.text!.isNotEmpty) {
                                                  String content =
                                                      data.text!.trim();
                                                  if (content.contains("\n")) {
                                                    content = content.substring(
                                                        0,
                                                        content.indexOf("\n"));
                                                  }

                                                  sendToController.text =
                                                      content;
                                                  _address = content;

                                                  _updatePreviewButtonState(
                                                      _address, _amountToSend);
                                                  setState(() {
                                                    _addressToggleFlag =
                                                        sendToController
                                                            .text.isNotEmpty;
                                                  });
                                                }
                                              },
                                              child:
                                                  sendToController.text.isEmpty
                                                      ? const ClipboardIcon()
                                                      : const XIcon(),
                                            ),
                                      if (sendToController.text.isEmpty)
                                        TextFieldIconButton(
                                          key: const Key(
                                              "sendViewAddressBookButtonKey"),
                                          onTap: () {
                                            Navigator.of(context).pushNamed(
                                              AddressBookView.routeName,
                                              arguments: widget.coin,
                                            );
                                          },
                                          child: const AddressBookIcon(),
                                        ),
                                      if (sendToController.text.isEmpty)
                                        TextFieldIconButton(
                                          key: const Key(
                                              "sendViewScanQrButtonKey"),
                                          onTap: () async {
                                            try {
                                              // ref
                                              //     .read(
                                              //         shouldShowLockscreenOnResumeStateProvider
                                              //             .state)
                                              //     .state = false;
                                              if (FocusScope.of(context)
                                                  .hasFocus) {
                                                FocusScope.of(context)
                                                    .unfocus();
                                                await Future<void>.delayed(
                                                    const Duration(
                                                        milliseconds: 75));
                                              }

                                              final qrResult =
                                                  await scanner.scan();

                                              // Future<void>.delayed(
                                              //   const Duration(seconds: 2),
                                              //   () => ref
                                              //       .read(
                                              //           shouldShowLockscreenOnResumeStateProvider
                                              //               .state)
                                              //       .state = true,
                                              // );

                                              Logging.instance.log(
                                                  "qrResult content: ${qrResult.rawContent}",
                                                  level: LogLevel.Info);

                                              final results =
                                                  AddressUtils.parseUri(
                                                      qrResult.rawContent);

                                              Logging.instance.log(
                                                  "qrResult parsed: $results",
                                                  level: LogLevel.Info);

                                              if (results.isNotEmpty &&
                                                  results["scheme"] ==
                                                      coin.uriScheme) {
                                                // auto fill address
                                                _address =
                                                    results["address"] ?? "";
                                                sendToController.text =
                                                    _address!;

                                                // autofill notes field
                                                if (results["message"] !=
                                                    null) {
                                                  noteController.text =
                                                      results["message"]!;
                                                } else if (results["label"] !=
                                                    null) {
                                                  noteController.text =
                                                      results["label"]!;
                                                }

                                                // autofill amount field
                                                if (results["amount"] != null) {
                                                  final amount = Decimal.parse(
                                                      results["amount"]!);
                                                  cryptoAmountController.text =
                                                      Format
                                                          .localizedStringAsFixed(
                                                    value: amount,
                                                    locale: ref
                                                        .read(
                                                            localeServiceChangeNotifierProvider)
                                                        .locale,
                                                    decimalPlaces:
                                                        Constants.decimalPlaces,
                                                  );
                                                  amount.toString();
                                                  _amountToSend = amount;
                                                }

                                                _updatePreviewButtonState(
                                                    _address, _amountToSend);
                                                setState(() {
                                                  _addressToggleFlag =
                                                      sendToController
                                                          .text.isNotEmpty;
                                                });

                                                // now check for non standard encoded basic address
                                              } else if (ref
                                                  .read(
                                                      walletsChangeNotifierProvider)
                                                  .getManager(walletId)
                                                  .validateAddress(
                                                      qrResult.rawContent)) {
                                                _address = qrResult.rawContent;
                                                sendToController.text =
                                                    _address ?? "";

                                                _updatePreviewButtonState(
                                                    _address, _amountToSend);
                                                setState(() {
                                                  _addressToggleFlag =
                                                      sendToController
                                                          .text.isNotEmpty;
                                                });
                                              }
                                            } on PlatformException catch (e, s) {
                                              // ref
                                              //     .read(
                                              //         shouldShowLockscreenOnResumeStateProvider
                                              //             .state)
                                              //     .state = true;
                                              // here we ignore the exception caused by not giving permission
                                              // to use the camera to scan a qr code
                                              Logging.instance.log(
                                                  "Failed to get camera permissions while trying to scan qr code in SendView: $e\n$s",
                                                  level: LogLevel.Warning);
                                            }
                                          },
                                          child: const QrCodeIcon(),
                                        )
                                    ],
                                  ),
                                ),
                              ),
                            ),
                          ),
                        ),
                        Builder(
                          builder: (_) {
                            final error = _updateInvalidAddressText(
                              _address ?? "",
                              ref
                                  .read(walletsChangeNotifierProvider)
                                  .getManager(walletId),
                            );

                            if (error == null || error.isEmpty) {
                              return Container();
                            } else {
                              return Align(
                                alignment: Alignment.topLeft,
                                child: Padding(
                                  padding: const EdgeInsets.only(
                                    left: 12.0,
                                    top: 4.0,
                                  ),
                                  child: Text(
                                    error,
                                    textAlign: TextAlign.left,
                                    style: STextStyles.label(context).copyWith(
                                      color: Theme.of(context)
                                          .extension<StackColors>()!
                                          .textError,
                                    ),
                                  ),
                                ),
                              );
                            }
                          },
                        ),
                        if (coin == Coin.firo)
                          const SizedBox(
                            height: 12,
                          ),
                        if (coin == Coin.firo)
                          Text(
                            "Send from",
                            style: STextStyles.smallMed12(context),
                            textAlign: TextAlign.left,
                          ),
                        if (coin == Coin.firo)
                          const SizedBox(
                            height: 8,
                          ),
                        if (coin == Coin.firo)
                          Stack(
                            children: [
                              const TextField(
                                readOnly: true,
                                textInputAction: TextInputAction.none,
                              ),
                              Padding(
                                padding: const EdgeInsets.symmetric(
                                  horizontal: 12,
                                ),
                                child: RawMaterialButton(
                                  splashColor: Theme.of(context)
                                      .extension<StackColors>()!
                                      .highlight,
                                  shape: RoundedRectangleBorder(
                                    borderRadius: BorderRadius.circular(
                                      Constants.size.circularBorderRadius,
                                    ),
                                  ),
                                  onPressed: () {
                                    showModalBottomSheet<dynamic>(
                                      backgroundColor: Colors.transparent,
                                      context: context,
                                      shape: const RoundedRectangleBorder(
                                        borderRadius: BorderRadius.vertical(
                                          top: Radius.circular(20),
                                        ),
                                      ),
                                      builder: (_) => FiroBalanceSelectionSheet(
                                        walletId: walletId,
                                      ),
                                    );
                                  },
                                  child: Row(
                                    mainAxisAlignment:
                                        MainAxisAlignment.spaceBetween,
                                    children: [
                                      Row(
                                        children: [
                                          Text(
                                            "${ref.watch(publicPrivateBalanceStateProvider.state).state} balance",
                                            style: STextStyles.itemSubtitle12(
                                                context),
                                          ),
                                          const SizedBox(
                                            width: 10,
                                          ),
                                          FutureBuilder(
                                            future: _firoBalanceFuture(
                                                provider, locale),
                                            builder: (context,
                                                AsyncSnapshot<String?>
                                                    snapshot) {
                                              if (snapshot.connectionState ==
                                                      ConnectionState.done &&
                                                  snapshot.hasData) {
                                                if (ref
                                                        .read(
                                                            publicPrivateBalanceStateProvider
                                                                .state)
                                                        .state ==
                                                    "Private") {
                                                  _privateBalanceString =
                                                      snapshot.data!;
                                                } else {
                                                  _publicBalanceString =
                                                      snapshot.data!;
                                                }
                                              }
                                              if (ref
                                                          .read(
                                                              publicPrivateBalanceStateProvider
                                                                  .state)
                                                          .state ==
                                                      "Private" &&
                                                  _privateBalanceString !=
                                                      null) {
                                                return Text(
                                                  "$_privateBalanceString ${coin.ticker}",
                                                  style:
                                                      STextStyles.itemSubtitle(
                                                          context),
                                                );
                                              } else if (ref
                                                          .read(
                                                              publicPrivateBalanceStateProvider
                                                                  .state)
                                                          .state ==
                                                      "Public" &&
                                                  _publicBalanceString !=
                                                      null) {
                                                return Text(
                                                  "$_publicBalanceString ${coin.ticker}",
                                                  style:
                                                      STextStyles.itemSubtitle(
                                                          context),
                                                );
                                              } else {
                                                return AnimatedText(
                                                  stringsToLoopThrough: const [
                                                    "Loading balance",
                                                    "Loading balance.",
                                                    "Loading balance..",
                                                    "Loading balance...",
                                                  ],
                                                  style:
                                                      STextStyles.itemSubtitle(
                                                          context),
                                                );
                                              }
                                            },
                                          ),
                                        ],
                                      ),
                                      SvgPicture.asset(
                                        Assets.svg.chevronDown,
                                        width: 8,
                                        height: 4,
                                        color: Theme.of(context)
                                            .extension<StackColors>()!
                                            .textSubtitle2,
                                      ),
                                    ],
                                  ),
                                ),
                              )
                            ],
                          ),
                        const SizedBox(
                          height: 12,
                        ),
                        Row(
                          mainAxisAlignment: MainAxisAlignment.spaceBetween,
                          children: [
                            Text(
                              "Amount",
                              style: STextStyles.smallMed12(context),
                              textAlign: TextAlign.left,
                            ),
                            BlueTextButton(
                              text: "Send all ${coin.ticker}",
                              onTap: () async {
                                if (coin == Coin.firo ||
                                    coin == Coin.firoTestNet) {
                                  final firoWallet =
                                      ref.read(provider).wallet as FiroWallet;
                                  if (ref
                                          .read(
                                              publicPrivateBalanceStateProvider
                                                  .state)
                                          .state ==
                                      "Private") {
                                    cryptoAmountController.text =
                                        (await firoWallet
                                                .availablePrivateBalance())
                                            .toStringAsFixed(
                                                Constants.decimalPlaces);
                                  } else {
                                    cryptoAmountController.text =
                                        (await firoWallet
                                                .availablePublicBalance())
                                            .toStringAsFixed(
                                                Constants.decimalPlaces);
                                  }
                                } else {
                                  cryptoAmountController.text = (await ref
                                          .read(provider)
                                          .availableBalance)
                                      .toStringAsFixed(Constants.decimalPlaces);
                                }
                              },
                            ),
                          ],
                        ),
                        const SizedBox(
                          height: 8,
                        ),
                        TextField(
                          style: STextStyles.smallMed14(context).copyWith(
                            color: Theme.of(context)
                                .extension<StackColors>()!
                                .textDark,
                          ),
                          key: const Key("amountInputFieldCryptoTextFieldKey"),
                          controller: cryptoAmountController,
                          focusNode: _cryptoFocus,
                          keyboardType: const TextInputType.numberWithOptions(
                            signed: false,
                            decimal: true,
                          ),
                          textAlign: TextAlign.right,
                          inputFormatters: [
                            // regex to validate a crypto amount with 8 decimal places
                            TextInputFormatter.withFunction((oldValue,
                                    newValue) =>
                                RegExp(r'^([0-9]*[,.]?[0-9]{0,8}|[,.][0-9]{0,8})$')
                                        .hasMatch(newValue.text)
                                    ? newValue
                                    : oldValue),
                          ],
                          decoration: InputDecoration(
                            contentPadding: const EdgeInsets.only(
                              top: 12,
                              right: 12,
                            ),
                            hintText: "0",
                            hintStyle: STextStyles.fieldLabel(context).copyWith(
                              fontSize: 14,
                            ),
                            prefixIcon: FittedBox(
                              fit: BoxFit.scaleDown,
                              child: Padding(
                                padding: const EdgeInsets.all(12),
                                child: Text(
                                  coin.ticker,
                                  style: STextStyles.smallMed14(context)
                                      .copyWith(
                                          color: Theme.of(context)
                                              .extension<StackColors>()!
                                              .accentColorDark),
                                ),
                              ),
                            ),
                          ),
                        ),
                        const SizedBox(
                          height: 8,
                        ),
                        TextField(
                          style: STextStyles.smallMed14(context).copyWith(
                            color: Theme.of(context)
                                .extension<StackColors>()!
                                .textDark,
                          ),
                          key: const Key("amountInputFieldFiatTextFieldKey"),
                          controller: baseAmountController,
                          focusNode: _baseFocus,
                          keyboardType: const TextInputType.numberWithOptions(
                            signed: false,
                            decimal: true,
                          ),
                          textAlign: TextAlign.right,
                          inputFormatters: [
                            // regex to validate a fiat amount with 2 decimal places
                            TextInputFormatter.withFunction((oldValue,
                                    newValue) =>
                                RegExp(r'^([0-9]*[,.]?[0-9]{0,2}|[,.][0-9]{0,2})$')
                                        .hasMatch(newValue.text)
                                    ? newValue
                                    : oldValue),
                          ],
                          onChanged: (baseAmountString) {
                            if (baseAmountString.isNotEmpty &&
                                baseAmountString != "." &&
                                baseAmountString != ",") {
                              final baseAmount = baseAmountString.contains(",")
                                  ? Decimal.parse(
                                      baseAmountString.replaceFirst(",", "."))
                                  : Decimal.parse(baseAmountString);

                              var _price = ref
                                  .read(priceAnd24hChangeNotifierProvider)
                                  .getPrice(coin)
                                  .item1;

                              if (_price == Decimal.zero) {
                                _amountToSend = Decimal.zero;
                              } else {
                                _amountToSend = baseAmount <= Decimal.zero
                                    ? Decimal.zero
                                    : (baseAmount / _price).toDecimal(
                                        scaleOnInfinitePrecision:
                                            Constants.decimalPlaces);
                              }
                              if (_cachedAmountToSend != null &&
                                  _cachedAmountToSend == _amountToSend) {
                                return;
                              }
                              _cachedAmountToSend = _amountToSend;
                              Logging.instance.log(
                                  "it changed $_amountToSend $_cachedAmountToSend",
                                  level: LogLevel.Info);

                              final amountString =
                                  Format.localizedStringAsFixed(
                                value: _amountToSend!,
                                locale: ref
                                    .read(localeServiceChangeNotifierProvider)
                                    .locale,
                                decimalPlaces: Constants.decimalPlaces,
                              );

                              _cryptoAmountChangeLock = true;
                              cryptoAmountController.text = amountString;
                              _cryptoAmountChangeLock = false;
                            } else {
                              _amountToSend = Decimal.zero;
                              _cryptoAmountChangeLock = true;
                              cryptoAmountController.text = "";
                              _cryptoAmountChangeLock = false;
                            }
                            // setState(() {
                            //   _calculateFeesFuture = calculateFees(
                            //       Format.decimalAmountToSatoshis(
                            //           _amountToSend!));
                            // });
                            _updatePreviewButtonState(_address, _amountToSend);
                          },
                          decoration: InputDecoration(
                            contentPadding: const EdgeInsets.only(
                              top: 12,
                              right: 12,
                            ),
                            hintText: "0",
                            hintStyle: STextStyles.fieldLabel(context).copyWith(
                              fontSize: 14,
                            ),
                            prefixIcon: FittedBox(
                              fit: BoxFit.scaleDown,
                              child: Padding(
                                padding: const EdgeInsets.all(12),
                                child: Text(
                                  ref.watch(prefsChangeNotifierProvider
                                      .select((value) => value.currency)),
                                  style: STextStyles.smallMed14(context)
                                      .copyWith(
                                          color: Theme.of(context)
                                              .extension<StackColors>()!
                                              .accentColorDark),
                                ),
                              ),
                            ),
                          ),
                        ),
                        const SizedBox(
                          height: 12,
                        ),
                        Text(
                          "Note (optional)",
                          style: STextStyles.smallMed12(context),
                          textAlign: TextAlign.left,
                        ),
                        const SizedBox(
                          height: 8,
                        ),
                        ClipRRect(
                          borderRadius: BorderRadius.circular(
                            Constants.size.circularBorderRadius,
                          ),
                          child: TextField(
                            controller: noteController,
                            focusNode: _noteFocusNode,
                            style: STextStyles.field(context),
                            onChanged: (_) => setState(() {}),
                            decoration: standardInputDecoration(
                              "Type something...",
                              _noteFocusNode,
                              context,
                            ).copyWith(
                              suffixIcon: noteController.text.isNotEmpty
                                  ? Padding(
                                      padding: const EdgeInsets.only(right: 0),
                                      child: UnconstrainedBox(
                                        child: Row(
                                          children: [
                                            TextFieldIconButton(
                                              child: const XIcon(),
                                              onTap: () async {
                                                setState(() {
                                                  noteController.text = "";
                                                });
                                              },
                                            ),
                                          ],
                                        ),
                                      ),
                                    )
                                  : null,
                            ),
                          ),
                        ),
                        const SizedBox(
                          height: 12,
                        ),
                        Text(
                          "Transaction fee (estimated)",
                          style: STextStyles.smallMed12(context),
                          textAlign: TextAlign.left,
                        ),
                        const SizedBox(
                          height: 8,
                        ),
                        Stack(
                          children: [
                            TextField(
                              controller: feeController,
                              readOnly: true,
                              textInputAction: TextInputAction.none,
                            ),
                            Padding(
                              padding: const EdgeInsets.symmetric(
                                horizontal: 12,
                              ),
                              child: RawMaterialButton(
                                splashColor: Theme.of(context)
                                    .extension<StackColors>()!
                                    .highlight,
                                shape: RoundedRectangleBorder(
                                  borderRadius: BorderRadius.circular(
                                    Constants.size.circularBorderRadius,
                                  ),
                                ),
                                onPressed: (coin == Coin.firo ||
                                            coin == Coin.firoTestNet) &&
                                        ref
                                                .watch(
                                                    publicPrivateBalanceStateProvider
                                                        .state)
                                                .state ==
                                            "Private"
                                    ? null
                                    : () {
                                        showModalBottomSheet<dynamic>(
                                          backgroundColor: Colors.transparent,
                                          context: context,
                                          shape: const RoundedRectangleBorder(
                                            borderRadius: BorderRadius.vertical(
                                              top: Radius.circular(20),
                                            ),
                                          ),
                                          builder: (_) =>
                                              TransactionFeeSelectionSheet(
                                            walletId: walletId,
                                            amount: Decimal.tryParse(
                                                    cryptoAmountController
                                                        .text) ??
                                                Decimal.zero,
                                          ),
                                        );
                                      },
                                child: ((coin == Coin.firo ||
                                            coin == Coin.firoTestNet) &&
                                        ref
                                                .watch(
                                                    publicPrivateBalanceStateProvider
                                                        .state)
                                                .state ==
                                            "Private")
                                    ? Row(
                                        children: [
                                          FutureBuilder(
                                            future: _calculateFeesFuture,
                                            builder: (context, snapshot) {
                                              if (snapshot.connectionState ==
                                                      ConnectionState.done &&
                                                  snapshot.hasData) {
                                                return Text(
                                                  "~${snapshot.data! as String} ${coin.ticker}",
                                                  style:
                                                      STextStyles.itemSubtitle(
                                                          context),
                                                );
                                              } else {
                                                return AnimatedText(
                                                  stringsToLoopThrough: const [
                                                    "Calculating",
                                                    "Calculating.",
                                                    "Calculating..",
                                                    "Calculating...",
                                                  ],
                                                  style:
                                                      STextStyles.itemSubtitle(
                                                          context),
                                                );
                                              }
                                            },
                                          ),
                                        ],
                                      )
                                    : Row(
                                        mainAxisAlignment:
                                            MainAxisAlignment.spaceBetween,
                                        children: [
                                          Row(
                                            children: [
                                              Text(
                                                ref
                                                    .watch(
                                                        feeRateTypeStateProvider
                                                            .state)
                                                    .state
                                                    .prettyName,
                                                style:
                                                    STextStyles.itemSubtitle12(
                                                        context),
                                              ),
                                              const SizedBox(
                                                width: 10,
                                              ),
                                              FutureBuilder(
                                                future: _calculateFeesFuture,
                                                builder: (context, snapshot) {
                                                  if (snapshot.connectionState ==
                                                          ConnectionState
                                                              .done &&
                                                      snapshot.hasData) {
                                                    return Text(
                                                      "~${snapshot.data! as String} ${coin.ticker}",
                                                      style: STextStyles
                                                          .itemSubtitle(
                                                              context),
                                                    );
                                                  } else {
                                                    return AnimatedText(
                                                      stringsToLoopThrough: const [
                                                        "Calculating",
                                                        "Calculating.",
                                                        "Calculating..",
                                                        "Calculating...",
                                                      ],
                                                      style: STextStyles
                                                          .itemSubtitle(
                                                              context),
                                                    );
                                                  }
                                                },
                                              ),
                                            ],
                                          ),
                                          SvgPicture.asset(
                                            Assets.svg.chevronDown,
                                            width: 8,
                                            height: 4,
                                            color: Theme.of(context)
                                                .extension<StackColors>()!
                                                .textSubtitle2,
                                          ),
                                        ],
                                      ),
                              ),
                            )
                          ],
                        ),
                        const Spacer(),
                        const SizedBox(
                          height: 12,
                        ),
                        TextButton(
                          onPressed: ref
                                  .watch(previewTxButtonStateProvider.state)
                                  .state
                              ? () async {
                                  // wait for keyboard to disappear
                                  FocusScope.of(context).unfocus();
                                  await Future<void>.delayed(
                                    const Duration(milliseconds: 100),
                                  );
                                  final manager = ref
                                      .read(walletsChangeNotifierProvider)
                                      .getManager(walletId);

                                  // TODO: remove the need for this!!
                                  final bool isOwnAddress =
                                      await manager.isOwnAddress(_address!);
                                  if (isOwnAddress) {
                                    await showDialog<dynamic>(
                                      context: context,
                                      useSafeArea: false,
                                      barrierDismissible: true,
                                      builder: (context) {
                                        return StackDialog(
                                          title: "Transaction failed",
                                          message:
                                              "Sending to self is currently disabled",
                                          rightButton: TextButton(
                                            style: Theme.of(context)
                                                .extension<StackColors>()!
                                                .getSecondaryEnabledButtonColor(
                                                    context),
                                            child: Text(
                                              "Ok",
                                              style: STextStyles.button(context)
                                                  .copyWith(
                                                      color: Theme.of(context)
                                                          .extension<
                                                              StackColors>()!
                                                          .accentColorDark),
                                            ),
                                            onPressed: () {
                                              Navigator.of(context).pop();
                                            },
                                          ),
                                        );
                                      },
                                    );
                                    return;
                                  }

                                  final amount = Format.decimalAmountToSatoshis(
                                      _amountToSend!);
                                  int availableBalance;
                                  if ((coin == Coin.firo ||
                                      coin == Coin.firoTestNet)) {
                                    if (ref
                                            .read(
                                                publicPrivateBalanceStateProvider
                                                    .state)
                                            .state ==
                                        "Private") {
                                      availableBalance =
                                          Format.decimalAmountToSatoshis(
                                              await (manager.wallet
                                                      as FiroWallet)
                                                  .availablePrivateBalance());
                                    } else {
                                      availableBalance =
                                          Format.decimalAmountToSatoshis(
                                              await (manager.wallet
                                                      as FiroWallet)
                                                  .availablePublicBalance());
                                    }
                                  } else {
                                    availableBalance =
                                        Format.decimalAmountToSatoshis(
                                            await manager.availableBalance);
                                  }

                                  // confirm send all
                                  if (amount == availableBalance) {
                                    final bool? shouldSendAll =
                                        await showDialog<bool>(
                                      context: context,
                                      useSafeArea: false,
                                      barrierDismissible: true,
                                      builder: (context) {
                                        return StackDialog(
                                          title: "Confirm send all",
                                          message:
                                              "You are about to send your entire balance. Would you like to continue?",
                                          leftButton: TextButton(
                                            style: Theme.of(context)
                                                .extension<StackColors>()!
                                                .getSecondaryEnabledButtonColor(
                                                    context),
                                            child: Text(
                                              "Cancel",
                                              style: STextStyles.button(context)
                                                  .copyWith(
                                                      color: Theme.of(context)
                                                          .extension<
                                                              StackColors>()!
                                                          .accentColorDark),
                                            ),
                                            onPressed: () {
                                              Navigator.of(context).pop(false);
                                            },
                                          ),
                                          rightButton: TextButton(
                                            style: Theme.of(context)
                                                .extension<StackColors>()!
                                                .getPrimaryEnabledButtonColor(
                                                    context),
                                            child: Text(
                                              "Yes",
                                              style:
                                                  STextStyles.button(context),
                                            ),
                                            onPressed: () {
                                              Navigator.of(context).pop(true);
                                            },
                                          ),
                                        );
                                      },
                                    );

                                    if (shouldSendAll == null ||
                                        shouldSendAll == false) {
                                      // cancel preview
                                      return;
                                    }
                                  }

                                  try {
                                    bool wasCancelled = false;

                                    unawaited(showDialog<dynamic>(
                                      context: context,
                                      useSafeArea: false,
                                      barrierDismissible: false,
                                      builder: (context) {
                                        return BuildingTransactionDialog(
                                          onCancel: () {
                                            wasCancelled = true;

                                            Navigator.of(context).pop();
                                          },
                                        );
                                      },
                                    ));

                                    Map<String, dynamic> txData;

                                    if ((coin == Coin.firo ||
                                            coin == Coin.firoTestNet) &&
                                        ref
                                                .read(
                                                    publicPrivateBalanceStateProvider
                                                        .state)
                                                .state !=
                                            "Private") {
                                      txData =
                                          await (manager.wallet as FiroWallet)
                                              .prepareSendPublic(
                                        address: _address!,
                                        satoshiAmount: amount,
                                        args: {
                                          "feeRate":
                                              ref.read(feeRateTypeStateProvider)
                                        },
                                      );
                                    } else {
                                      txData = await manager.prepareSend(
                                        address: _address!,
                                        satoshiAmount: amount,
                                        args: {
                                          "feeRate":
                                              ref.read(feeRateTypeStateProvider)
                                        },
                                      );
                                    }

                                    if (!wasCancelled && mounted) {
                                      // pop building dialog
                                      Navigator.of(context).pop();
                                      txData["note"] = noteController.text;
                                      txData["address"] = _address;

                                      unawaited(Navigator.of(context).push(
                                        RouteGenerator.getRoute(
                                          shouldUseMaterialRoute: RouteGenerator
                                              .useMaterialPageRoute,
                                          builder: (_) =>
                                              ConfirmTransactionView(
                                            transactionInfo: txData,
                                            walletId: walletId,
                                          ),
                                          settings: const RouteSettings(
                                            name: ConfirmTransactionView
                                                .routeName,
                                          ),
                                        ),
                                      ));
                                    }
                                  } catch (e) {
                                    if (mounted) {
                                      // pop building dialog
                                      Navigator.of(context).pop();

                                      unawaited(showDialog<dynamic>(
                                        context: context,
                                        useSafeArea: false,
                                        barrierDismissible: true,
                                        builder: (context) {
                                          return StackDialog(
                                            title: "Transaction failed",
                                            message: e.toString(),
                                            rightButton: TextButton(
                                              style: Theme.of(context)
                                                  .extension<StackColors>()!
                                                  .getSecondaryEnabledButtonColor(
                                                      context),
                                              child: Text(
                                                "Ok",
                                                style: STextStyles.button(
                                                        context)
                                                    .copyWith(
                                                        color: Theme.of(context)
                                                            .extension<
                                                                StackColors>()!
                                                            .accentColorDark),
                                              ),
                                              onPressed: () {
                                                Navigator.of(context).pop();
                                              },
                                            ),
                                          );
                                        },
                                      ));
                                    }
                                  }
                                }
                              : null,
                          style: ref
                                  .watch(previewTxButtonStateProvider.state)
                                  .state
                              ? Theme.of(context)
                                  .extension<StackColors>()!
                                  .getPrimaryEnabledButtonColor(context)
                              : Theme.of(context)
                                  .extension<StackColors>()!
                                  .getPrimaryDisabledButtonColor(context),
                          child: Text(
                            "Preview",
                            style: STextStyles.button(context),
                          ),
                        ),
                        const SizedBox(
                          height: 4,
                        ),
                      ],
                    ),
                  ),
                ),
              ),
            ),
          );
        },
      ),
    );
  }
}