// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

import 'dart:async';
import 'dart:math';

import 'package:example_router/core/constants/form_field.dart';
import 'package:example_router/core/dependency_injection/get_it.dart';
import 'package:example_router/core/routes/router.dart';
import 'package:example_router/core/utils/custom_password.dart';
import 'package:example_router/core/utils/forms.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart';
import 'package:wyatt_form_bloc/wyatt_form_bloc.dart';
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart';

FutureOrResult<int?> onSignUpSuccess(
  AuthenticationRepository<int> repo,
  Account? account,
  WyattForm form,
) async {
  const id = -1;
  final confirmedPassword =
      form.valueOf<String?>(AppFormField.confirmedPassword);

  debugPrint(
      'onSignUpSuccess: $account, generatedId: $id, extraFormData: $confirmedPassword');
  return const Ok<int, AppException>(id);
}

FutureOrResult<int?> onAccountChanges(
  AuthenticationRepository<int> repo,
  Account? account,
) async {
  final id = Random().nextInt(1000);
  final token =
      await repo.getIdentityToken().fold((value) => value, (error) => 'null');

  debugPrint('onAccountChanges: $account, token: $token, generatedId: $id');
  return Ok<int, AppException>(id);
}

class App extends StatelessWidget {
  final AuthenticationRepository<int> authenticationRepository =
      AuthenticationRepositoryImpl(
    authenticationCacheDataSource: getIt<AuthenticationCacheDataSource<int>>(),
    authenticationRemoteDataSource: getIt<AuthenticationRemoteDataSource>(),
    onSignUpSuccess: onSignUpSuccess,
    onAuthChange: onAccountChanges,
    customPasswordValidator: const CustomPassword.pure(),
    extraSignUpInputs: [
      FormInput(
        AppFormField.confirmedPassword,
        const ConfirmedPassword.pure(),
        metadata: const FormInputMetadata<void>(export: false),
      ),
    ],
  );

  App({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    AuthenticationState<int>? previous;

    final AuthenticationCubit<int> authenticationCubit =
        AuthenticationCubit(authenticationRepository: authenticationRepository);

    final GoRouter router = GoRouter(
      initialLocation: '/',
      routes: AppRouter.routes,
      debugLogDiagnostics: true,
      errorBuilder: (_, __) => const ColoredBox(
        color: Colors.red,
      ),
      refreshListenable: GoRouterRefreshStream(authenticationCubit.stream),
      redirect: (context, state) {
        final authState = authenticationCubit.state;

        if (authState.status != previous?.status) {
          previous = authState;
          // Checking if current path is onboarding or not
          final isOnboarding = AppRouter.publicRoutes.contains(state.subloc);
          if (authState.status == AuthenticationStatus.unauthenticated) {
            debugPrint('Not logged');
            if (!isOnboarding) {
              return '/';
            }
          } else if (authState.status == AuthenticationStatus.authenticated) {
            debugPrint('Logged');
            return '/home';
          }
        }
        return null;
      },
    );

    return MultiRepositoryProvider(
      providers: [
        RepositoryProvider<AuthenticationRepository<int>>.value(
          value: authenticationRepository,
        ),
        RepositoryProvider<FormRepository>(
          create: (context) =>
              FormRepositoryImpl()..registerForm(AppForms.getEditProfileForm()),
        ),
      ],
      child: MultiBlocProvider(
        providers: [
          BlocProvider<AuthenticationCubit<int>>.value(
            value: authenticationCubit,
          ),
          BlocProvider<SignUpCubit<int>>(
            create: (_) => SignUpCubit(
              authenticationRepository: authenticationRepository,
            ),
          ),
          BlocProvider<SignInCubit<int>>(
            create: (_) => SignInCubit(
              authenticationRepository: authenticationRepository,
            ),
          ),
        ],
        child: MaterialApp.router(
          title: 'Demo Authentication',
          debugShowCheckedModeBanner: false,
          routerConfig: router,
        ),
      ),
    );
  }
}

class GoRouterRefreshStream extends ChangeNotifier {
  GoRouterRefreshStream(Stream<dynamic> stream) {
    notifyListeners();
    _subscription = stream.asBroadcastStream().listen(
          (dynamic _) => notifyListeners(),
        );
  }

  late final StreamSubscription<dynamic> _subscription;

  @override
  void dispose() {
    _subscription.cancel();
    super.dispose();
  }
}
