import 'package:auto_size_text/auto_size_text.dart';
import 'package:design/design.dart';
import 'package:guru_utils/settings/settings.dart';
import 'package:guru_widgets/appbar/guru_app_bar.dart';
import 'package:guru_widgets/banner/purchase_banner.dart';
import 'package:guru_widgets/button/single_tap_widget.dart';
import 'package:guru_widgets/common/divider.dart';
import 'package:guru_widgets/common/flexible_container.dart';
import 'package:guru_widgets/common/spacer.dart';
import 'package:guru_widgets/localizations/widgets_strings.dart';
import 'package:guru_widgets/theme/guru_theme.dart';
import 'package:guru_utils/datetime/datetime_utils.dart';
import 'package:guru_widgets/tile/guru_list_tile.dart';
import 'package:rxdart/rxdart.dart';
import 'package:guru_utils/widget/widget_utils.dart';
import 'package:guru_utils/uri/uri_utils.dart';
import 'package:guru_popup/guru_popup.dart';
import 'package:guru_utils/extensions/extensions.dart';
import 'package:guru_utils/log/log.dart';
import 'package:guru_utils/app_ownership/app_ownership_utils.dart';
import 'package:guru_widgets/pages/webview/guru_webview_page.dart';

export 'package:guru_widgets/pages/settings/guru_settings_page.dart';

part 'guru_settings_page.g.dart';

/// Created by Haoyi on 2023/6/5

abstract class SettingItem {
  final SettingData<bool>? visible;

  SettingItem({this.visible});
}

abstract class InseparableSettingItem extends SettingItem {
  InseparableSettingItem({super.visible});
}

abstract class ComposableSettingItem extends SettingItem {
  ComposableSettingItem({super.visible});
}

mixin CustomDividerAware on InseparableSettingItem {
  bool get hasDivider => true;
}

class SwitchSetting extends InseparableSettingItem {
  final SettingData<bool> settingData;
  final String enabledTitle;
  final String? disabledTitle;
  final String? leading;
  final String? enabledSummary;
  final String? disabledSummary;
  final ValueChanged<bool>? onChanged;

  SwitchSetting(
      {required this.enabledTitle,
      required this.settingData,
      this.disabledTitle,
      this.enabledSummary,
      this.disabledSummary,
      this.leading,
      this.onChanged,
      super.visible});
}

class EntranceSetting extends InseparableSettingItem {
  final String title;
  final String? leading;
  final String? summary;
  final VoidCallback? onTap;
  final TextStyle? overrideTitleStyle;
  final TextStyle? overrideSummaryStyle;

  EntranceSetting(
      {required this.title,
      this.leading,
      this.summary,
      this.onTap,
      this.overrideSummaryStyle,
      this.overrideTitleStyle,
      super.visible});
}

class WidgetSetting extends InseparableSettingItem with CustomDividerAware {
  final Widget widget;
  @override
  final bool hasDivider;

  WidgetSetting({required this.widget, super.visible, this.hasDivider = false});
}

class StreamSetting<T> extends InseparableSettingItem with CustomDividerAware {
  final Stream<T> stream;
  final AsyncWidgetBuilder<T> builder;
  @override
  final bool hasDivider;

  StreamSetting(this.stream, this.builder, {super.visible, this.hasDivider = false});

  Widget build() {
    return StreamBuilder<T>(
      stream: stream,
      builder: builder,
    );
  }
}

class CheckedSetting extends InseparableSettingItem {
  final String title;
  final String? leading;
  final String? summary;
  final String? description;
  final SettingData<bool> settingData;

  CheckedSetting(
      {required this.title,
      required this.settingData,
      this.leading,
      this.summary,
      this.description,
      super.visible});
}

class CheckedItem<T> {
  final String title;
  final String? leading;
  final String? summary;
  final String? description;
  final T value;
  final VoidCallback? onTap;

  CheckedItem(
      {required this.title,
      required this.value,
      this.leading,
      this.summary,
      this.description,
      this.onTap});
}

class CheckedGroupSetting<T> extends SettingItem {
  final bool useDivider;
  final List<CheckedItem<T>> items;
  final SettingData<T> settingData;

  CheckedGroupSetting(
      {required this.items, required this.settingData, this.useDivider = false, super.visible});
}

class SoundSwitchSetting extends InseparableSettingItem {
  final ValueChanged<bool>? onChanged;

  SoundSwitchSetting(this.onChanged, {super.visible});
}

class VibrateSwitchSetting extends InseparableSettingItem {
  final ValueChanged<bool>? onChanged;

  VibrateSwitchSetting(this.onChanged, {super.visible});
}

class GroupSetting extends ComposableSettingItem {
  final String? title;
  final List<InseparableSettingItem> items;

  GroupSetting({this.title, required this.items, super.visible});
}

// typedef ValueFetcher<T, R> = R Function(T value);

// class PurchaseSetting<T> extends SettingItem {
//   final String image;
//   final String summary;
//   final ValueFetcher<T, String> priceFetcher;
//   final Stream<T>? productStream;

//   final VoidCallback? onTap;

//   PurchaseSetting(
//       {required this.priceFetcher, required this.image, required this.summary, this.productStream, this.onTap});
// }

@DesignSpec(width: 750, height: 1624)
abstract class GuruSettingsDesignSpec implements BasicDesignSpec {
  @SpecVertical(24)
  double get defaultTopSpacing;

  @SpecVertical(80)
  double get versionTopSpacing;

  @SpecVertical(22)
  double get versionBottomSpacing;

  @SpecVertical(56)
  double get defaultBottomSpacing;

  @SpecVertical(96)
  double get scrollableBottomSpacing;

  static GuruSettingsDesignSpec get() => _GuruSettingsDesignSpec.get();
}

class GuruVersionWidget extends StatefulWidget {
  final AppOwnerInfo info;
  const GuruVersionWidget({super.key, required this.info});

  @override
  State<StatefulWidget> createState() => GuruVersionWidgetState();
}

class GuruVersionWidgetState extends State<GuruVersionWidget> {
  int lastEnterDebugModeTimestamp = 0;
  int tapVersionCount = 0;
  int lastTapTimestamp = 0;
  final designSpec = GuruSettingsDesignSpec.get();

  void showDebugPageEntrance() {
    final TextEditingController passwordController = TextEditingController();
    GuruPopup.instance.showSimpleAlertDialog(
        barrierDismissible: false,
        title: "Instructions",
        positiveText: "Enter Password",
        onPositive: () {
          final now = DateTime.now();
          if (passwordController.text == "112358${DateTimeUtils.twoDigits(now.hour)}") {
            lastEnterDebugModeTimestamp = DateTimeUtils.currentTimeInMillis();
            Settings.get().debugMode.set(true);
          } else if (passwordController.text == 'trademark') {
            
          } else if (passwordController.text == 'legal') {
            GuruPopup.instance.showBitmapDialog(info: widget.info);
          } else if (passwordController.text == "853211") {
            Settings.get().debugMode.set(false);
          } else {
            GuruPopup.instance.showCommonToast("wrong password");
          }
        },
        content: TextField(
          controller: passwordController,
          decoration: const InputDecoration(
              labelText: "Password", hintText: "Enter Your Password", prefixIcon: Icon(Icons.lock)),
          obscureText: true,
        ));
  }

  @override
  Widget build(BuildContext context) {
    final guruTheme = GuruTheme.of(Get.context!);
    final colorScheme = guruTheme.colorScheme;
    return GestureDetector(
      onTap: () {
        final now = DateTimeUtils.currentTimeInMillis();
        if (now - lastTapTimestamp < 800) {
          tapVersionCount++;
          if (tapVersionCount > 9) {
            final lastInterval = now - lastEnterDebugModeTimestamp;
            if (lastInterval < 600000) {
              GuruPopup.instance.showCommonToast("already enabled debug mode");
            } else {
              showDebugPageEntrance();
            }
          }
        } else {
          tapVersionCount = 0;
        }
        lastTapTimestamp = now;
      },
      child: Container(
        margin: EdgeInsets.only(top: designSpec.versionTopSpacing),
        alignment: Alignment.topCenter,
        child: StreamBuilder<String>(
          stream: Rx.combineLatest2(
              Settings.get().version.observe(),
              Settings.get().buildNumber.observe(),
              (version, buildNumber) => "Version $version-$buildNumber"),
          builder: (context, snapshot) {
            return InkWell(
              child: Text(
                snapshot.data ?? "Version",
                style: TextStyle(
                    fontSize: 12,
                    color: colorScheme.secondaryColor ?? const Color(0xffa2a2a2),
                    fontWeight: FontWeight.w400),
              ),
            );
          },
        ),
      ),
    );
  }
}

class GuruSettingsPage extends StatefulWidget {
  final Uri? policyUrl;
  final Uri? termsOfServiceUrl;
  final List<SettingItem> items;
  final LeadingType leadingType;
  final Color? backgroundColor;
  final VoidCallback? onEnterDebugView;
  final VoidCallback? onTestTap;
  final VoidCallback? onPolicyTap;
  final bool subpage;
  final AppOwnerInfo appOwnerinfo;

  GuruSettingsPage(
      {Key? key,
      required List<SettingItem> items,
      this.policyUrl,
      this.termsOfServiceUrl,
      this.leadingType = LeadingType.close,
      this.backgroundColor,
      this.subpage = false,
      this.onEnterDebugView,
      this.onTestTap,
      this.onPolicyTap,
      this.appOwnerinfo = const AppOwnerInfo('')})
      : items = rebuildItems(items),
        super(key: key);

  // 这里添加这个逻辑是为了保证在没有group的item可以放到一个统一的组里。然后进行背景渲染
  // 因为单独添加divider时bg的颜色会和background的颜色有冲突没办法完美的覆盖
  static List<SettingItem> rebuildItems(List<SettingItem> items) {
    final List<InseparableSettingItem> autoGroupItems = [];
    final List<SettingItem> resultItems = [];
    for (var i = 0; i < items.length; i++) {
      final SettingItem item = items[i];
      final autoGroup =
          (item is InseparableSettingItem) && (item is! CustomDividerAware || item.hasDivider);
      if (autoGroup) {
        autoGroupItems.add(item);
      } else {
        if (autoGroupItems.isNotEmpty) {
          if (autoGroupItems.length == 1) {
            resultItems.add(autoGroupItems[0]);
          } else {
            resultItems.add(GroupSetting(items: autoGroupItems.toList()));
          }
          autoGroupItems.clear();
        }
        resultItems.add(item);
      }
    }
    if (autoGroupItems.isNotEmpty) {
      if (autoGroupItems.length == 1) {
        resultItems.add(autoGroupItems[0]);
      } else {
        resultItems.add(GroupSetting(items: autoGroupItems.toList()));
      }
      autoGroupItems.clear();
    }
    return resultItems;
  }

  @override
  State<StatefulWidget> createState() => GuruSettingsPageState._();
}

class GuruSettingsPageState extends State<GuruSettingsPage> {
  final ScrollController _controller = ScrollController();

  final BehaviorSubject<bool> scrollable = BehaviorSubject.seeded(false);

  Stream<bool> get observableScrollable => scrollable.stream;

  late GuruSettingsDesignSpec designSpec;

  GuruSettingsPageState._();

  @override
  void initState() {
    super.initState();
    _controller.addListener(processScroll);
  }

  void processScroll() {
    if (_controller.hasClients &&
        _controller.position.maxScrollExtent > _controller.position.minScrollExtent) {
      scrollable.addIfChanged(true);
    }
  }

  @override
  void dispose() {
    scrollable.close();
    _controller.removeListener(processScroll);
    _controller.dispose();
    super.dispose();
  }

  HorizontalDivider buildDivider() {
    final guruTheme = GuruTheme.of(Get.context!);
    final colorScheme = guruTheme.colorScheme;
    final tileTheme = guruTheme.listTileTheme;
    final designSpec = guruTheme.designSpec.pageListTileDesignSpec;
    return HorizontalDivider(
      height: designSpec.dividerHeight,
      thickness: designSpec.dividerHeight,
      indent: designSpec.dividerIndent,
      color: tileTheme.dividerColor ?? colorScheme.dividerColor ?? Colors.white.withOpacity(0.14),
      bgColor: tileTheme.backgroundColor ?? colorScheme.containerColor ?? Colors.white,
    );
  }

  Widget buildRootWidget({
    required BuildContext context,
    Color? backgroundColor,
    required String title,
    Color? appbarColor,
    required Widget body,
  }) {
    final guruTheme = GuruTheme.of(context);
    final colorScheme = guruTheme.colorScheme;
    final appStrings = WidgetsStrings.get();
    return Scaffold(
      backgroundColor: backgroundColor ?? colorScheme.backgroundColor,
      appBar: GuruAppBar(title: appStrings.settings, leadingType: widget.leadingType),
      body: body,
    );
  }

  Widget _version() {
    return GuruVersionWidget(info: widget.appOwnerinfo);
  }

  Widget _buildPolicyLinks() {
    final appStrings = WidgetsStrings.get();
    return SizedBox(
        child: Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        _textLink(appStrings.privacyPolicy, widget.policyUrl.toString(), onAfterTap: () {
          // GuruAnalytics.instance
          //     .logEventEx("settings", itemName: "privacy_clk");
          final uri = widget.policyUrl;
          if (uri != null) {
            widget.onPolicyTap?.call();

            GuruPopup.instance.showDialog(widget: GuruWebviewPage(url: uri, title: appStrings.privacyPolicy,), useSafeArea: false);
            // UriUtils.launchURL(uri);
          }
        }),
        const FlexibleContainer(
          width: 0.5,
          height: 10,
          color: Color(0xffD6D6D8),
        ),
        _textLink(appStrings.termsOfService, widget.termsOfServiceUrl.toString(), onAfterTap: () {
          // GuruAnalytics.instance
          //     .logEventEx("settings", itemName: "tos_clk");
          final uri = widget.termsOfServiceUrl;
          if (uri != null) {
            GuruPopup.instance.showDialog(widget: GuruWebviewPage(url: uri, title: appStrings.termsOfService,), useSafeArea: false);
            // UriUtils.launchURL(uri);
          }
        }),
      ],
    ));
  }

  Widget _textLink(String text, String url, {VoidCallback? onAfterTap}) {
    final guruTheme = GuruTheme.of(Get.context!);
    final colorScheme = guruTheme.colorScheme;
    final themeData = Theme.of(Get.context!);
    return TapWidget(
      child: Container(
          padding: const EdgeInsets.symmetric(horizontal: 8),
          child: AutoSizeText(
            text,
            minFontSize: 6,
            style:
                TextStyle(fontSize: 11, color: colorScheme.primaryColor ?? themeData.primaryColor),
          )),
      onTap: () {
        onAfterTap?.call();
      },
    );
  }

  Widget _buildEntrance(EntranceSetting setting) {
    final guruTheme = GuruTheme.of(Get.context!);
    return GuruListTile(
      title: setting.title,
      summary: setting.summary,
      leading: setting.leading,
      trailing: guruTheme.iconScheme.entranceIcon,
      titleTextStyle: setting.overrideTitleStyle,
      onTap: setting.onTap,
    );
  }

  Widget _buildCheckedItem(CheckedItem item, bool checked) {
    final checkedTile = GuruCheckedListTile(
      title: item.title,
      summary: item.summary,
      checked: checked,
      leading: item.leading,
      onTap: item.onTap,
    );
    return (item.description != null)
        ? Column(
            children: [
              checkedTile,
              _buildDescription(item.description!),
            ],
          )
        : checkedTile;
  }

  Widget _buildDescription(String description) {
    final guruTheme = GuruTheme.of(Get.context!);
    final designSpec = guruTheme.designSpec.pageListTileDesignSpec;
    final contentColor = guruTheme.listTileTheme.secondaryContentColor ??
        guruTheme.colorScheme.secondaryContentColor;
    return Padding(
      padding: designSpec.descriptionPadding,
      child: Text(
        description,
        style: TextStyle(
          fontSize: designSpec.descriptionFontSize,
          color: contentColor,
        ),
      ),
    );
  }

  Widget _buildChecked(CheckedSetting setting) {
    return StreamBuilder<bool>(
        stream: setting.settingData.observe(),
        builder: (context, snapshot) {
          final checked = snapshot.data ?? false;
          final checkedTile = GuruCheckedListTile(
            title: setting.title,
            summary: setting.summary,
            checked: checked,
            leading: setting.leading,
            onTap: () {
              setting.settingData.set(!checked);
            },
          );
          return (setting.description != null)
              ? Column(
                  children: [
                    checkedTile,
                    _buildDescription(setting.description!),
                  ],
                )
              : checkedTile;
        });
  }

  Widget _buildCheckedGroup(CheckedGroupSetting setting) {
    return StreamBuilder<dynamic>(
        stream: setting.settingData.observe(),
        builder: (context, snapshot) {
          final items = setting.items;
          final selected = snapshot.data;
          final List<Widget> tiles = [];
          for (var item in items) {
            tiles.add(_buildCheckedItem(item, selected == item.value));
          }
          Widget? divider;
          if (setting.useDivider) {
            divider = buildDivider();
          }
          return Column(children: WidgetUtils.separate(tiles, divider));
        });
  }

  Widget _buildStream(StreamSetting setting) {
    return setting.build();
  }

  Widget _buildWidget(WidgetSetting setting) {
    return setting.widget;
  }

  Widget _buildSwitch(SwitchSetting setting) {
    return StreamBuilder<bool>(
        stream: setting.settingData.observe(),
        builder: (context, snapshot) {
          return GuruSwitchListTile(
            title: snapshot.data == true
                ? setting.enabledTitle
                : (setting.disabledTitle ?? setting.enabledTitle),
            leading: setting.leading,
            enabled: snapshot.data == true,
            onChanged: setting.onChanged,
          );
        });
  }

  Widget? _buildItem(SettingItem item) {
    if (item is GroupSetting) {
      return _buildGroup(item);
    } else if (item is EntranceSetting) {
      return _buildEntrance(item);
    } else if (item is SwitchSetting) {
      return _buildSwitch(item);
    } else if (item is CheckedSetting) {
      return _buildChecked(item);
    } else if (item is CheckedGroupSetting) {
      return _buildCheckedGroup(item);
    } else if (item is StreamSetting) {
      return _buildStream(item);
    } else if (item is WidgetSetting) {
      return _buildWidget(item);
    }
    return null;
  }

  Widget? _checkVisibleAndBuildItem(SettingItem item) {
    if (item.visible != null) {
      return StreamBuilder<bool>(
          stream: item.visible!.observe(),
          builder: (context, snapshot) {
            if (snapshot.data == true) {
              return _buildItem(item) ?? const EmptySpacer();
            } else {
              return const EmptySpacer();
            }
          });
    } else {
      return _buildItem(item) ?? const EmptySpacer();
    }
  }

  Widget _buildGroup(GroupSetting group) {
    final List<Widget> tiles = [];
    for (final item in group.items) {
      final itemWidget = _checkVisibleAndBuildItem(item);
      if (itemWidget == null || itemWidget is EmptySpacer) {
        continue;
      }
      tiles.add(itemWidget);
    }

    final divider = buildDivider();
    return GuruListTileGroup(title: group.title, tiles: WidgetUtils.separate(tiles, divider));
  }

  List<Widget> buildItems() {
    final List<Widget> widgets = [];
    final divider = buildDivider();
    final int itemsLength = widget.items.length;

    for (var i = 0; i < itemsLength; i++) {
      final SettingItem item = widget.items[i];
      final itemWidget = _checkVisibleAndBuildItem(item);
      if (itemWidget == null || itemWidget is EmptySpacer) {
        continue;
      }
      widgets.add(itemWidget);

      if (i < itemsLength - 1) {
        final SettingItem nextItem = widget.items[i + 1];
        if (nextItem is! GroupSetting) {
          widgets.add(divider);
          if (item is WidgetSetting) {
            if (!item.hasDivider) {
              widgets.removeLast();
            }
          }
        }
      }
    }

    final lastItem = widgets[widgets.length - 1];
    if (lastItem is HorizontalDivider) {
      widgets.removeLast();
    }

    return widgets;
  }

  Widget _buildBottom() {
    final double bottomPadding = MediaQuery.of(context).padding.bottom;
    return Column(
      children: <Widget>[
        _version(),
        SizedBox(height: designSpec.versionBottomSpacing),
        _buildPolicyLinks(),
        StreamBuilder<bool>(
          stream: observableScrollable,
          builder: (context, snapshot) {
            if (snapshot.data == true) {
              return SizedSpacer(height: designSpec.scrollableBottomSpacing);
            } else {
              return SizedSpacer(height: designSpec.defaultBottomSpacing);
            }
          },
        ),
        SizedBox(height: bottomPadding),
      ],
    );
  }

  Widget _buildDebug() {
    const textStyle = TextStyle(
        color: Colors.orangeAccent,
        fontSize: 18,
        fontStyle: FontStyle.italic,
        fontWeight: GuruTheme.fwBold);
    return StreamBuilder<bool>(
        stream: Settings.get().debugMode.observe(),
        builder: (context, snapshot) {
          final enabled = snapshot.data ?? false;
          if (enabled) {
            return _buildGroup(GroupSetting(title: "Development", items: [
              EntranceSetting(
                  title: "Debug Page",
                  overrideTitleStyle: textStyle,
                  onTap: widget.onEnterDebugView),
              EntranceSetting(
                  title: "Test Tap", overrideTitleStyle: textStyle, onTap: widget.onTestTap)
            ]));
          } else {
            return const EmptySpacer();
          }
        });
  }

  Widget _buildSettingItems() {
    return Column(
      children: [
        SizedSpacer(height: designSpec.defaultTopSpacing),
        ...buildItems(),
        if (!widget.subpage) ...[_buildDebug()]
      ],
    );
  }

  Widget buildBody() {
    designSpec = GuruSettingsDesignSpec.get();
    return LayoutBuilder(
        builder: (context, constraints) => SingleChildScrollView(
            controller: _controller,
            scrollDirection: Axis.vertical,
            child: ConstrainedBox(
              constraints: BoxConstraints(minHeight: constraints.maxHeight),
              child: Column(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  ...[_buildSettingItems()],
                  if (!widget.subpage) ...[_buildBottom()]
                ],
              ),
            )));
  }

  @override
  Widget build(BuildContext context) {
    final appStrings = WidgetsStrings.get();
    return buildRootWidget(
        context: context,
        title: appStrings.settings,
        backgroundColor: widget.backgroundColor,
        body: buildBody());
  }
}
