import 'package:flutter/material.dart';

import 'package:go_router/go_router.dart';

import 'package:provider/provider.dart';

import 'package:pshared/controllers/balance_mask/wallets.dart';
import 'package:pshared/controllers/payment/source.dart';
import 'package:pshared/models/ledger/account.dart';
import 'package:pshared/models/payment/type.dart';
import 'package:pshared/models/recipient/recipient.dart';
import 'package:pshared/provider/ledger.dart';
import 'package:pshared/provider/organizations.dart';
import 'package:pshared/provider/payment/amount.dart';
import 'package:pshared/provider/payment/flow.dart';
import 'package:pshared/provider/payment/multiple/provider.dart';
import 'package:pshared/provider/payment/multiple/quotation.dart';
import 'package:pshared/provider/payment/payments.dart';
import 'package:pshared/provider/payment/provider.dart';
import 'package:pshared/provider/payment/quotation/quotation.dart';
import 'package:pshared/provider/payment/updates.dart';
import 'package:pshared/provider/payout_verification.dart';
import 'package:pshared/provider/recipient/provider.dart';
import 'package:pshared/provider/recipient/methods_cache.dart';
import 'package:pshared/provider/recipient/pmethods.dart';

import 'package:pweb/app/router/pages.dart';
import 'package:pweb/app/router/payout_routes.dart';
import 'package:pweb/controllers/payouts/multiple_payouts.dart';
import 'package:pweb/controllers/payouts/multi_quotation.dart';
import 'package:pweb/controllers/payments/page.dart';
import 'package:pweb/controllers/payouts/payout_verification.dart';
import 'package:pweb/pages/invitations/page/page.dart';
import 'package:pweb/providers/multiple_payouts.dart';
import 'package:pweb/controllers/payouts/quotation.dart';
import 'package:pshared/models/payment/wallet.dart';
import 'package:pweb/pages/address_book/form/page.dart';
import 'package:pweb/pages/address_book/page/page.dart';
import 'package:pweb/pages/dashboard/dashboard.dart';
import 'package:pweb/pages/payout_page/send/page.dart';
import 'package:pweb/pages/payout_page/wallet/edit/page.dart';
import 'package:pweb/pages/report/details/page.dart';
import 'package:pweb/pages/report/page.dart';
import 'package:pweb/pages/settings/profile/page.dart';
import 'package:pweb/pages/wallet_top_up/page.dart';
import 'package:pweb/widgets/dialogs/confirmation_dialog.dart';
import 'package:pweb/utils/error/snackbar.dart';
import 'package:pweb/widgets/sidebar/destinations.dart';
import 'package:pweb/widgets/sidebar/page.dart';
import 'package:pweb/utils/payment/availability.dart';
import 'package:pweb/services/payments/csv_input.dart';

import 'package:pweb/generated/i18n/app_localizations.dart';

RouteBase payoutShellRoute() => ShellRoute(
  builder: (context, state, child) => MultiProvider(
    providers: [
      ChangeNotifierProxyProvider2<
        OrganizationsProvider,
        RecipientsProvider,
        PaymentMethodsProvider
      >(
        create: (_) => PaymentMethodsProvider(),
        update: (context, organizations, recipients, provider) =>
            provider!..updateProviders(organizations, recipients),
      ),
      ChangeNotifierProxyProvider2<
        OrganizationsProvider,
        RecipientsProvider,
        RecipientMethodsCacheProvider
      >(
        create: (_) => RecipientMethodsCacheProvider(),
        update: (context, organizations, recipients, provider) =>
            provider!..updateProviders(organizations, recipients),
      ),
      ChangeNotifierProxyProvider2<
        RecipientsProvider,
        PaymentMethodsProvider,
        PaymentFlowProvider
      >(
        create: (_) =>
            PaymentFlowProvider(initialType: enabledPaymentTypes.first),
        update: (context, recipients, methods, provider) =>
            provider!..update(recipients, methods),
      ),
      ChangeNotifierProvider(create: (_) => PaymentAmountProvider()),
      ChangeNotifierProxyProvider2<
        WalletsController,
        LedgerAccountsProvider,
        PaymentSourceController
      >(
        create: (_) => PaymentSourceController(),
        update: (_, wallets, ledger, controller) =>
            controller!..update(wallets, ledger),
      ),
      ChangeNotifierProxyProvider6<
        OrganizationsProvider,
        PaymentAmountProvider,
        PaymentSourceController,
        PaymentFlowProvider,
        RecipientsProvider,
        PaymentMethodsProvider,
        QuotationProvider
      >(
        create: (_) => QuotationProvider(),
        update:
            (
              _,
              organization,
              payment,
              source,
              flow,
              recipients,
              methods,
              provider,
            ) => provider!
              ..update(
                organization,
                payment,
                source,
                flow,
                recipients,
                methods,
              ),
      ),
      ChangeNotifierProxyProvider<QuotationProvider, QuotationController>(
        create: (_) => QuotationController(),
        update: (_, quotation, controller) => controller!..update(quotation),
      ),
      ChangeNotifierProxyProvider2<
        OrganizationsProvider,
        QuotationProvider,
        PaymentProvider
      >(
        create: (_) => PaymentProvider(),
        update: (context, organization, quotation, provider) =>
            provider!..update(organization, quotation),
      ),
      ChangeNotifierProvider(create: (_) => PayoutVerificationProvider()),
      ChangeNotifierProxyProvider<
        PayoutVerificationProvider,
        PayoutVerificationController
      >(
        create: (_) => PayoutVerificationController(),
        update: (context, verification, controller) =>
            controller!..update(verification),
      ),
      ChangeNotifierProxyProvider5<
        PaymentProvider,
        QuotationProvider,
        PaymentFlowProvider,
        PaymentAmountProvider,
        RecipientsProvider,
        PaymentPageController
      >(
        create: (_) => PaymentPageController(),
        update:
            (
              context,
              payment,
              quotation,
              flow,
              amount,
              recipients,
              controller,
            ) =>
                controller!
                  ..update(payment, quotation, flow, amount, recipients),
      ),
      ChangeNotifierProxyProvider<
        OrganizationsProvider,
        MultiQuotationProvider
      >(
        create: (_) => MultiQuotationProvider(),
        update: (context, organization, provider) =>
            provider!..update(organization),
      ),
      ChangeNotifierProxyProvider<
        MultiQuotationProvider,
        MultiQuotationController
      >(
        create: (_) => MultiQuotationController(),
        update: (_, quotation, controller) => controller!..update(quotation),
      ),
      ChangeNotifierProxyProvider2<
        OrganizationsProvider,
        MultiQuotationProvider,
        MultiPaymentProvider
      >(
        create: (_) => MultiPaymentProvider(),
        update: (context, organization, quotation, provider) =>
            provider!..update(organization, quotation),
      ),
      ChangeNotifierProxyProvider3<
        PaymentsProvider,
        PaymentProvider,
        MultiPaymentProvider,
        PaymentsUpdatesProvider
      >(
        create: (_) => PaymentsUpdatesProvider(),
        lazy: false,
        update: (_, payments, payment, multiPayment, controller) =>
            controller!..update(
              paymentsProvider: payments,
              paymentProvider: payment,
              multiPaymentProvider: multiPayment,
            ),
      ),
      ChangeNotifierProxyProvider2<
        MultiQuotationProvider,
        MultiPaymentProvider,
        MultiplePayoutsProvider
      >(
        create: (_) => MultiplePayoutsProvider(),
        update: (context, quotation, payment, provider) =>
            provider!..update(quotation, payment),
      ),
      ChangeNotifierProxyProvider2<
        MultiplePayoutsProvider,
        PaymentSourceController,
        MultiplePayoutsController
      >(
        create: (_) =>
            MultiplePayoutsController(csvInput: WebCsvInputService()),
        update: (context, provider, sourceController, controller) =>
            controller!..update(provider, sourceController),
      ),
    ],
    child: PageSelector(child: child, routerState: state),
  ),
  routes: [
    GoRoute(
      name: PayoutRoutes.dashboard,
      path: routerPage(Pages.dashboard),
      pageBuilder: (context, _) => NoTransitionPage(
        child: DashboardPage(
          onRecipientSelected: (recipient) =>
              _startPayment(context, recipient: recipient),
          onGoToPaymentWithoutRecipient: (type) =>
              _startPayment(context, recipient: null, paymentType: type),
          onTopUp: (wallet) => _openWalletTopUp(context, wallet),
          onLedgerAddFunds: (account) => _openLedgerAddFunds(context, account),
          onWalletTap: (wallet) => _openWalletTopUp(context, wallet),
          onLedgerTap: (account) => _openLedgerEdit(context, account),
        ),
      ),
    ),
    GoRoute(
      name: PayoutRoutes.recipients,
      path: PayoutRoutes.recipientsPath,
      pageBuilder: (context, _) {
        final loc = AppLocalizations.of(context)!;
        return NoTransitionPage(
          child: RecipientAddressBookPage(
            onRecipientSelected: (recipient) =>
                _startPayment(context, recipient: recipient),
            onAddRecipient: () => _openAddRecipient(context),
            onEditRecipient: (recipient) =>
                _openEditRecipient(context, recipient: recipient),
            onDeleteRecipient: (recipient) async {
              final confirmed = await showConfirmationDialog(
                context: context,
                title: loc.delete,
                message: loc.deleteRecipientConfirmation,
                confirmLabel: loc.delete,
              );
              if (!confirmed) return;
              await executeActionWithNotification(
                context: context,
                action: () async =>
                    context.read<RecipientsProvider>().delete(recipient.id),
                successMessage: loc.recipientDeletedSuccessfully,
                errorMessage: loc.errorDeleteRecipient,
              );
            },
          ),
        );
      },
    ),
    GoRoute(
      name: PayoutRoutes.invitations,
      path: PayoutRoutes.invitationsPath,
      pageBuilder: (_, _) => const NoTransitionPage(child: InvitationsPage()),
    ),
    GoRoute(
      name: PayoutRoutes.addRecipient,
      path: PayoutRoutes.addRecipientPath,
      pageBuilder: (context, _) {
        final recipient = context.read<RecipientsProvider>().currentObject;
        return NoTransitionPage(
          child: AddressBookRecipientForm(
            recipient: recipient,
            onSaved: (_) => _popOrGo(context),
          ),
        );
      },
    ),
    GoRoute(
      name: PayoutRoutes.editRecipient,
      path: PayoutRoutes.editRecipientPath,
      pageBuilder: (context, _) {
        final recipient = context.read<RecipientsProvider>().currentObject;
        return NoTransitionPage(
          child: AddressBookRecipientForm(
            recipient: recipient,
            onSaved: (_) => _popOrGo(context),
          ),
        );
      },
    ),
    GoRoute(
      name: PayoutRoutes.payment,
      path: PayoutRoutes.paymentPath,
      pageBuilder: (context, state) {
        final amountProvider = context.read<PaymentAmountProvider>();
        if (amountProvider.comment.isNotEmpty) {
          WidgetsBinding.instance.addPostFrameCallback((_) {
            amountProvider.setComment('');
          });
        }
        final fallbackDestination = PayoutDestination.dashboard;

        return NoTransitionPage(
          child: PaymentPage(
            onBack: (_) => _popOrGo(context),
            initialPaymentType: PayoutRoutes.paymentTypeFromState(state),
            initialDestinationLedgerAccountRef:
                PayoutRoutes.destinationLedgerAccountRefFromState(state),
            fallbackDestination: fallbackDestination,
          ),
        );
      },
    ),
    GoRoute(
      name: PayoutRoutes.settings,
      path: PayoutRoutes.settingsPath,
      pageBuilder: (_, _) =>
          const NoTransitionPage(child: ProfileSettingsPage()),
    ),
    GoRoute(
      name: PayoutRoutes.reports,
      path: PayoutRoutes.reportsPath,
      pageBuilder: (_, _) =>
          const NoTransitionPage(child: OperationHistoryPage()),
    ),
    GoRoute(
      name: PayoutRoutes.reportPayment,
      path: PayoutRoutes.reportPaymentPath,
      pageBuilder: (_, state) => NoTransitionPage(
        child: PaymentDetailsPage(
          paymentRef:
              state.uri.queryParameters[PayoutRoutes.reportPaymentIdQuery] ??
              '',
        ),
      ),
    ),
    GoRoute(
      name: PayoutRoutes.methods,
      path: PayoutRoutes.methodsPath,
      redirect: (_, state) => PayoutRoutes.dashboardPath,
    ),
    GoRoute(
      name: PayoutRoutes.editWallet,
      path: PayoutRoutes.editWalletPath,
      pageBuilder: (context, state) => NoTransitionPage(
        child: WalletEditPage(onBack: () => _popOrGo(context)),
      ),
    ),
    GoRoute(
      name: PayoutRoutes.walletTopUp,
      path: PayoutRoutes.walletTopUpPath,
      pageBuilder: (context, state) {
        return NoTransitionPage(
          child: WalletTopUpPage(onBack: () => _popOrGo(context)),
        );
      },
    ),
  ],
);

void _startPayment(
  BuildContext context, {
  Recipient? recipient,
  PaymentType? paymentType,
}) {
  context.read<RecipientsProvider>().setCurrentObject(recipient?.id);
  context.pushNamed(
    PayoutRoutes.payment,
    queryParameters: PayoutRoutes.buildQueryParameters(
      paymentType: paymentType,
    ),
  );
}

void _openAddRecipient(BuildContext context, {Recipient? recipient}) {
  context.read<RecipientsProvider>().setCurrentObject(recipient?.id);
  context.pushNamed(PayoutRoutes.addRecipient);
}

void _openEditRecipient(BuildContext context, {required Recipient recipient}) {
  context.read<RecipientsProvider>().setCurrentObject(recipient.id);
  context.pushNamed(PayoutRoutes.editRecipient);
}

void _openLedgerEdit(BuildContext context, LedgerAccount account) {
  context.read<PaymentSourceController>().selectLedgerByRef(
    account.ledgerAccountRef,
  );
  context.pushToEditWallet();
}

void _openLedgerAddFunds(BuildContext context, LedgerAccount account) {
  context.read<PaymentSourceController>().selectLedgerByRef(
    account.ledgerAccountRef,
  );
  context.read<RecipientsProvider>().setCurrentObject(null);
  context.pushNamed(
    PayoutRoutes.payment,
    queryParameters: PayoutRoutes.buildQueryParameters(
      paymentType: PaymentType.ledger,
      destinationLedgerAccountRef: account.ledgerAccountRef,
    ),
  );
}

void _openWalletTopUp(BuildContext context, Wallet wallet) {
  context.read<WalletsController>().selectWallet(wallet);
  context.pushToWalletTopUp();
}

void _popOrGo(BuildContext context) {
  final router = GoRouter.of(context);
  if (router.canPop()) {
    router.pop();
  } else {
    context.goToPayout(PayoutDestination.dashboard);
  }
}
