import 'dart:async';
import 'dart:math';

import 'package:cached_network_image/cached_network_image.dart';
import 'package:clock/clock.dart';
import 'package:copy_with/copy_with.dart';
import 'package:event_bus/event_bus.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:kiwi/kiwi.dart';
import 'package:logging/logging.dart';
import 'package:mutex/mutex.dart';
import 'package:nc_photos/account.dart';
import 'package:nc_photos/api/api_util.dart' as api_util;
import 'package:nc_photos/app_localizations.dart';
import 'package:nc_photos/bloc_util.dart';
import 'package:nc_photos/controller/account_controller.dart';
import 'package:nc_photos/di_container.dart';
import 'package:nc_photos/entity/pref.dart';
import 'package:nc_photos/entity/server_status.dart';
import 'package:nc_photos/entity/sqlite/database.dart' as sql;
import 'package:nc_photos/event/event.dart';
import 'package:nc_photos/exception_event.dart';
import 'package:nc_photos/exception_util.dart' as exception_util;
import 'package:nc_photos/help_utils.dart' as help_util;
import 'package:nc_photos/k.dart' as k;
import 'package:nc_photos/theme.dart';
import 'package:nc_photos/toast.dart';
import 'package:nc_photos/url_launcher_util.dart';
import 'package:nc_photos/widget/home.dart';
import 'package:nc_photos/widget/settings.dart';
import 'package:nc_photos/widget/settings/account_settings.dart';
import 'package:nc_photos/widget/sign_in.dart';
import 'package:np_codegen/np_codegen.dart';
import 'package:to_string/to_string.dart';

part 'account_picker_dialog.g.dart';
part 'account_picker_dialog/bloc.dart';
part 'account_picker_dialog/state_event.dart';

typedef _BlocBuilder = BlocBuilder<_Bloc, _State>;
typedef _BlocListener = BlocListener<_Bloc, _State>;

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

  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (context) => _Bloc(
        container: KiwiContainer().resolve(),
        accountController: context.read(),
      ),
      child: const _WrappedAccountPickerDialog(),
    );
  }
}

class _WrappedAccountPickerDialog extends StatelessWidget {
  const _WrappedAccountPickerDialog();

  @override
  Widget build(BuildContext context) {
    return MultiBlocListener(
      listeners: [
        _BlocListener(
          listenWhen: (previous, current) =>
              previous.newSelectAccount != current.newSelectAccount,
          listener: (context, state) {
            if (state.newSelectAccount != null) {
              Navigator.of(context).pushNamedAndRemoveUntil(
                Home.routeName,
                (_) => false,
                arguments: HomeArguments(state.newSelectAccount!),
              );
            }
          },
        ),
        _BlocListener(
          listenWhen: (previous, current) => previous.error != current.error,
          listener: (context, state) {
            if (state.error != null) {
              AppToast.showToast(
                context,
                msg: exception_util.toUserString(state.error!.error),
                duration: k.snackBarDurationNormal,
              );
            }
          },
        ),
      ],
      child: Dialog(
        child: Padding(
          padding: const EdgeInsets.all(8),
          child: ClipRRect(
            borderRadius: BorderRadius.circular(24),
            child: SingleChildScrollView(
              child: Column(
                mainAxisSize: MainAxisSize.min,
                children: [
                  const SizedBox(height: 8),
                  Stack(
                    children: [
                      Positioned.fill(
                        child: Center(
                          child: Text(
                            L10n.global().appTitle,
                            style: Theme.of(context).textTheme.headlineSmall,
                          ),
                        ),
                      ),
                      if (!Pref().isFollowSystemThemeOr(false))
                        Align(
                          alignment: AlignmentDirectional.centerEnd,
                          child: _DarkModeSwitch(
                            onChanged: _onDarkModeChanged,
                          ),
                        ),
                    ],
                  ),
                  const SizedBox(height: 8),
                  ClipRRect(
                    borderRadius: BorderRadius.circular(24),
                    child: Container(
                      color: Theme.of(context).colorScheme.background,
                      child: Material(
                        type: MaterialType.transparency,
                        child: _BlocBuilder(
                          buildWhen: (previous, current) =>
                              previous.isOpenDropdown !=
                                  current.isOpenDropdown ||
                              previous.accounts != current.accounts,
                          builder: (context, state) {
                            final bloc = context.read<_Bloc>();
                            return Column(
                              mainAxisSize: MainAxisSize.min,
                              children: [
                                const _AccountDropdown(),
                                if (state.isOpenDropdown) ...[
                                  ...state.accounts
                                      .where(
                                          (a) => a.id != bloc.activeAccount.id)
                                      .map((a) => _AccountView(account: a)),
                                  const _NewAccountView(),
                                ] else
                                  const _AccountSettingsView(),
                              ],
                            );
                          },
                        ),
                      ),
                    ),
                  ),
                  _IconTile(
                    icon: const Icon(Icons.settings_outlined),
                    title: Text(L10n.global().settingsMenuLabel),
                    onTap: () {
                      Navigator.of(context)
                        ..pop()
                        ..pushNamed(
                          Settings.routeName,
                          arguments: SettingsArguments(
                              context.read<_Bloc>().activeAccount),
                        );
                    },
                  ),
                  _IconTile(
                    icon: const Icon(Icons.help_outline),
                    title: Text(L10n.global().helpTooltip),
                    onTap: () {
                      Navigator.of(context).pop();
                      launch(help_util.mainUrl);
                    },
                  ),
                  const _AboutChin(),
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }

  void _onDarkModeChanged(bool value) {
    Pref().setDarkTheme(value).then((_) {
      KiwiContainer().resolve<EventBus>().fire(ThemeChangedEvent());
    });
  }
}

class _DarkModeSwitch extends StatelessWidget {
  const _DarkModeSwitch({
    this.onChanged,
  });

  @override
  Widget build(BuildContext context) {
    return DarkModeSwitchTheme(
      child: Switch(
        value: Theme.of(context).brightness == Brightness.dark,
        onChanged: onChanged,
        activeThumbImage:
            const AssetImage("assets/ic_dark_mode_switch_24dp.png"),
        inactiveThumbImage:
            const AssetImage("assets/ic_dark_mode_switch_24dp.png"),
      ),
    );
  }

  final ValueChanged<bool>? onChanged;
}

class _AccountDropdown extends StatelessWidget {
  const _AccountDropdown();

  @override
  Widget build(BuildContext context) {
    return _AccountTile(
      account: context.read<_Bloc>().activeAccount,
      trailing: _BlocBuilder(
        builder: (_, state) {
          return AnimatedRotation(
            turns: state.isOpenDropdown ? .5 : 0,
            duration: k.animationDurationShort,
            child: Icon(
              Icons.keyboard_arrow_down_outlined,
              color: state.isOpenDropdown
                  ? Theme.of(context).colorScheme.primary
                  : null,
            ),
          );
        },
      ),
      onTap: () {
        context.read<_Bloc>().add(const _ToggleDropdown());
      },
    );
  }
}

class _AccountTile extends StatelessWidget {
  const _AccountTile({
    required this.account,
    this.trailing,
    this.onTap,
  });

  @override
  Widget build(BuildContext context) {
    final accountLabel = AccountPref.of(account).getAccountLabel();
    return ListTile(
      leading: SizedBox.square(
        dimension: 40,
        child: Center(child: _AccountIcon(account)),
      ),
      title: accountLabel != null
          ? SizedBox(
              height: 64,
              child: Align(
                alignment: AlignmentDirectional.centerStart,
                child: Text(
                  accountLabel,
                  maxLines: 1,
                  overflow: TextOverflow.clip,
                ),
              ),
            )
          : Text(
              account.address,
              maxLines: 1,
              overflow: TextOverflow.clip,
            ),
      subtitle: accountLabel == null ? Text(account.username2) : null,
      trailing: trailing,
      onTap: onTap,
    );
  }

  final Account account;
  final Widget? trailing;
  final VoidCallback? onTap;
}

class _AccountIcon extends StatelessWidget {
  const _AccountIcon(this.account);

  @override
  Widget build(BuildContext context) {
    return ClipRRect(
      borderRadius: BorderRadius.circular(24),
      child: CachedNetworkImage(
        imageUrl: api_util.getAccountAvatarUrl(account, 64),
        fadeInDuration: const Duration(),
        filterQuality: FilterQuality.high,
      ),
    );
  }

  final Account account;
}

class _IconTile extends StatelessWidget {
  const _IconTile({
    required this.icon,
    required this.title,
    this.onTap,
  });

  @override
  Widget build(BuildContext context) {
    return ListTile(
      leading: SizedBox.square(
        dimension: 40,
        child: Center(child: icon),
      ),
      title: title,
      onTap: onTap,
    );
  }

  final Widget icon;
  final Widget title;
  final VoidCallback? onTap;
}

class _AccountView extends StatelessWidget {
  const _AccountView({
    required this.account,
  });

  @override
  Widget build(BuildContext context) {
    return _AccountTile(
      account: account,
      trailing: IconButton(
        icon: const Icon(Icons.close),
        tooltip: L10n.global().deleteTooltip,
        onPressed: () {
          context.read<_Bloc>().add(_DeleteAccount(account));
        },
      ),
      onTap: () {
        context.read<_Bloc>().add(_SwitchAccount(account));
      },
    );
  }

  final Account account;
}

class _AccountSettingsView extends StatelessWidget {
  const _AccountSettingsView();

  @override
  Widget build(BuildContext context) {
    return _IconTile(
      icon: const Icon(Icons.manage_accounts_outlined),
      title: Text(L10n.global().accountSettingsTooltip),
      onTap: () {
        Navigator.of(context)
          ..pop()
          ..pushNamed(AccountSettings.routeName);
      },
    );
  }
}

class _NewAccountView extends StatelessWidget {
  const _NewAccountView();

  @override
  Widget build(BuildContext context) {
    return _IconTile(
      icon: const Icon(Icons.add),
      title: Text(L10n.global().addServerTooltip),
      onTap: () {
        Navigator.of(context)
          ..pop()
          ..pushNamed(SignIn.routeName);
      },
    );
  }
}

class _AboutChin extends StatelessWidget {
  const _AboutChin();

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<ServerStatus?>(
      stream: context.read<_Bloc>().accountController.serverController.status,
      initialData: context
          .read<_Bloc>()
          .accountController
          .serverController
          .status
          .valueOrNull,
      builder: (context, snapshot) {
        var text = "${L10n.global().appTitle} ${k.versionStr}";
        if (snapshot.hasData) {
          final status = snapshot.requireData!;
          text +=
              "  ${_getSymbol()}  ${status.productName} ${status.versionName}";
        }
        return Padding(
          padding: const EdgeInsets.symmetric(vertical: 8),
          child: Text(
            text,
            style: Theme.of(context).textTheme.bodySmall,
          ),
        );
      },
    );
  }

  String _getSymbol() {
    final today = clock.now();
    if (today.month == 1 && today.day == 1) {
      // firework
      return "\u{1f386}";
    } else if (today.month == 4 && today.day == 10) {
      // initial commit!
      return "\u{1f382}";
    } else {
      const symbols = [
        // cloud
        "\u2601",
        // heart
        "\u2665",
        // star
        "\u2b50",
        // rainbow
        "\u{1f308}",
        // globe
        "\u{1f310}",
        // clover
        "\u{1f340}",
      ];
      return symbols[Random(_seed).nextInt(symbols.length)];
    }
  }

  static final _seed = Random().nextInt(65536);
}