import 'package:anitrack/i18n/strings.g.dart';
import 'package:anitrack/src/data/anime.dart';
import 'package:anitrack/src/ui/bloc/anime_list_bloc.dart';
import 'package:anitrack/src/ui/bloc/calendar_bloc.dart';
import 'package:anitrack/src/ui/bloc/details_bloc.dart';
import 'package:anitrack/src/ui/constants.dart';
import 'package:anitrack/src/ui/helpers.dart';
import 'package:anitrack/src/ui/widgets/grid_item.dart';
import 'package:anitrack/src/ui/widgets/image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:get_it/get_it.dart';

enum Weekday {
  monday,
  tuesday,
  wednesday,
  thursday,
  friday,
  saturday,
  sunday,
  unknown;

  String toName() {
    switch (this) {
      case Weekday.monday:
        return t.calendar.days.monday;
      case Weekday.tuesday:
        return t.calendar.days.tuesday;
      case Weekday.wednesday:
        return t.calendar.days.wednesday;
      case Weekday.thursday:
        return t.calendar.days.thursday;
      case Weekday.friday:
        return t.calendar.days.friday;
      case Weekday.saturday:
        return t.calendar.days.saturday;
      case Weekday.sunday:
        return t.calendar.days.sunday;
      case Weekday.unknown:
        return t.calendar.days.unknown;
    }
  }
}

extension AddIfExists<K, V> on Map<K, List<V>> {
  void addOrSet(K key, V value) {
    if (containsKey(key)) {
      this[key]!.add(value);
    } else {
      this[key] = List<V>.from([value]);
    }
  }
}

class CalendarPage extends StatefulWidget {
  const CalendarPage({super.key});

  static MaterialPageRoute<dynamic> get route => MaterialPageRoute<dynamic>(
    builder: (_) => const CalendarPage(),
    settings: const RouteSettings(
      name: calendarRoute,
    ),
  );

  static AppBar buildAppBar(BuildContext context) {
    return AppBar(
      title: Text(t.calendar.calendar),
      actions: [
        IconButton(
          onPressed: () {
            context.read<CalendarBloc>().add(
              RefreshPerformedEvent(),
            );
          },
          icon: const Icon(Icons.refresh),
        ),
      ],
    );
  }

  @override
  CalendarPageState createState() => CalendarPageState();
}

class CalendarPageState extends State<CalendarPage> {
  /// State for the "refreshing" overlay.
  OverlayEntry? _overlayEntry;

  List<Widget> _renderWeekdayList(
    BuildContext context,
    Weekday day,
    Map<Weekday, List<AnimeTrackingData>> data,
  ) {
    if (!data.containsKey(day)) {
      return const [];
    }

    assert(data[day]!.isNotEmpty, 'There should be at least one anime');
    return [
      SliverToBoxAdapter(
        child: Padding(
          padding: const EdgeInsets.only(
            right: 16,
            top: 20,
            bottom: 4,
          ),
          child: Text(
            day.toName(),
            style: Theme.of(context).textTheme.titleLarge,
          ),
        ),
      ),
      SliverGrid.builder(
        gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: 3,
          mainAxisSpacing: 8,
          crossAxisSpacing: 8,
          childAspectRatio: 120 / (100 * (16 / 9)),
        ),
        itemCount: data[day]!.length,
        itemBuilder: (context, index) {
          final anime = data[day]![index];
          return GridItem(
            enableDrag: false,
            child: AnimeCoverImage(
              url: anime.thumbnailUrl,
              hero: 'calendar_${anime.id}',
              onTap: () {
                context.read<DetailsBloc>().add(
                  AnimeDetailsRequestedEvent(
                    anime,
                    heroImagePrefix: 'calendar_',
                  ),
                );
              },
            ),
          );
        },
      ),
    ];
  }

  Map<Weekday, List<AnimeTrackingData>> _constructAiringMap() {
    final airingAnimeMap = <Weekday, List<AnimeTrackingData>>{};
    for (final anime in GetIt.I.get<AnimeListBloc>().unfilteredAnime) {
      if (!anime.airing) continue;

      final Weekday day;
      switch (anime.broadcastDay) {
        case 'Mondays':
          day = Weekday.monday;
        case 'Tuesdays':
          day = Weekday.tuesday;
        case 'Wednesdays':
          day = Weekday.wednesday;
        case 'Thursdays':
          day = Weekday.thursday;
        case 'Fridays':
          day = Weekday.friday;
        case 'Saturdays':
          day = Weekday.saturday;
        case 'Sundays':
          day = Weekday.sunday;
        default:
          day = Weekday.unknown;
      }

      airingAnimeMap.addOrSet(day, anime);
    }
    return airingAnimeMap;
  }

  @override
  Widget build(BuildContext context) {
    return BlocBuilder<AnimeListBloc, AnimeListState>(
      builder: (context, animeListState) {
        final airingAnimeMap = _constructAiringMap();
        return BlocListener<CalendarBloc, CalendarState>(
          listenWhen: (previous, current) =>
              previous.refreshing != current.refreshing,
          listener: (context, state) {
            if (!state.refreshing) {
              _overlayEntry?.remove();
              _overlayEntry?.dispose();
              _overlayEntry = null;
            } else {
              _overlayEntry = OverlayEntry(
                builder: (context) => SafeArea(
                  child: Stack(
                    children: [
                      const ModalBarrier(
                        dismissible: false,
                        color: Colors.black54,
                      ),
                      Center(
                        child: SizedBox(
                          width: 150,
                          height: 150,
                          child: DecoratedBox(
                            decoration: BoxDecoration(
                              borderRadius: BorderRadius.circular(10),
                              color: Colors.grey.shade800,
                            ),
                            child: Column(
                              mainAxisSize: MainAxisSize.min,
                              mainAxisAlignment: MainAxisAlignment.center,
                              children: [
                                const Padding(
                                  padding: EdgeInsets.all(25),
                                  child: CircularProgressIndicator(),
                                ),
                                BlocBuilder<CalendarBloc, CalendarState>(
                                  builder: (context, state) => Text(
                                    t.settings.importIndicator(
                                      current: state.refreshingCount,
                                      total: state.refreshingTotal,
                                    ),
                                    style: Theme.of(
                                      context,
                                    ).textTheme.bodyMedium,
                                  ),
                                ),
                              ],
                            ),
                          ),
                        ),
                      ),
                    ],
                  ),
                ),
              );
              Overlay.of(context).insert(_overlayEntry!);
            }
          },
          child: WillPopScope(
            onWillPop: () async =>
                !context.read<CalendarBloc>().state.refreshing,
            child: Padding(
              padding: const EdgeInsetsGeometry.symmetric(
                horizontal: 12,
              ),
              child: CustomScrollView(
                slivers: [
                  // Render all available weekdays
                  ..._renderWeekdayList(
                    context,
                    Weekday.unknown,
                    airingAnimeMap,
                  ),
                  ..._renderWeekdayList(
                    context,
                    Weekday.monday,
                    airingAnimeMap,
                  ),
                  ..._renderWeekdayList(
                    context,
                    Weekday.tuesday,
                    airingAnimeMap,
                  ),
                  ..._renderWeekdayList(
                    context,
                    Weekday.wednesday,
                    airingAnimeMap,
                  ),
                  ..._renderWeekdayList(
                    context,
                    Weekday.thursday,
                    airingAnimeMap,
                  ),
                  ..._renderWeekdayList(
                    context,
                    Weekday.friday,
                    airingAnimeMap,
                  ),
                  ..._renderWeekdayList(
                    context,
                    Weekday.saturday,
                    airingAnimeMap,
                  ),
                  ..._renderWeekdayList(
                    context,
                    Weekday.sunday,
                    airingAnimeMap,
                  ),

                  // Provide a nice bottom padding, while keeping the elastic effect attached
                  // to the bottom-most edge.
                  const SliverToBoxAdapter(
                    child: SizedBox(
                      height: 16,
                    ),
                  ),
                ],
              ),
            ),
          ),
        );
      },
    );
  }
}
