import 'dart:io';

import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
import 'package:fl_clash/providers/providers.dart';
import 'package:fl_clash/state.dart';
import 'package:fl_clash/widgets/widgets.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

import 'item.dart';

class RequestsView extends ConsumerStatefulWidget {
  const RequestsView({super.key});

  @override
  ConsumerState<RequestsView> createState() => _RequestsViewState();
}

class _RequestsViewState extends ConsumerState<RequestsView> with PageMixin {
  final _requestsStateNotifier = ValueNotifier<ConnectionsState>(
    const ConnectionsState(loading: true),
  );
  List<Connection> _requests = [];
  final _tag = CacheTag.requests;
  late ScrollController _scrollController;
  bool _isLoad = false;

  double _currentMaxWidth = 0;

  @override
  get onSearch => (value) {
        _requestsStateNotifier.value = _requestsStateNotifier.value.copyWith(
          query: value,
        );
      };

  @override
  get onKeywordsUpdate => (keywords) {
        _requestsStateNotifier.value =
            _requestsStateNotifier.value.copyWith(keywords: keywords);
      };

  @override
  void initState() {
    super.initState();
    final preOffset = globalState.cacheScrollPosition[_tag] ?? -1;
    _scrollController = ScrollController(
      initialScrollOffset: preOffset > 0 ? preOffset : double.maxFinite,
    );
    _requests = globalState.appState.requests.list;
    _requestsStateNotifier.value = _requestsStateNotifier.value.copyWith(
      connections: _requests,
    );
    ref.listenManual(
      isCurrentPageProvider(
        PageLabel.requests,
        handler: (pageLabel, viewMode) =>
            pageLabel == PageLabel.tools && viewMode == ViewMode.mobile,
      ),
      (prev, next) {
        if (prev != next && next == true) {
          initPageState();
        }
      },
      fireImmediately: true,
    );
    ref.listenManual(
      requestsProvider.select((state) => state.list),
      (prev, next) {
        if (!connectionListEquality.equals(prev, next)) {
          _requests = next;
          updateRequestsThrottler();
        }
      },
    );
  }

  double _calcCacheHeight(Connection item) {
    final size = globalState.measure.computeTextSize(
      Text(
        item.desc,
        style: context.textTheme.bodyLarge,
      ),
      maxWidth: _currentMaxWidth,
    );
    final chainsText = item.chains.join("");
    final length = item.chains.length;
    final chainSize = globalState.measure.computeTextSize(
      Text(
        chainsText,
        style: context.textTheme.bodyMedium,
      ),
      maxWidth: (_currentMaxWidth - (length - 1) * 6 - length * 24),
    );
    final baseHeight = globalState.measure.bodyMediumHeight;
    final lines = (chainSize.height / baseHeight).round();
    final computerHeight =
        size.height + chainSize.height + 24 + 24 * (lines - 1);
    return computerHeight + 8 + 32 + globalState.measure.bodyMediumHeight;
  }

  @override
  void dispose() {
    _requestsStateNotifier.dispose();
    _scrollController.dispose();
    _currentMaxWidth = 0;
    super.dispose();
  }

  updateRequestsThrottler() {
    throttler.call(FunctionTag.requests, () {
      final isEquality = connectionListEquality.equals(
        _requests,
        _requestsStateNotifier.value.connections,
      );
      if (isEquality) {
        return;
      }
      WidgetsBinding.instance.addPostFrameCallback((_) {
        if (mounted) {
          _requestsStateNotifier.value = _requestsStateNotifier.value.copyWith(
            connections: _requests,
          );
        }
      });
    }, duration: commonDuration);
  }

  _preLoad() {
    if (_isLoad == true) {
      return;
    }
    _isLoad = true;
    WidgetsBinding.instance.addPostFrameCallback((_) async {
      if (!mounted) {
        return;
      }
      final isMobileView = ref.read(isMobileViewProvider);
      if (isMobileView) {
        await Future.delayed(Duration(milliseconds: 300));
      }
      final parts = _requests.batch(10);
      globalState.cacheHeightMap[_tag] ??= FixedMap(
        _requests.length,
      );
      for (int i = 0; i < parts.length; i++) {
        final part = parts[i];
        await Future(
          () {
            for (final request in part) {
              globalState.cacheHeightMap[_tag]?.updateCacheValue(
                request.id,
                () => _calcCacheHeight(request),
              );
            }
          },
        );
      }
      _requestsStateNotifier.value = _requestsStateNotifier.value.copyWith(
        loading: false,
      );
    });
  }

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (_, constraints) {
        return Consumer(
          builder: (_, ref, child) {
            final value = ref.watch(
              patchClashConfigProvider.select(
                (state) =>
                    state.findProcessMode == FindProcessMode.always &&
                    Platform.isAndroid,
              ),
            );
            _currentMaxWidth = constraints.maxWidth - 40 - (value ? 60 : 0);
            return child!;
          },
          child: TextScaleNotification(
            child: ValueListenableBuilder<ConnectionsState>(
              valueListenable: _requestsStateNotifier,
              builder: (_, state, __) {
                _preLoad();
                final connections = state.list;
                final items = connections
                    .map<Widget>(
                      (connection) => ConnectionItem(
                        key: Key(connection.id),
                        connection: connection,
                        onClickKeyword: (value) {
                          context.commonScaffoldState?.addKeyword(value);
                        },
                      ),
                    )
                    .separated(
                      const Divider(
                        height: 0,
                      ),
                    )
                    .toList();
                final content = connections.isEmpty
                    ? NullStatus(
                        label:
                            appLocalizations.nullTip(appLocalizations.requests),
                      )
                    : Align(
                        alignment: Alignment.topCenter,
                        child: ScrollToEndBox(
                          controller: _scrollController,
                          tag: _tag,
                          dataSource: connections,
                          child: CommonScrollBar(
                            controller: _scrollController,
                            child: CacheItemExtentListView(
                              tag: _tag,
                              reverse: true,
                              shrinkWrap: true,
                              physics: NextClampingScrollPhysics(),
                              controller: _scrollController,
                              itemExtentBuilder: (index) {
                                if (index.isOdd) {
                                  return 0;
                                }
                                return _calcCacheHeight(
                                    connections[index ~/ 2]);
                              },
                              itemBuilder: (_, index) {
                                return items[index];
                              },
                              itemCount: items.length,
                              keyBuilder: (int index) {
                                if (index.isOdd) {
                                  return "divider";
                                }
                                return connections[index ~/ 2].id;
                              },
                            ),
                          ),
                        ),
                      );
                return FadeBox(
                  child: state.loading
                      ? Center(
                          child: CircularProgressIndicator(),
                        )
                      : content,
                );
              },
            ),
            onNotification: (_) {
              globalState.cacheHeightMap[_tag]?.clear();
            },
          ),
        );
      },
    );
  }
}
