import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_expandable_fab/flutter_expandable_fab.dart';
import 'package:gap/gap.dart';
import 'package:go_router/go_router.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:provider/provider.dart';
import 'package:responsive_framework/responsive_framework.dart';
import 'package:styled_widget/styled_widget.dart';
import 'package:surface/providers/config.dart';
import 'package:surface/providers/post.dart';
import 'package:surface/providers/sn_network.dart';
import 'package:surface/providers/sn_realm.dart';
import 'package:surface/providers/userinfo.dart';
import 'package:surface/types/post.dart';
import 'package:surface/types/realm.dart';
import 'package:surface/widgets/account/account_image.dart';
import 'package:surface/widgets/app_bar_leading.dart';
import 'package:surface/widgets/dialog.dart';
import 'package:surface/widgets/feed/feed_news.dart';
import 'package:surface/widgets/feed/feed_unknown.dart';
import 'package:surface/widgets/navigation/app_scaffold.dart';
import 'package:surface/widgets/post/fediverse_post_item.dart';
import 'package:surface/widgets/post/post_item.dart';
import 'package:very_good_infinite_list/very_good_infinite_list.dart';

const kPostChannels = ['Global', 'Friends', 'Following'];
const kPostChannelIcons = [Symbols.globe, Symbols.group, Symbols.subscriptions];

const Map<String, IconData> kCategoryIcons = {
  'technology': Symbols.tools_wrench,
  'gaming': Symbols.gamepad,
  'life': Symbols.nightlife,
  'arts': Symbols.format_paint,
  'sports': Symbols.sports_soccer,
  'music': Symbols.music_note,
  'news': Symbols.newspaper,
  'knowledge': Symbols.library_books,
  'literature': Symbols.book,
  'funny': Symbols.attractions,
};

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

  @override
  State<ExploreScreen> createState() => _ExploreScreenState();
}

class _ExploreScreenState extends State<ExploreScreen>
    with TickerProviderStateMixin {
  late TabController _tabController = TabController(
    length: kPostChannels.length,
    vsync: this,
  );

  final _fabKey = GlobalKey<ExpandableFabState>();
  final _listKey = GlobalKey<_PostListWidgetState>();

  bool _showCategories = false;

  final List<SnPostCategory> _categories = List.empty(growable: true);

  Future<void> _fetchCategories() async {
    _categories.clear();
    try {
      final sn = context.read<SnNetworkProvider>();
      final resp = await sn.client.get('/cgi/co/categories?take=100');
      setState(() {
        _categories.addAll(resp.data
                .map((e) => SnPostCategory.fromJson(e))
                .cast<SnPostCategory>() ??
            []);
      });
    } catch (err) {
      if (mounted) context.showErrorDialog(err);
    }
  }

  final List<SnRealm> _realms = List.empty(growable: true);

  Future<void> _fetchRealms() async {
    try {
      final ua = context.read<UserProvider>();
      if (!ua.isAuthorized) return;
      final rels = context.read<SnRealmProvider>();
      final out = await rels.listAvailableRealms();
      setState(() {
        _realms.addAll(out);
      });
    } catch (err) {
      if (!mounted) return;
      context.showErrorDialog(err);
      rethrow;
    }
  }

  void _toggleShowCategories() {
    _showCategories = !_showCategories;
    if (_showCategories) {
      _tabController = TabController(length: _categories.length, vsync: this);
      _listKey.currentState?.setCategory(_categories[_tabController.index]);
      _listKey.currentState?.refreshPosts();
    } else {
      _tabController = TabController(length: kPostChannels.length, vsync: this);
      _listKey.currentState?.setCategory(null);
      _listKey.currentState?.refreshPosts();
    }
    _tabListen();
    setState(() {});
  }

  void _tabListen() {
    _tabController.addListener(() {
      if (_tabController.indexIsChanging) {
        if (_showCategories) {
          _listKey.currentState?.setCategory(_categories[_tabController.index]);
          _listKey.currentState?.refreshPosts();
          return;
        }
        switch (_tabController.index) {
          case 0:
          case 3:
            _listKey.currentState?.setChannel(null);
            break;
          case 1:
            _listKey.currentState?.setChannel('friends');
            break;
          case 2:
            _listKey.currentState?.setChannel('following');
            break;
        }
        _listKey.currentState?.refreshPosts();
      }
    });
  }

  @override
  void initState() {
    super.initState();
    _tabListen();
    _fetchCategories();
    _fetchRealms();
  }

  @override
  void dispose() {
    _tabController.dispose();
    super.dispose();
  }

  Future<void> refreshPosts() async {
    await _listKey.currentState?.refreshPosts();
  }

  @override
  Widget build(BuildContext context) {
    final cfg = context.watch<ConfigProvider>();
    return AppScaffold(
      noBackground: ResponsiveScaffold.getIsExpand(context),
      floatingActionButtonLocation: ExpandableFab.location,
      floatingActionButton: ExpandableFab(
        key: _fabKey,
        distance: 75,
        type: ExpandableFabType.up,
        childrenAnimation: ExpandableFabAnimation.none,
        overlayStyle: ExpandableFabOverlayStyle(
          color: Theme.of(context)
              .colorScheme
              .surface
              .withAlpha((255 * 0.5).round()),
        ),
        openButtonBuilder: RotateFloatingActionButtonBuilder(
          child: const Icon(Symbols.add, size: 28),
          fabSize: ExpandableFabSize.regular,
          foregroundColor:
              Theme.of(context).floatingActionButtonTheme.foregroundColor,
          backgroundColor:
              Theme.of(context).floatingActionButtonTheme.backgroundColor,
        ),
        closeButtonBuilder: DefaultFloatingActionButtonBuilder(
          child: const Icon(Symbols.close, size: 28),
          fabSize: ExpandableFabSize.regular,
          foregroundColor:
              Theme.of(context).floatingActionButtonTheme.foregroundColor,
          backgroundColor:
              Theme.of(context).floatingActionButtonTheme.backgroundColor,
        ),
        children: [
          Row(
            children: [
              Text('writePost').tr(),
              const Gap(20),
              FloatingActionButton(
                heroTag: null,
                tooltip: 'writePost'.tr(),
                onPressed: () {
                  GoRouter.of(context).pushNamed('postEditor').then((value) {
                    if (value == true) {
                      refreshPosts();
                    }
                  });
                  _fabKey.currentState!.toggle();
                },
                child: const Icon(Symbols.edit),
              ),
            ],
          ),
          Row(
            children: [
              Text('postDraftBox').tr(),
              const Gap(20),
              FloatingActionButton(
                heroTag: null,
                tooltip: 'postDraftBox'.tr(),
                onPressed: () {
                  GoRouter.of(context).pushNamed('postDraftBox');
                  _fabKey.currentState!.toggle();
                },
                child: const Icon(Symbols.box_edit),
              ),
            ],
          ),
        ],
      ),
      body: NestedScrollView(
        headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
          return [
            SliverOverlapAbsorber(
              handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
              sliver: SliverAppBar(
                leading:
                    ResponsiveBreakpoints.of(context).smallerOrEqualTo(MOBILE)
                        ? AutoAppBarLeading()
                        : null,
                titleSpacing: 0,
                title: Row(
                  children: [
                    if (ResponsiveBreakpoints.of(context).largerThan(MOBILE))
                      const Gap(8),
                    IconButton(
                      icon: const Icon(Symbols.shuffle),
                      onPressed: () {
                        GoRouter.of(context).pushNamed('postShuffle');
                      },
                    ),
                    if (ResponsiveBreakpoints.of(context).largerThan(MOBILE))
                      const Gap(48),
                    Expanded(
                      child: Center(
                        child: IconButton(
                          padding: EdgeInsets.zero,
                          constraints: const BoxConstraints(),
                          visualDensity: VisualDensity.compact,
                          icon: _listKey.currentState?.realm != null
                              ? AccountImage(
                                  content: _listKey.currentState!.realm!.avatar,
                                  radius: 14,
                                )
                              : Image.asset(
                                  'assets/icon/icon-dark.png',
                                  width: 32,
                                  height: 32,
                                  color: Theme.of(context)
                                      .appBarTheme
                                      .foregroundColor,
                                ),
                          onPressed: () {
                            showModalBottomSheet(
                              context: context,
                              builder: (context) => _PostListRealmPopup(
                                realms: _realms,
                                onUpdate: (realm) {
                                  _listKey.currentState?.setRealm(realm);
                                  _listKey.currentState?.refreshPosts();
                                  Future.delayed(
                                      const Duration(milliseconds: 100), () {
                                    if (mounted) {
                                      setState(() {});
                                    }
                                  });
                                },
                                onMixedFeedChanged: (flag) {
                                  _listKey.currentState?.setRealm(null);
                                  _listKey.currentState?.setCategory(null);
                                  if (_showCategories && flag) {
                                    _toggleShowCategories();
                                  }
                                  _listKey.currentState?.refreshPosts();
                                },
                              ),
                            );
                          },
                        ),
                      ),
                    ),
                  ],
                ),
                floating: true,
                snap: true,
                actions: [
                  IconButton(
                    icon: const Icon(Symbols.category),
                    style: _showCategories
                        ? ButtonStyle(
                            foregroundColor: WidgetStateProperty.all(
                              Theme.of(context).colorScheme.primary,
                            ),
                            backgroundColor: MaterialStateProperty.all(
                              Theme.of(context).colorScheme.secondaryContainer,
                            ),
                          )
                        : null,
                    onPressed: cfg.mixedFeed
                        ? null
                        : () {
                            _toggleShowCategories();
                          },
                  ),
                  IconButton(
                    icon: const Icon(Symbols.search),
                    onPressed: () {
                      GoRouter.of(context).pushNamed('postSearch');
                    },
                  ),
                  const Gap(8),
                ],
                bottom: cfg.mixedFeed
                    ? null
                    : TabBar(
                        isScrollable: _showCategories,
                        controller: _tabController,
                        tabs: _showCategories
                            ? [
                                for (final category in _categories)
                                  Tab(
                                    child: Row(
                                      mainAxisSize: MainAxisSize.min,
                                      crossAxisAlignment:
                                          CrossAxisAlignment.center,
                                      children: [
                                        Icon(
                                          kCategoryIcons[category.alias] ??
                                              Symbols.question_mark,
                                          color: Theme.of(context)
                                              .appBarTheme
                                              .foregroundColor!,
                                        ),
                                        const Gap(8),
                                        Flexible(
                                          child: Text(
                                            'postCategory${category.alias.capitalize()}'
                                                    .trExists()
                                                ? 'postCategory${category.alias.capitalize()}'
                                                    .tr()
                                                : category.name,
                                            maxLines: 1,
                                          ).textColor(
                                            Theme.of(context)
                                                .appBarTheme
                                                .foregroundColor!,
                                          ),
                                        ),
                                      ],
                                    ),
                                  ),
                              ]
                            : [
                                for (final channel in kPostChannels)
                                  Tab(
                                    child: Row(
                                      mainAxisSize: MainAxisSize.min,
                                      crossAxisAlignment:
                                          CrossAxisAlignment.center,
                                      children: [
                                        Icon(
                                          kPostChannelIcons[
                                              kPostChannels.indexOf(channel)],
                                          size: 20,
                                          color: Theme.of(context)
                                              .appBarTheme
                                              .foregroundColor,
                                        ),
                                        const Gap(8),
                                        Flexible(
                                          child: Text(
                                            'postChannel$channel',
                                            maxLines: 1,
                                          ).tr().textColor(
                                                Theme.of(context)
                                                    .appBarTheme
                                                    .foregroundColor,
                                              ),
                                        ),
                                      ],
                                    ),
                                  ),
                              ],
                      ),
              ),
            ),
          ];
        },
        body: _PostListWidget(
          key: _listKey,
        ),
      ),
    );
  }
}

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

  @override
  State<_PostListWidget> createState() => _PostListWidgetState();
}

class _PostListWidgetState extends State<_PostListWidget> {
  bool _isBusy = false;

  SnRealm? get realm => _selectedRealm;

  final List<SnFeedEntry> _feed = List.empty(growable: true);
  SnRealm? _selectedRealm;
  String? _selectedChannel;
  SnPostCategory? _selectedCategory;
  bool _hasLoadedAll = false;

  // Called when using regular feed
  Future<void> _fetchPosts() async {
    if (_hasLoadedAll) return;

    setState(() => _isBusy = true);

    final pt = context.read<SnPostContentProvider>();
    final result = await pt.listPosts(
      take: 10,
      offset: _feed.length,
      categories: _selectedCategory != null ? [_selectedCategory!.alias] : null,
      channel: _selectedChannel,
      realm: _selectedRealm?.alias,
    );
    final out = result.$1;

    if (!mounted) return;

    final postCount = result.$2;
    _feed.addAll(
      out.map((ele) => SnFeedEntry(
          type: 'interactive.post',
          data: ele.toJson(),
          createdAt: ele.createdAt)),
    );
    _hasLoadedAll = _feed.length >= postCount;

    if (mounted) setState(() => _isBusy = false);
  }

  // Called when mixed feed is enabled
  Future<void> _fetchFeed() async {
    if (_hasLoadedAll) return;

    setState(() => _isBusy = true);

    final pt = context.read<SnPostContentProvider>();
    final result = await pt.getFeed(
      cursor: _feed
          .where((ele) => !['reader.news'].contains(ele.type))
          .lastOrNull
          ?.createdAt,
    );

    if (!mounted) return;

    _feed.addAll(result);
    _hasLoadedAll = result.isEmpty;

    if (mounted) setState(() => _isBusy = false);
  }

  void setChannel(String? channel) {
    _selectedChannel = channel;
    setState(() {});
  }

  void setRealm(SnRealm? realm) {
    _selectedRealm = realm;
    setState(() {});
  }

  void setCategory(SnPostCategory? category) {
    _selectedCategory = category;
    setState(() {});
  }

  Future<void> refreshPosts() {
    _hasLoadedAll = false;
    _feed.clear();
    final cfg = context.read<ConfigProvider>();
    if (cfg.mixedFeed) {
      return _fetchFeed();
    } else {
      return _fetchPosts();
    }
  }

  @override
  void initState() {
    super.initState();
    final cfg = context.read<ConfigProvider>();
    if (cfg.mixedFeed) {
      _fetchFeed();
    } else {
      _fetchPosts();
    }
  }

  @override
  Widget build(BuildContext context) {
    final cfg = context.watch<ConfigProvider>();
    return MediaQuery.removePadding(
      context: context,
      removeTop: true,
      child: RefreshIndicator(
        displacement: 40 + MediaQuery.of(context).padding.top,
        onRefresh: () => refreshPosts(),
        child: InfiniteList(
          padding: EdgeInsets.only(top: 8),
          itemCount: _feed.length,
          isLoading: _isBusy,
          centerLoading: true,
          hasReachedMax: _hasLoadedAll,
          onFetchData: cfg.mixedFeed ? _fetchFeed : _fetchPosts,
          itemBuilder: (context, idx) {
            final ele = _feed[idx];
            switch (ele.type) {
              case 'interactive.post':
                return OpenablePostItem(
                  useReplace: true,
                  data: SnPost.fromJson(ele.data),
                  maxWidth: 640,
                  onChanged: (data) {
                    setState(() {
                      _feed[idx] = _feed[idx].copyWith(data: data.toJson());
                    });
                  },
                  onDeleted: () {
                    refreshPosts();
                  },
                );
              case 'fediverse.post':
                return FediversePostWidget(
                  data: SnFediversePost.fromJson(ele.data),
                  maxWidth: 640,
                );
              case 'reader.news':
                return Center(
                  child: Container(
                    constraints: BoxConstraints(maxWidth: 640),
                    child: NewsFeedEntry(data: ele),
                  ),
                );
              default:
                return Container(
                  constraints: BoxConstraints(maxWidth: 640),
                  child: FeedUnknownEntry(data: ele),
                );
            }
          },
          separatorBuilder: (_, __) => const Divider().padding(vertical: 2),
        ),
      ),
    );
  }
}

class _PostListRealmPopup extends StatelessWidget {
  final List<SnRealm>? realms;
  final Function(SnRealm?) onUpdate;
  final Function(bool) onMixedFeedChanged;

  const _PostListRealmPopup({
    required this.realms,
    required this.onUpdate,
    required this.onMixedFeedChanged,
  });

  @override
  Widget build(BuildContext context) {
    final cfg = context.watch<ConfigProvider>();

    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Row(
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            const Icon(Symbols.tune, size: 24),
            const Gap(16),
            Text('filterFeed', style: Theme.of(context).textTheme.titleLarge)
                .tr(),
          ],
        ).padding(horizontal: 20, top: 16, bottom: 12),
        SwitchListTile(
          secondary: const Icon(Symbols.merge_type),
          contentPadding: const EdgeInsets.symmetric(horizontal: 24),
          title: Text('mixedFeed').tr(),
          subtitle: Text('mixedFeedDescription').tr(),
          value: cfg.mixedFeed,
          onChanged: (value) {
            cfg.mixedFeed = value;
            onMixedFeedChanged.call(value);
          },
        ),
        if (!cfg.mixedFeed)
          ListTile(
            leading: const Icon(Symbols.close),
            title: Text('postInGlobal').tr(),
            subtitle: Text('postViewInGlobalDescription').tr(),
            contentPadding: const EdgeInsets.symmetric(horizontal: 24),
            onTap: () {
              onUpdate.call(null);
              Navigator.pop(context);
            },
          ),
        if (!cfg.mixedFeed) const Divider(height: 1),
        if (!cfg.mixedFeed)
          Expanded(
            child: ListView.builder(
              itemCount: realms?.length ?? 0,
              itemBuilder: (context, idx) {
                final realm = realms![idx];
                return ListTile(
                  title: Text(realm.name),
                  subtitle: Text('@${realm.alias}'),
                  leading: AccountImage(content: realm.avatar, radius: 18),
                  onTap: () {
                    onUpdate.call(realm);
                    Navigator.pop(context);
                  },
                );
              },
            ),
          ),
      ],
    );
  }
}