import 'dart:io' show Platform;

import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';

import 'package:flutter_native_splash/flutter_native_splash.dart';
import 'package:flutter_web_plugins/url_strategy.dart';
import 'package:just_audio_background/just_audio_background.dart';
import 'package:just_audio_media_kit/just_audio_media_kit.dart';
import 'package:media_kit/media_kit.dart';
import 'package:provider/provider.dart';
import 'package:window_manager/window_manager.dart';

import 'package:feeddeck/repositories/app_repository.dart';
import 'package:feeddeck/repositories/layout_repository.dart';
import 'package:feeddeck/repositories/profile_repository.dart';
import 'package:feeddeck/repositories/settings_repository.dart';
import 'package:feeddeck/utils/constants.dart';
import 'package:feeddeck/widgets/confirmation/confirmation.dart';
import 'package:feeddeck/widgets/home/home.dart';
import 'package:feeddeck/widgets/reset_password/reset_password.dart';

/// Before we are calling [runApp] we have to ensure that the widget bindings
/// are initialized, so that we can preserve the splash screen until we are done
/// with the initalization of the app.
void main() async {
  WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized();
  FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);

  /// We have to initialize the [windowManager] before we are calling [runApp]
  /// on all desktop platforms, so we can set the window size, title and so on.
  if (!kIsWeb && (Platform.isLinux || Platform.isMacOS || Platform.isWindows)) {
    await windowManager.ensureInitialized();

    WindowOptions windowOptions = const WindowOptions(
      size: Size(1024, 768),
      center: true,
      backgroundColor: Colors.transparent,
      skipTaskbar: false,
      titleBarStyle: TitleBarStyle.normal,
    );

    windowManager.waitUntilReadyToShow(windowOptions, () async {
      await windowManager.setTitle('FeedDeck');
      await windowManager.show();
      await windowManager.focus();
    });
  }

  /// Initialize the [media_kit] packages, so that we can play audio and video
  /// files.
  MediaKit.ensureInitialized();
  JustAudioMediaKit.ensureInitialized();

  /// Initialize the [just_audio_background] package, so that we can play audio
  /// files in the background.
  ///
  /// We can not initialize the [just_audio_background] package on Windows and
  /// Linux, because then the returned duration in the `_player.durationStream`
  /// isn't working correctly in the [ItemAudioPlayer] widget.
  if (kIsWeb || Platform.isAndroid || Platform.isIOS || Platform.isMacOS) {
    await JustAudioBackground.init(
      androidNotificationChannelId: 'com.ryanheise.bg_demo.channel.audio',
      androidNotificationChannelName: 'Audio playback',
      androidNotificationOngoing: true,
    );
  }

  /// For the ewb we have to use the path url strategy, so that the redirect
  /// within Supabase is working in all cases. On all other platforms this is a
  /// noop.
  usePathUrlStrategy();

  /// We also have to initialize the [SettingsRepository], which contains all
  /// the settings, to use the app with Supabase. This way a user can also use
  /// his own Supabase backend.
  await SettingsRepository().init();

  runApp(const FeedDeckApp());
}

/// [FeedDeckScrollBehavior] changes the scrolling behavior of the app. This is
/// required to enable scrolling on desktop via drag, which is the only way that
/// a user can scroll vertically with a mouse via drag.
///
/// E.g. this is required for the [ColumnLayoutSources] widget, where we rely on
/// the drag gesture to scroll the list of sources.
class FeedDeckScrollBehavior extends MaterialScrollBehavior {
  @override
  Set<PointerDeviceKind> get dragDevices => {
        PointerDeviceKind.touch,
        PointerDeviceKind.mouse,
        PointerDeviceKind.trackpad,
        PointerDeviceKind.stylus,
        PointerDeviceKind.unknown,
      };
}

/// [onGenerateRoute] is used in `onGenerateRoute` and `onGenerateInitialRoutes`
/// of the [MaterialApp] to add handling for some special routes were do not
/// want to render the [Home] widget (e.g. set a new password).
///
/// For that we have to check the current path and when the user call one of
/// this "special" routes we render the corresponding widget and pass all the
/// required values from the query paramters to this widget.
Route onGenerateRoute(RouteSettings settings) {
  if (settings.name != null) {
    var uriData = Uri.parse(settings.name!);

    switch (uriData.path) {
      case '/confirmation':
        return MaterialPageRoute(
          builder: (_) => Confirmation(
            template: uriData.queryParameters['template'] ?? '',
            confirmationUrl: uriData.queryParameters['confirmation_url'] ?? '',
          ),
        );
      case '/reset-password':
        return MaterialPageRoute(
          builder: (_) => const ResetPassword(),
        );
    }
  }

  return MaterialPageRoute(
    builder: (_) => const Home(),
  );
}

/// The [FeedDeckApp] is the root widget of the app. The widget is used to
/// initialize some of our providers via the [MultiProvider] widget and to set
/// the theme for the app.
class FeedDeckApp extends StatelessWidget {
  const FeedDeckApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) => LayoutRepository()),
        ChangeNotifierProvider(create: (_) => AppRepository()),
        ChangeNotifierProvider(create: (_) => ProfileRepository()),
      ],
      child: TooltipVisibility(
        visible: false,
        child: MaterialApp(
          title: 'FeedDeck',
          debugShowCheckedModeBanner: false,
          theme: ThemeData(
            useMaterial3: true,
            colorScheme: const ColorScheme(
              brightness: Constants.brightness,
              primary: Constants.primary,
              onPrimary: Constants.onPrimary,
              secondary: Constants.secondary,
              onSecondary: Constants.onSecondary,
              error: Constants.error,
              onError: Constants.onError,
              background: Constants.background,
              onBackground: Constants.onBackground,
              surface: Constants.surface,
              onSurface: Constants.onSurface,
            ),
            canvasColor: Constants.canvasColor,
            appBarTheme: const AppBarTheme(
              centerTitle: true,
              backgroundColor: Constants.appBarBackgroundColor,
              scrolledUnderElevation: Constants.scrolledUnderElevation,
              elevation: Constants.appBarElevation,
            ),
            snackBarTheme: const SnackBarThemeData(
              backgroundColor: Constants.surface,
              contentTextStyle: TextStyle(
                color: Constants.onSurface,
              ),
            ),
            dialogTheme: const DialogTheme(
              backgroundColor: Constants.background,
              surfaceTintColor: Constants.background,
              contentTextStyle: TextStyle(
                color: Constants.onBackground,
              ),
            ),
            popupMenuTheme: const PopupMenuThemeData(
              color: Constants.background,
              surfaceTintColor: Constants.background,
              textStyle: TextStyle(
                color: Constants.onBackground,
              ),
            ),
            drawerTheme: const DrawerThemeData(
              backgroundColor: Constants.background,
              surfaceTintColor: Constants.background,
            ),
            bottomSheetTheme: const BottomSheetThemeData(
              backgroundColor: Constants.background,
              surfaceTintColor: Constants.background,
            ),
            pageTransitionsTheme: const PageTransitionsTheme(
              builders: {
                TargetPlatform.android: CupertinoPageTransitionsBuilder(),
                TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
                TargetPlatform.linux: CupertinoPageTransitionsBuilder(),
                TargetPlatform.macOS: CupertinoPageTransitionsBuilder(),
                TargetPlatform.windows: CupertinoPageTransitionsBuilder(),
              },
            ),
          ),
          scrollBehavior: FeedDeckScrollBehavior(),
          onGenerateInitialRoutes: (initialRoute) =>
              [onGenerateRoute(RouteSettings(name: initialRoute))],
          onGenerateRoute: (RouteSettings settings) =>
              onGenerateRoute(settings),
        ),
      ),
    );
  }
}
