import 'package:decimal/decimal.dart';
import 'package:flutter/material.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockingjay/mockingjay.dart' as mockingjay;
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/address.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/transaction.dart';
import 'package:stackwallet/pages/wallet_view/transaction_views/transaction_details_view.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/services/coins/coin_service.dart';
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
import 'package:stackwallet/services/coins/manager.dart';
import 'package:stackwallet/services/locale_service.dart';
import 'package:stackwallet/services/notes_service.dart';
import 'package:stackwallet/services/price_service.dart';
import 'package:stackwallet/services/wallets.dart';
import 'package:stackwallet/utilities/amount/amount.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/prefs.dart';
import 'package:stackwallet/utilities/theme/light_colors.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/transaction_card.dart';
import 'package:tuple/tuple.dart';

import 'transaction_card_test.mocks.dart';

@GenerateMocks([
  Wallets,
  Manager,
  CoinServiceAPI,
  FiroWallet,
  LocaleService,
  Prefs,
  PriceService,
  NotesService
], customMocks: [])
void main() {
  TestWidgetsFlutterBinding.ensureInitialized();
  testWidgets("Sent confirmed tx displays correctly", (tester) async {
    final mockManager = MockManager();
    final mockLocaleService = MockLocaleService();
    final wallets = MockWallets();
    final mockPrefs = MockPrefs();
    final mockPriceService = MockPriceService();

    final tx = Transaction(
      txid: "some txid",
      timestamp: 1648595998,
      type: TransactionType.outgoing,
      amount: 100000000,
      amountString: Amount(
        rawValue: BigInt.from(100000000),
        fractionDigits: Coin.firo.decimals,
      ).toJsonString(),
      fee: 3794,
      height: 450123,
      subType: TransactionSubType.none,
      isCancelled: false,
      walletId: '',
      isLelantus: null,
      slateId: '',
      otherData: '',
      nonce: null,
      inputs: [],
      outputs: [],
    )..address.value = Address(
        walletId: "walletId",
        value: "",
        publicKey: [],
        derivationIndex: 0,
        derivationPath: null,
        type: AddressType.p2pkh,
        subType: AddressSubType.receiving);

    final CoinServiceAPI wallet = MockFiroWallet();

    when(wallet.coin.ticker).thenAnswer((_) => "FIRO");
    when(mockLocaleService.locale).thenAnswer((_) => "en_US");
    when(mockPrefs.currency).thenAnswer((_) => "USD");
    when(mockPrefs.externalCalls).thenAnswer((_) => true);
    when(mockPriceService.getPrice(Coin.firo))
        .thenAnswer((realInvocation) => Tuple2(Decimal.ten, 0.00));

    when(wallet.coin).thenAnswer((_) => Coin.firo);

    when(wallets.getManager("wallet-id"))
        .thenAnswer((realInvocation) => Manager(wallet));

    when(wallet.storedChainHeight).thenAnswer((_) => 6000000);
    //
    await tester.pumpWidget(
      ProviderScope(
        overrides: [
          walletsChangeNotifierProvider.overrideWithValue(wallets),
          localeServiceChangeNotifierProvider
              .overrideWithValue(mockLocaleService),
          prefsChangeNotifierProvider.overrideWithValue(mockPrefs),
          priceAnd24hChangeNotifierProvider.overrideWithValue(mockPriceService)
        ],
        child: MaterialApp(
          theme: ThemeData(
            extensions: [
              StackColors.fromStackColorTheme(
                LightColors(),
              ),
            ],
          ),
          home: TransactionCard(transaction: tx, walletId: "wallet-id"),
        ),
      ),
    );

    //
    final title = find.text("Sent");
    // final price1 = find.text("0.00 USD");
    final amount = Util.isDesktop
        ? find.text("-1.00000000 FIRO")
        : find.text("1.00000000 FIRO");

    final icon = find.byIcon(FeatherIcons.arrowUp);

    expect(title, findsOneWidget);
    // expect(price1, findsOneWidget);
    expect(amount, findsOneWidget);
    // expect(icon, findsOneWidget);
    //
    await tester.pumpAndSettle(const Duration(seconds: 2));
    //
    // final price2 = find.text("\$10.00");
    // expect(price2, findsOneWidget);
    //
    // verify(mockManager.addListener(any)).called(1);
    verify(mockLocaleService.addListener(any)).called(1);

    verify(mockPrefs.currency).called(1);
    verify(mockPriceService.getPrice(Coin.firo)).called(1);
    verify(wallet.coin.ticker).called(1);

    verify(mockLocaleService.locale).called(1);

    verifyNoMoreInteractions(mockManager);
    verifyNoMoreInteractions(mockLocaleService);
  });

  testWidgets("Anonymized confirmed tx displays correctly", (tester) async {
    final mockManager = MockManager();
    final mockLocaleService = MockLocaleService();
    final wallets = MockWallets();
    final mockPrefs = MockPrefs();
    final mockPriceService = MockPriceService();

    final tx = Transaction(
      txid: "some txid",
      timestamp: 1648595998,
      type: TransactionType.outgoing,
      amount: 9659,
      amountString: Amount(
        rawValue: BigInt.from(9659),
        fractionDigits: Coin.firo.decimals,
      ).toJsonString(),
      fee: 3794,
      height: 450123,
      subType: TransactionSubType.mint,
      isCancelled: false,
      walletId: '',
      isLelantus: null,
      slateId: '',
      otherData: '',
      nonce: null,
      inputs: [],
      outputs: [],
    )..address.value = Address(
        walletId: "walletId",
        value: "",
        publicKey: [],
        derivationIndex: 0,
        derivationPath: null,
        type: AddressType.p2pkh,
        subType: AddressSubType.receiving);

    final CoinServiceAPI wallet = MockFiroWallet();

    when(wallet.coin.ticker).thenAnswer((_) => "FIRO");
    when(mockLocaleService.locale).thenAnswer((_) => "en_US");
    when(mockPrefs.currency).thenAnswer((_) => "USD");
    when(mockPrefs.externalCalls).thenAnswer((_) => true);
    when(mockPriceService.getPrice(Coin.firo))
        .thenAnswer((realInvocation) => Tuple2(Decimal.ten, 0.00));

    when(wallet.coin).thenAnswer((_) => Coin.firo);
    when(wallet.storedChainHeight).thenAnswer((_) => 6000000);

    when(wallets.getManager("wallet-id"))
        .thenAnswer((realInvocation) => Manager(wallet));
    //
    await tester.pumpWidget(
      ProviderScope(
        overrides: [
          walletsChangeNotifierProvider.overrideWithValue(wallets),
          localeServiceChangeNotifierProvider
              .overrideWithValue(mockLocaleService),
          prefsChangeNotifierProvider.overrideWithValue(mockPrefs),
          priceAnd24hChangeNotifierProvider.overrideWithValue(mockPriceService)
        ],
        child: MaterialApp(
          theme: ThemeData(
            extensions: [
              StackColors.fromStackColorTheme(
                LightColors(),
              ),
            ],
          ),
          home: TransactionCard(transaction: tx, walletId: "wallet-id"),
        ),
      ),
    );

    //
    final title = find.text("Anonymized");
    // final price1 = find.text("0.00 USD");
    final amount = find.text("-0.00009659 FIRO");

    final icon = find.byIcon(FeatherIcons.arrowUp);

    expect(title, findsOneWidget);
    // expect(price1, findsOneWidget);
    expect(amount, findsOneWidget);
    // expect(icon, findsOneWidget);
    //
    await tester.pumpAndSettle(const Duration(seconds: 2));
    //
    // final price2 = find.text("\$10.00");
    // expect(price2, findsOneWidget);
    //
    // verify(mockManager.addListener(any)).called(1);
    verify(mockLocaleService.addListener(any)).called(1);

    verify(mockPrefs.currency).called(1);
    verify(mockPriceService.getPrice(Coin.firo)).called(1);
    verify(wallet.coin.ticker).called(1);

    verify(mockLocaleService.locale).called(1);

    verifyNoMoreInteractions(mockManager);
    verifyNoMoreInteractions(mockLocaleService);
  });

  testWidgets("Received unconfirmed tx displays correctly", (tester) async {
    final mockManager = MockManager();
    final mockLocaleService = MockLocaleService();
    final wallets = MockWallets();
    final mockPrefs = MockPrefs();
    final mockPriceService = MockPriceService();

    final tx = Transaction(
      txid: "some txid",
      timestamp: 1648595998,
      type: TransactionType.incoming,
      amount: 100000000,
      amountString: Amount(
        rawValue: BigInt.from(100000000),
        fractionDigits: Coin.firo.decimals,
      ).toJsonString(),
      fee: 3794,
      height: 450123,
      subType: TransactionSubType.none,
      isCancelled: false,
      walletId: '',
      isLelantus: null,
      slateId: '',
      otherData: '',
      nonce: null,
      inputs: [],
      outputs: [],
    )..address.value = Address(
        walletId: "walletId",
        value: "",
        publicKey: [],
        derivationIndex: 0,
        derivationPath: null,
        type: AddressType.p2pkh,
        subType: AddressSubType.receiving);

    final CoinServiceAPI wallet = MockFiroWallet();

    when(wallet.coin.ticker).thenAnswer((_) => "FIRO");
    when(mockLocaleService.locale).thenAnswer((_) => "en_US");
    when(mockPrefs.currency).thenAnswer((_) => "USD");
    when(mockPrefs.externalCalls).thenAnswer((_) => true);
    when(mockPriceService.getPrice(Coin.firo))
        .thenAnswer((realInvocation) => Tuple2(Decimal.ten, 0.00));

    when(wallet.coin).thenAnswer((_) => Coin.firo);

    when(wallets.getManager("wallet-id"))
        .thenAnswer((realInvocation) => Manager(wallet));

    when(wallet.storedChainHeight).thenAnswer((_) => 6000000);

    await tester.pumpWidget(
      ProviderScope(
        overrides: [
          walletsChangeNotifierProvider.overrideWithValue(wallets),
          localeServiceChangeNotifierProvider
              .overrideWithValue(mockLocaleService),
          prefsChangeNotifierProvider.overrideWithValue(mockPrefs),
          priceAnd24hChangeNotifierProvider.overrideWithValue(mockPriceService)
        ],
        child: MaterialApp(
          theme: ThemeData(
            extensions: [
              StackColors.fromStackColorTheme(
                LightColors(),
              ),
            ],
          ),
          home: TransactionCard(transaction: tx, walletId: "wallet-id"),
        ),
      ),
    );

    final title = find.text("Received");
    final amount = Util.isDesktop
        ? find.text("+1.00000000 FIRO")
        : find.text("1.00000000 FIRO");

    expect(title, findsOneWidget);
    expect(amount, findsOneWidget);

    await tester.pumpAndSettle(const Duration(seconds: 2));

    verify(mockLocaleService.addListener(any)).called(1);

    verify(mockPrefs.currency).called(1);
    verify(mockPriceService.getPrice(Coin.firo)).called(1);
    verify(wallet.coin.ticker).called(1);

    verify(mockLocaleService.locale).called(1);

    verifyNoMoreInteractions(mockManager);
    verifyNoMoreInteractions(mockLocaleService);
  });

  testWidgets("Tap gesture", (tester) async {
    final mockManager = MockManager();
    final mockLocaleService = MockLocaleService();
    final wallets = MockWallets();
    final mockPrefs = MockPrefs();
    final mockPriceService = MockPriceService();
    final navigator = mockingjay.MockNavigator();

    final tx = Transaction(
      txid: "some txid",
      timestamp: 1648595998,
      type: TransactionType.outgoing,
      amount: 100000000,
      amountString: Amount(
        rawValue: BigInt.from(100000000),
        fractionDigits: Coin.firo.decimals,
      ).toJsonString(),
      fee: 3794,
      height: 450123,
      subType: TransactionSubType.none,
      isCancelled: false,
      walletId: '',
      isLelantus: null,
      slateId: '',
      otherData: '',
      nonce: null,
      inputs: [],
      outputs: [],
    )..address.value = Address(
        walletId: "walletId",
        value: "",
        publicKey: [],
        derivationIndex: 0,
        derivationPath: null,
        type: AddressType.p2pkh,
        subType: AddressSubType.receiving);

    final CoinServiceAPI wallet = MockFiroWallet();

    when(wallet.coin.ticker).thenAnswer((_) => "FIRO");
    when(mockLocaleService.locale).thenAnswer((_) => "en_US");
    when(mockPrefs.currency).thenAnswer((_) => "USD");
    when(mockPrefs.externalCalls).thenAnswer((_) => true);
    when(mockPriceService.getPrice(Coin.firo))
        .thenAnswer((realInvocation) => Tuple2(Decimal.ten, 0.00));

    when(wallet.coin).thenAnswer((_) => Coin.firo);

    when(wallets.getManager("wallet id"))
        .thenAnswer((realInvocation) => Manager(wallet));

    when(wallet.storedChainHeight).thenAnswer((_) => 6000000);

    mockingjay
        .when(() => navigator.pushNamed("/transactionDetails",
            arguments: Tuple3(tx, Coin.firo, "wallet id")))
        .thenAnswer((_) async => {});

    await tester.pumpWidget(
      ProviderScope(
        overrides: [
          walletsChangeNotifierProvider.overrideWithValue(wallets),
          localeServiceChangeNotifierProvider
              .overrideWithValue(mockLocaleService),
          prefsChangeNotifierProvider.overrideWithValue(mockPrefs),
          priceAnd24hChangeNotifierProvider.overrideWithValue(mockPriceService)
        ],
        child: MaterialApp(
          theme: ThemeData(
            extensions: [
              StackColors.fromStackColorTheme(LightColors()),
            ],
          ),
          home: mockingjay.MockNavigatorProvider(
              navigator: navigator,
              child: TransactionCard(transaction: tx, walletId: "wallet id")),
        ),
      ),
    );

    expect(find.byType(GestureDetector), findsOneWidget);

    await tester.tap(find.byType(GestureDetector));
    await tester.pump();

    verify(mockLocaleService.addListener(any)).called(1);

    verify(mockPrefs.currency).called(2);
    verify(mockLocaleService.locale).called(4);
    verify(wallet.coin.ticker).called(1);
    verify(wallet.storedChainHeight).called(2);

    verifyNoMoreInteractions(wallet);
    verifyNoMoreInteractions(mockLocaleService);

    if (Util.isDesktop) {
      expect(find.byType(TransactionDetailsView), findsOneWidget);
    } else {
      mockingjay
          .verify(() => navigator.pushNamed("/transactionDetails",
              arguments: Tuple3(tx, Coin.firo, "wallet id")))
          .called(1);
    }
  });
}