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

import '../auth/auth_provider.dart';
import '../auth/login_screen.dart';
import '../auth/server_url_screen.dart';
import '../library/album_detail_screen.dart';
import '../library/artist_detail_screen.dart';
import '../models/album.dart';
import '../models/artist.dart';
import '../models/playlist.dart';
import '../discover/discover_screen.dart';
import '../library/home_screen.dart';
import '../library/library_screen.dart';
import '../player/now_playing_screen.dart';
import '../player/player_bar.dart';
import '../player/queue_screen.dart';
import '../playlists/playlist_detail_screen.dart';
import '../playlists/playlists_list_screen.dart';
import '../requests/requests_screen.dart';
import '../search/search_screen.dart';
import '../settings/settings_screen.dart';
import '../update/client_update_provider.dart';
import '../update/update_banner.dart';
import '../admin/admin_landing_screen.dart';
import '../admin/admin_requests_screen.dart';
import '../admin/admin_quarantine_screen.dart';
import '../admin/admin_users_screen.dart';
import 'widgets/version_gate.dart';

/// Exposed as a Provider so its single argument is a real `Ref` (the
/// constructor's redirect closures use `ref.read`). Widgets consume the
/// router via `ref.watch(routerProvider)` instead of constructing it
/// themselves with a `WidgetRef`, which can't satisfy the `Ref` type.
final routerProvider = Provider<GoRouter>((ref) => buildRouter(ref));

GoRouter buildRouter(Ref ref) {
  return GoRouter(
    initialLocation: '/',
    redirect: (ctx, state) async {
      final url = await ref.read(serverUrlProvider.future);
      if (url == null) return '/server-url';
      final user = await ref.read(authControllerProvider.future);
      final loc = state.matchedLocation;
      if (user == null && loc != '/login' && loc != '/server-url') {
        return '/login';
      }
      if (user != null && (loc == '/login' || loc == '/server-url')) {
        return '/home';
      }
      if (loc.startsWith('/admin') && !user!.isAdmin) {
        return '/home';
      }
      return null;
    },
    routes: [
      GoRoute(path: '/', redirect: (_, __) => '/home'),
      GoRoute(path: '/server-url', builder: (_, __) => const ServerUrlScreen()),
      GoRoute(path: '/login',      builder: (_, __) => const LoginScreen()),
      // /now-playing lives outside the ShellRoute on purpose. The full
      // player IS the player UI when active, and we don't want the mini
      // bar from the shell underneath fighting for the bottom strip.
      // Pushing this route unmounts the shell entirely; the slide-up
      // transition still feels right because the shell stays painted
      // for the duration of the animation.
      GoRoute(
        path: '/now-playing',
        pageBuilder: (_, __) => CustomTransitionPage(
          child: const NowPlayingScreen(),
          transitionDuration: const Duration(milliseconds: 280),
          reverseTransitionDuration: const Duration(milliseconds: 240),
          transitionsBuilder: (_, anim, __, child) {
            final eased = CurvedAnimation(
              parent: anim,
              curve: Curves.easeOutCubic,
              reverseCurve: Curves.easeInCubic,
            );
            return SlideTransition(
              position: Tween(
                begin: const Offset(0, 1),
                end: Offset.zero,
              ).animate(eased),
              child: child,
            );
          },
        ),
      ),
      // /queue lives outside the ShellRoute too. Why: pushing /queue
      // from /now-playing (which is also outside the shell) used to
      // cause go_router to mount a second ShellRoute instance under
      // the existing one, producing a duplicate page-key assertion
      // (NavigatorState._debugCheckDuplicatedPageKeys). Top-level
      // routes can stack on each other freely; shell-children can't
      // when something on top of the shell is already routing.
      GoRoute(path: '/queue', builder: (_, __) => const QueueScreen()),
      ShellRoute(
        builder: (ctx, state, child) => VersionGate(child: _ShellWithPlayerBar(child: child)),
        routes: [
          GoRoute(path: '/home', builder: (_, __) => const HomeScreen()),
          GoRoute(
            path: '/artists/:id',
            // `extra` carries an optional ArtistRef so the detail
            // header renders immediately while tracks/albums load.
            builder: (_, s) => ArtistDetailScreen(
              id: s.pathParameters['id']!,
              seed: s.extra is ArtistRef ? s.extra as ArtistRef : null,
            ),
          ),
          GoRoute(
            path: '/albums/:id',
            builder: (_, s) => AlbumDetailScreen(
              id: s.pathParameters['id']!,
              seed: s.extra is AlbumRef ? s.extra as AlbumRef : null,
            ),
          ),
          GoRoute(path: '/search', builder: (_, __) => const SearchScreen()),
          GoRoute(path: '/library', builder: (_, __) => const LibraryScreen()),
          GoRoute(path: '/discover', builder: (_, __) => const DiscoverScreen()),
          GoRoute(path: '/settings', builder: (_, __) => const SettingsScreen()),
          GoRoute(path: '/playlists', builder: (_, __) => const PlaylistsListScreen()),
          GoRoute(
            path: '/playlists/:id',
            builder: (_, s) => PlaylistDetailScreen(
              id: s.pathParameters['id']!,
              seed: s.extra is Playlist ? s.extra as Playlist : null,
            ),
          ),
          GoRoute(path: '/requests',         builder: (_, __) => const RequestsScreen()),
          GoRoute(path: '/admin',            builder: (_, __) => const AdminLandingScreen()),
          GoRoute(path: '/admin/requests',   builder: (_, __) => const AdminRequestsScreen()),
          GoRoute(path: '/admin/quarantine', builder: (_, __) => const AdminQuarantineScreen()),
          GoRoute(path: '/admin/users',      builder: (_, __) => const AdminUsersScreen()),
        ],
      ),
    ],
  );
}

class _ShellWithPlayerBar extends ConsumerWidget {
  const _ShellWithPlayerBar({required this.child});
  final Widget child;
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    // The banner sits above the routed child and uses SafeArea to clear
    // the system status bar. MediaQuery.padding is screen-relative
    // though, so the child's Scaffold/AppBar would still apply its own
    // top status-bar inset on top of the banner — doubling the gap.
    // When the banner is showing, strip that inset from the child so
    // its AppBar lands directly under the banner.
    final hasBanner = ref.watch(shouldShowUpdateBannerProvider) != null;
    return Column(
      children: [
        // VersionTooOldBanner above UpdateBanner: a server-rejects-you
        // soft warning carries more user-relevant urgency than the APK
        // download prompt below it. The two banners can coexist
        // (server says you're too old AND a local APK is queued).
        const VersionTooOldBanner(),
        const UpdateBanner(),
        Expanded(
          child: hasBanner
              ? MediaQuery.removePadding(
                  context: context,
                  removeTop: true,
                  child: child,
                )
              : child,
        ),
        const PlayerBar(),
      ],
    );
  }
}

