import 'dart:async';

import 'package:copy_with/copy_with.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_colorpicker/flutter_colorpicker.dart';
import 'package:logging/logging.dart';
import 'package:nc_photos/app_localizations.dart';
import 'package:nc_photos/bloc_util.dart';
import 'package:nc_photos/controller/pref_controller.dart';
import 'package:nc_photos/exception_event.dart';
import 'package:nc_photos/exception_util.dart' as exception_util;
import 'package:nc_photos/k.dart' as k;
import 'package:nc_photos/mobile/android/android_info.dart';
import 'package:nc_photos/object_extension.dart';
import 'package:nc_photos/session_storage.dart';
import 'package:nc_photos/snack_bar_manager.dart';
import 'package:nc_photos/theme.dart';
import 'package:nc_photos/widget/page_visibility_mixin.dart';
import 'package:np_codegen/np_codegen.dart';
import 'package:np_platform_util/np_platform_util.dart';
import 'package:to_string/to_string.dart';

part 'theme/bloc.dart';
part 'theme/state_event.dart';
part 'theme_settings.g.dart';

// typedef _BlocBuilder = BlocBuilder<_Bloc, _State>;
typedef _BlocListener = BlocListener<_Bloc, _State>;
typedef _BlocSelector<T> = BlocSelector<_Bloc, _State, T>;

class ThemeSettings extends StatelessWidget {
  const ThemeSettings({super.key});

  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (_) => _Bloc(
        prefController: context.read(),
      ),
      child: const _WrappedThemeSettings(),
    );
  }
}

class _WrappedThemeSettings extends StatefulWidget {
  const _WrappedThemeSettings();

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

@npLog
class _WrappedThemeSettingsState extends State<_WrappedThemeSettings>
    with RouteAware, PageVisibilityMixin {
  @override
  void initState() {
    super.initState();
    _bloc.add(const _Init());
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: MultiBlocListener(
        listeners: [
          _BlocListener(
            listenWhen: (previous, current) => previous.error != current.error,
            listener: (context, state) {
              if (state.error != null && isPageVisible()) {
                SnackBarManager().showSnackBar(SnackBar(
                  content:
                      Text(exception_util.toUserString(state.error!.error)),
                  duration: k.snackBarDurationNormal,
                ));
              }
            },
          ),
        ],
        child: CustomScrollView(
          slivers: [
            SliverAppBar(
              pinned: true,
              title: Text(L10n.global().settingsThemeTitle),
            ),
            SliverList(
              delegate: SliverChildListDelegate(
                [
                  const _SeedColorOption(),
                  if (getRawPlatform() == NpPlatform.android &&
                      AndroidInfo().sdkInt >= AndroidVersion.Q)
                    _BlocSelector<bool>(
                      selector: (state) => state.isFollowSystemTheme,
                      builder: (_, isFollowSystemTheme) {
                        return SwitchListTile(
                          title: Text(
                              L10n.global().settingsFollowSystemThemeTitle),
                          value: isFollowSystemTheme,
                          onChanged: (value) {
                            _bloc.add(_SetFollowSystemTheme(value));
                          },
                        );
                      },
                    ),
                  _BlocSelector<bool>(
                    selector: (state) => state.isUseBlackInDarkTheme,
                    builder: (context, isUseBlackInDarkTheme) {
                      return SwitchListTile(
                        title: Text(
                            L10n.global().settingsUseBlackInDarkThemeTitle),
                        subtitle: Text(isUseBlackInDarkTheme
                            ? L10n.global()
                                .settingsUseBlackInDarkThemeTrueDescription
                            : L10n.global()
                                .settingsUseBlackInDarkThemeFalseDescription),
                        value: isUseBlackInDarkTheme,
                        onChanged: (value) {
                          _bloc.add(_SetUseBlackInDarkTheme(
                              value, Theme.of(context)));
                        },
                      );
                    },
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }

  late final _bloc = context.read<_Bloc>();
}

class _SeedColorOption extends StatelessWidget {
  const _SeedColorOption();

  @override
  Widget build(BuildContext context) {
    return _BlocSelector<int?>(
      selector: (state) => state.seedColor,
      builder: (context, seedColor) {
        if (SessionStorage().isSupportDynamicColor) {
          return ListTile(
            title: Text(L10n.global().settingsSeedColorTitle),
            subtitle: Text(seedColor == null
                ? L10n.global().settingsSeedColorSystemColorDescription
                : L10n.global().settingsSeedColorDescription),
            trailing: seedColor == null
                ? null
                : Icon(
                    Icons.circle,
                    size: 32,
                    color: Color(seedColor),
                  ),
            onTap: () => _onSeedColorPressed(context),
          );
        } else {
          return ListTile(
            title: Text(L10n.global().settingsSeedColorTitle),
            subtitle: Text(L10n.global().settingsSeedColorDescription),
            trailing: Icon(
              Icons.circle,
              size: 32,
              color: seedColor?.run(Color.new) ?? defaultSeedColor,
            ),
            onTap: () => _onSeedColorPressed(context),
          );
        }
      },
    );
  }

  Future<void> _onSeedColorPressed(BuildContext context) async {
    final result = await showDialog<int>(
      context: context,
      builder: (context) => const _SeedColorPicker(),
    );
    if (result == null) {
      return;
    }
    if (context.mounted) {
      context
          .read<_Bloc>()
          .add(_SetSeedColor(result == -1 ? null : Color(result)));
    }
  }
}

class _SeedColorPicker extends StatefulWidget {
  const _SeedColorPicker();

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

class _SeedColorPickerState extends State<_SeedColorPicker> {
  @override
  Widget build(BuildContext context) {
    return Visibility(
      visible: _isVisible,
      child: AlertDialog(
        title: Text(L10n.global().settingsSeedColorPickerTitle),
        content: Wrap(
          children: const [
            Color(0xFFF44336),
            Color(0xFF9C27B0),
            Color(0xFF2196F3),
            Color(0xFF4CAF50),
            Color(0xFFFFC107),
            null,
          ]
              .map((c) => _SeedColorPickerItem(
                    seedColor: c,
                    onSelected: () => _onItemSelected(context, c?.value),
                  ))
              .toList(),
        ),
        actions: SessionStorage().isSupportDynamicColor
            ? [
                TextButton(
                  onPressed: () => _onItemSelected(context, -1),
                  child: Text(L10n.global()
                      .settingsSeedColorPickerSystemColorButtonLabel),
                ),
              ]
            : null,
      ),
    );
  }

  Future<void> _onItemSelected(BuildContext context, int? seedColor) async {
    if (seedColor != null) {
      Navigator.of(context).pop(seedColor);
      return;
    }
    setState(() {
      _isVisible = false;
    });
    final color = await showDialog<Color>(
      context: context,
      builder: (_) => const _SeedColorCustomPicker(),
      barrierColor: Colors.transparent,
    );
    Navigator.of(context).pop(color?.value);
  }

  var _isVisible = true;
}

class _SeedColorCustomPicker extends StatefulWidget {
  const _SeedColorCustomPicker();

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

class _SeedColorCustomPickerState extends State<_SeedColorCustomPicker> {
  @override
  Widget build(BuildContext context) {
    return AlertDialog(
      title: Text(L10n.global().settingsSeedColorPickerTitle),
      content: SingleChildScrollView(
        child: _HueRingPicker(
          pickerColor: _customColor,
          onColorChanged: (value) {
            setState(() {
              _customColor = value;
            });
          },
        ),
      ),
      actions: [
        TextButton(
          onPressed: () {
            Navigator.of(context).pop(_customColor);
          },
          child: Text(L10n.global().applyButtonLabel),
        ),
      ],
    );
  }

  late var _customColor = getSeedColor(context) ?? defaultSeedColor;
}

class _SeedColorPickerItem extends StatelessWidget {
  const _SeedColorPickerItem({
    required this.seedColor,
    this.onSelected,
  });

  @override
  Widget build(BuildContext context) {
    final content = SizedBox.square(
      dimension: _size,
      child: Center(
        child: seedColor != null
            ? Icon(
                Icons.circle,
                size: _size * .9,
                color: seedColor,
              )
            : Transform.scale(
                scale: .9,
                child: Stack(
                  alignment: Alignment.center,
                  children: [
                    Image.asset("assets/ic_custom_color_56dp.png"),
                    const Icon(
                      Icons.colorize_outlined,
                      size: _size * .5,
                      color: Colors.black87,
                    ),
                  ],
                ),
              ),
      ),
    );
    if (onSelected != null) {
      return InkWell(
        customBorder: const CircleBorder(),
        onTap: onSelected,
        child: content,
      );
    } else {
      return content;
    }
  }

  final Color? seedColor;
  final VoidCallback? onSelected;

  static const _size = 56.0;
}

/// Based on the original HueRingPicker
class _HueRingPicker extends StatefulWidget {
  const _HueRingPicker({
    Key? key,
    required this.pickerColor,
    required this.onColorChanged,
    // ignore: unused_element
    this.colorPickerHeight = 250.0,
    // ignore: unused_element
    this.hueRingStrokeWidth = 20.0,
    // ignore: unused_element
    this.displayThumbColor = true,
    // ignore: unused_element
    this.pickerAreaBorderRadius = const BorderRadius.all(Radius.zero),
  }) : super(key: key);

  final Color pickerColor;
  final ValueChanged<Color> onColorChanged;
  final double colorPickerHeight;
  final double hueRingStrokeWidth;
  final bool displayThumbColor;
  final BorderRadius pickerAreaBorderRadius;

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

class _HueRingPickerState extends State<_HueRingPicker> {
  HSVColor currentHsvColor = const HSVColor.fromAHSV(0.0, 0.0, 0.0, 0.0);

  @override
  void initState() {
    currentHsvColor = HSVColor.fromColor(widget.pickerColor);
    super.initState();
  }

  @override
  void didUpdateWidget(_HueRingPicker oldWidget) {
    super.didUpdateWidget(oldWidget);
    currentHsvColor = HSVColor.fromColor(widget.pickerColor);
  }

  void onColorChanging(HSVColor color) {
    setState(() => currentHsvColor = color.withSaturation(1).withValue(1));
    widget.onColorChanged(currentHsvColor.toColor());
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: <Widget>[
        ClipRRect(
          borderRadius: widget.pickerAreaBorderRadius,
          child: Padding(
            padding: const EdgeInsets.all(18),
            child: Stack(
              alignment: AlignmentDirectional.center,
              children: <Widget>[
                ColorIndicator(
                  currentHsvColor,
                  width: 128,
                  height: 128,
                ),
                SizedBox(
                  width: widget.colorPickerHeight,
                  height: widget.colorPickerHeight,
                  child: ColorPickerHueRing(
                    currentHsvColor,
                    onColorChanging,
                    displayThumbColor: widget.displayThumbColor,
                    strokeWidth: 26,
                  ),
                ),
              ],
            ),
          ),
        ),
      ],
    );
  }
}