// Dart imports:
import 'dart:async';
import 'dart:convert';
import 'dart:io';

// Flutter imports:
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

// Package imports:
import 'package:redux/redux.dart';
import 'package:redux_logging/redux_logging.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:invoiceninja_flutter/utils/platforms.dart';
// import 'package:bitsdojo_window/bitsdojo_window.dart';

// Project imports:
import 'package:invoiceninja_flutter/.env.dart';
import 'package:invoiceninja_flutter/constants.dart';
import 'package:invoiceninja_flutter/data/models/entities.dart';
import 'package:invoiceninja_flutter/data/models/serializers.dart';
import 'package:invoiceninja_flutter/main_app.dart';
import 'package:invoiceninja_flutter/redux/app/app_middleware.dart';
import 'package:invoiceninja_flutter/redux/app/app_reducer.dart';
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
import 'package:invoiceninja_flutter/redux/auth/auth_middleware.dart';
import 'package:invoiceninja_flutter/redux/client/client_middleware.dart';
import 'package:invoiceninja_flutter/redux/company_gateway/company_gateway_middleware.dart';
import 'package:invoiceninja_flutter/redux/credit/credit_middleware.dart';
import 'package:invoiceninja_flutter/redux/dashboard/dashboard_middleware.dart';
import 'package:invoiceninja_flutter/redux/design/design_middleware.dart';
import 'package:invoiceninja_flutter/redux/document/document_middleware.dart';
import 'package:invoiceninja_flutter/redux/expense/expense_middleware.dart';
import 'package:invoiceninja_flutter/redux/expense_category/expense_category_middleware.dart';
import 'package:invoiceninja_flutter/redux/group/group_middleware.dart';
import 'package:invoiceninja_flutter/redux/invoice/invoice_middleware.dart';
import 'package:invoiceninja_flutter/redux/payment/payment_middleware.dart';
import 'package:invoiceninja_flutter/redux/payment_term/payment_term_middleware.dart';
import 'package:invoiceninja_flutter/redux/product/product_middleware.dart';
import 'package:invoiceninja_flutter/redux/project/project_middleware.dart';
import 'package:invoiceninja_flutter/redux/quote/quote_middleware.dart';
import 'package:invoiceninja_flutter/redux/recurring_expense/recurring_expense_middleware.dart';
import 'package:invoiceninja_flutter/redux/recurring_invoice/recurring_invoice_middleware.dart';
import 'package:invoiceninja_flutter/redux/reports/reports_middleware.dart';
import 'package:invoiceninja_flutter/redux/settings/settings_middleware.dart';
import 'package:invoiceninja_flutter/redux/subscription/subscription_middleware.dart';
import 'package:invoiceninja_flutter/redux/task/task_middleware.dart';
import 'package:invoiceninja_flutter/redux/task_status/task_status_middleware.dart';
import 'package:invoiceninja_flutter/redux/tax_rate/tax_rate_middleware.dart';
import 'package:invoiceninja_flutter/redux/token/token_middleware.dart';
import 'package:invoiceninja_flutter/redux/ui/pref_state.dart';
import 'package:invoiceninja_flutter/redux/user/user_middleware.dart';
import 'package:invoiceninja_flutter/redux/vendor/vendor_middleware.dart';
import 'package:invoiceninja_flutter/redux/webhook/webhook_middleware.dart';
import 'package:invoiceninja_flutter/redux/transaction/transaction_middleware.dart';
import 'package:invoiceninja_flutter/redux/bank_account/bank_account_middleware.dart';
import 'package:invoiceninja_flutter/redux/purchase_order/purchase_order_middleware.dart';
import 'package:invoiceninja_flutter/redux/transaction_rule/transaction_rule_middleware.dart';
import 'package:window_manager/window_manager.dart';
import 'package:invoiceninja_flutter/redux/schedule/schedule_middleware.dart';
// STARTER: import - do not remove comment

import 'package:invoiceninja_flutter/utils/web_stub.dart'
    if (dart.library.html) 'package:invoiceninja_flutter/utils/web.dart';

// https://github.com/dart-lang/io/issues/83#issuecomment-940617222
const isrgRootX1 = '''-----BEGIN CERTIFICATE-----
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
-----END CERTIFICATE-----
''';

void main({bool isTesting = false}) async {
  WidgetsFlutterBinding.ensureInitialized();
  _registerErrorHandlers();

  try {
    SecurityContext.defaultContext.setTrustedCertificatesBytes(
      Uint8List.fromList(isrgRootX1.codeUnits),
    );
  } catch (e) {
    // Ignore CERT_ALREADY_IN_HASH_TABLE
  }

  if (isDesktopOS()) {
    await windowManager.ensureInitialized();

    final prefs = await SharedPreferences.getInstance();
    windowManager.waitUntilReadyToShow(
        WindowOptions(
          center: true,
          size: Size(
            prefs.getDouble(kSharedPrefWidth) ?? 800,
            prefs.getDouble(kSharedPrefHeight) ?? 600,
          ),
        ), () async {
      await windowManager.show();
      await windowManager.focus();
    });
  }

  final store = Store<AppState>(appReducer,
      initialState: await _initialState(isTesting),
      middleware: []
        ..addAll(createStoreAuthMiddleware())
        ..addAll(createStoreDocumentsMiddleware())
        ..addAll(createStoreDashboardMiddleware())
        ..addAll(createStoreProductsMiddleware())
        ..addAll(createStoreClientsMiddleware())
        ..addAll(createStoreInvoicesMiddleware())
        ..addAll(createStoreExpensesMiddleware())
        ..addAll(createStoreVendorsMiddleware())
        ..addAll(createStoreTasksMiddleware())
        ..addAll(createStoreProjectsMiddleware())
        ..addAll(createStorePaymentsMiddleware())
        ..addAll(createStoreQuotesMiddleware())
        ..addAll(createStoreSettingsMiddleware())
        ..addAll(createStoreReportsMiddleware())
        // STARTER: middleware - do not remove comment
        ..addAll(createStoreSchedulesMiddleware())
        ..addAll(createStoreTransactionRulesMiddleware())
        ..addAll(createStoreTransactionsMiddleware())
        ..addAll(createStoreBankAccountsMiddleware())
        ..addAll(createStorePurchaseOrdersMiddleware())
        ..addAll(createStoreRecurringExpensesMiddleware())
        ..addAll(createStoreSubscriptionsMiddleware())
        ..addAll(createStoreTaskStatusesMiddleware())
        ..addAll(createStoreExpenseCategoriesMiddleware())
        ..addAll(createStoreRecurringInvoicesMiddleware())
        ..addAll(createStoreWebhooksMiddleware())
        ..addAll(createStoreTokensMiddleware())
        ..addAll(createStorePaymentTermsMiddleware())
        ..addAll(createStoreDesignsMiddleware())
        ..addAll(createStoreCreditsMiddleware())
        ..addAll(createStoreUsersMiddleware())
        ..addAll(createStoreTaxRatesMiddleware())
        ..addAll(createStoreCompanyGatewaysMiddleware())
        ..addAll(createStoreGroupsMiddleware())
        ..addAll(createStorePersistenceMiddleware())
        ..addAll(isTesting || kReleaseMode || !Config.DEBUG_EVENTS
            ? []
            : [
                LoggingMiddleware<dynamic>.printer(
                  formatter: LoggingMiddleware.multiLineFormatter,
                ),
              ]));

  if (!kReleaseMode) {
    runApp(InvoiceNinjaApp(store: store));
  } else {
    await SentryFlutter.init(
      (options) {
        options.dsn = Config.SENTRY_DNS;
        options.release = const String.fromEnvironment('SENTRY_RELEASE',
            defaultValue: kClientVersion);
        options.dist = kClientVersion;
        options.beforeSend = (SentryEvent event, {dynamic hint}) {
          final state = store.state;
          final account = state.account;
          final reportErrors = account.reportErrors;

          if (!reportErrors) {
            return null;
          }

          event = event.copyWith(
            environment: '${store.state.environment}'.split('.').last,
            /*
            extra: <String, dynamic>{
              'server_version': account.currentVersion,
              'route': state.uiState.currentRoute,
            },
            */
          );

          return event;
        };
      },
      appRunner: () => runApp(InvoiceNinjaApp(store: store)),
    );
  }

  /*
  if (isWindows()) {
    doWhenWindowReady(() {
      final win = appWindow;
      win.title = 'Invoice Ninja';
      win.show();
    });
  }
  */
}

Future<AppState> _initialState(bool isTesting) async {
  final prefs = await SharedPreferences.getInstance();
  final prefString = prefs.getString(kSharedPrefs);

  final url = WebUtils.apiUrl ?? prefs.getString(kSharedPrefUrl) ?? '';
  if (!kReleaseMode) {
    //url = kAppStagingUrl;
    //url = kAppProductionUrl;
    //url = kAppDemoUrl;
  }

  PrefState? prefState = PrefState();
  if (prefString != null) {
    try {
      prefState = serializers.deserializeWith(
          PrefState.serializer, json.decode(prefString));
    } catch (e) {
      print('## Error: Failed to load prefs: $e');
    }
  }

  prefState = prefState!.rebuild((b) => b
    ..enableDarkModeSystem =
        PlatformDispatcher.instance.platformBrightness == Brightness.dark);

  String? browserRoute;
  if (kIsWeb && prefState.isDesktop) {
    browserRoute = WebUtils.browserRoute;
    if (browserRoute!.isNotEmpty && browserRoute.length > 4) {
      if (browserRoute == '/kanban') {
        browserRoute = '/task';
        prefState = prefState.rebuild((b) => b
          ..showKanban = true
          ..useSidebarEditor[EntityType.task] = true);
      }
    } else {
      browserRoute = null;
    }
  }

  bool reportErrors = false;
  bool whiteLabeled = false;
  String? referralCode = '';

  if (kIsWeb) {
    reportErrors = WebUtils.getHtmlValue('report-errors') == '1';
    whiteLabeled = WebUtils.getHtmlValue('white-label') == '1';
    referralCode = WebUtils.getHtmlValue('rc');
    if (reportErrors) {
      print('## Error reporting is enabled');
    }
  }

  return AppState(
    prefState: prefState,
    url: Config.DEMO_MODE ? '' : url,
    referralCode: referralCode,
    reportErrors: reportErrors,
    isWhiteLabeled: whiteLabeled,
    currentRoute: browserRoute,
  );
}

void _registerErrorHandlers() {
  /*
  // * Show some error UI if any uncaught exception happens
  FlutterError.onError = (FlutterErrorDetails details) {
    FlutterError.presentError(details);
    //errorLogger.logError(details.exception, details.stack);
  };
  // * Handle errors from the underlying platform/OS
  PlatformDispatcher.instance.onError = (Object error, StackTrace stack) {
    //errorLogger.logError(error, stack);
    return true;
  };
  */

  ErrorWidget.builder = (FlutterErrorDetails details) {
    return Material(
      color: Colors.grey.shade100,
      child: Center(
        child: Text(
          details.toString(),
          style: TextStyle(color: Colors.black),
        ),
      ),
    );
  };
}
