// Copyright 2013 The Flutter Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

// This scenario demonstrates how to use redirect to handle a sign-in flow.
//
// The GoRouter.redirect method is called before the app is navigate to a
// new page. You can choose to redirect to a different page by returning a
// non-null URL string.

/// The login information.
class LoginInfo extends ChangeNotifier {
  /// The username of login.
  String get userName => _userName;
  String _userName = '';

  /// Whether a user has logged in.
  bool get loggedIn => _userName.isNotEmpty;

  /// Logs in a user.
  void login(String userName) {
    _userName = userName;
    notifyListeners();
  }

  /// Logs out the current user.
  void logout() {
    _userName = '';
    notifyListeners();
  }
}

/// Provides login information to its descendants.
class LoginInfoProvider extends InheritedNotifier<LoginInfo> {
  /// Creates a [LoginInfoProvider].
  const LoginInfoProvider({
    super.key,
    required super.notifier,
    required super.child,
  });

  /// Returns the [LoginInfo] from the closest [LoginInfoProvider] ancestor.
  static LoginInfo of(BuildContext context, {bool listen = true}) {
    final LoginInfoProvider? result = listen
        ? context.dependOnInheritedWidgetOfExactType<LoginInfoProvider>()
        : context
                  .getElementForInheritedWidgetOfExactType<LoginInfoProvider>()
                  ?.widget
              as LoginInfoProvider?;
    assert(result != null, 'No LoginInfoProvider found in context');
    return result!.notifier!;
  }
}

void main() => runApp(App());

/// The main app.
class App extends StatelessWidget {
  /// Creates an [App].
  App({super.key});

  final LoginInfo _loginInfo = LoginInfo();

  /// The title of the app.
  static const String title = 'GoRouter Example: Redirection';

  // add the login info into the tree as app state that can change over time
  @override
  Widget build(BuildContext context) => LoginInfoProvider(
    notifier: _loginInfo,
    child: MaterialApp.router(
      routerConfig: _router,
      title: title,
      debugShowCheckedModeBanner: false,
    ),
  );

  late final GoRouter _router = GoRouter(
    routes: <GoRoute>[
      GoRoute(
        path: '/',
        builder: (BuildContext context, GoRouterState state) =>
            const HomeScreen(),
      ),
      GoRoute(
        path: '/login',
        builder: (BuildContext context, GoRouterState state) =>
            const LoginScreen(),
      ),
    ],

    // redirect to the login page if the user is not logged in
    redirect: (BuildContext context, GoRouterState state) {
      // if the user is not logged in, they need to login
      final bool loggedIn = _loginInfo.loggedIn;
      final loggingIn = state.matchedLocation == '/login';
      if (!loggedIn) {
        return '/login';
      }

      // if the user is logged in but still on the login page, send them to
      // the home page
      if (loggingIn) {
        return '/';
      }

      // no need to redirect at all
      return null;
    },

    // changes on the listenable will cause the router to refresh it's route
    refreshListenable: _loginInfo,
  );
}

/// The login screen.
class LoginScreen extends StatelessWidget {
  /// Creates a [LoginScreen].
  const LoginScreen({super.key});

  @override
  Widget build(BuildContext context) => Scaffold(
    appBar: AppBar(title: const Text(App.title)),
    body: Center(
      child: ElevatedButton(
        onPressed: () {
          // log a user in, letting all the listeners know
          LoginInfoProvider.of(context, listen: false).login('test-user');

          // router will automatically redirect from /login to / using
          // refreshListenable
        },
        child: const Text('Login'),
      ),
    ),
  );
}

/// The home screen.
class HomeScreen extends StatelessWidget {
  /// Creates a [HomeScreen].
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    final LoginInfo info = LoginInfoProvider.of(context);

    return Scaffold(
      appBar: AppBar(
        title: const Text(App.title),
        actions: <Widget>[
          IconButton(
            onPressed: info.logout,
            tooltip: 'Logout: ${info.userName}',
            icon: const Icon(Icons.logout),
          ),
        ],
      ),
      body: const Center(child: Text('HomeScreen')),
    );
  }
}
