import 'dart:io';

import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:flushbar/flushbar_helper.dart';
import 'package:famedlysdk/famedlysdk.dart';
import 'package:file_picker_cross/file_picker_cross.dart';

import 'package:fluffychat/components/settings_themes.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/utils/platform_infos.dart';
import 'package:fluffychat/utils/sentry_controller.dart';
import 'package:fluffychat/views/settings_devices.dart';
import 'package:fluffychat/views/settings_ignore_list.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:image_picker/image_picker.dart';
import 'package:url_launcher/url_launcher.dart';

import '../components/adaptive_page_layout.dart';
import '../components/content_banner.dart';
import '../components/dialogs/simple_dialogs.dart';
import '../components/matrix.dart';
import '../utils/app_route.dart';
import '../config/app_config.dart';
import '../config/setting_keys.dart';
import 'app_info.dart';
import 'chat_list.dart';
import 'settings_emotes.dart';

class SettingsView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return AdaptivePageLayout(
      primaryPage: FocusPage.SECOND,
      firstScaffold: ChatList(),
      secondScaffold: Settings(),
    );
  }
}

class Settings extends StatefulWidget {
  @override
  _SettingsState createState() => _SettingsState();
}

class _SettingsState extends State<Settings> {
  Future<dynamic> profileFuture;
  dynamic profile;
  Future<bool> crossSigningCachedFuture;
  bool crossSigningCached;
  Future<bool> megolmBackupCachedFuture;
  bool megolmBackupCached;

  void logoutAction(BuildContext context) async {
    if (await showOkCancelAlertDialog(
          context: context,
          title: L10n.of(context).areYouSure,
        ) ==
        OkCancelResult.cancel) {
      return;
    }
    var matrix = Matrix.of(context);
    await SimpleDialogs(context)
        .tryRequestWithLoadingDialog(matrix.client.logout());
  }

  void _changePasswordAccountAction(BuildContext context) async {
    final input = await showTextInputDialog(
      context: context,
      title: L10n.of(context).changePassword,
      textFields: [
        DialogTextField(
          hintText: L10n.of(context).pleaseEnterYourPassword,
          obscureText: true,
        ),
        DialogTextField(
          hintText: L10n.of(context).chooseAStrongPassword,
          obscureText: true,
        ),
      ],
    );
    if (input == null) return;
    await SimpleDialogs(context).tryRequestWithLoadingDialog(
      Matrix.of(context)
          .client
          .changePassword(input.last, oldPassword: input.first),
    );
    await FlushbarHelper.createSuccess(
            message: L10n.of(context).passwordHasBeenChanged)
        .show(context);
  }

  void _deleteAccountAction(BuildContext context) async {
    if (await showOkCancelAlertDialog(
          context: context,
          title: L10n.of(context).warning,
          message: L10n.of(context).deactivateAccountWarning,
        ) ==
        OkCancelResult.cancel) {
      return;
    }
    if (await showOkCancelAlertDialog(
            context: context, title: L10n.of(context).areYouSure) ==
        OkCancelResult.cancel) {
      return;
    }
    final input = await showTextInputDialog(
      context: context,
      title: L10n.of(context).pleaseEnterYourPassword,
      textFields: [DialogTextField(obscureText: true, hintText: '******')],
    );
    if (input == null) return;
    await SimpleDialogs(context).tryRequestWithLoadingDialog(
      Matrix.of(context).client.deactivateAccount(auth: {
        'type': 'm.login.password',
        'user': Matrix.of(context).client.userID,
        'password': input.single,
      }),
    );
  }

  void setJitsiInstanceAction(BuildContext context) async {
    var input = await showTextInputDialog(
      context: context,
      title: L10n.of(context).editJitsiInstance,
      textFields: [
        DialogTextField(initialText: Matrix.of(context).jitsiInstance),
      ],
    );
    if (input == null) return;
    var jitsi = input.single;
    if (!jitsi.endsWith('/')) {
      jitsi += '/';
    }
    final matrix = Matrix.of(context);
    await matrix.store.setItem(SettingKeys.jitsiInstance, jitsi);
    matrix.jitsiInstance = jitsi;
  }

  void setDisplaynameAction(BuildContext context) async {
    final input = await showTextInputDialog(
      context: context,
      title: L10n.of(context).editDisplayname,
      textFields: [
        DialogTextField(
          initialText: profile?.displayname ??
              Matrix.of(context).client.userID.localpart,
        )
      ],
    );
    if (input == null) return;
    final matrix = Matrix.of(context);
    final success = await SimpleDialogs(context).tryRequestWithLoadingDialog(
      matrix.client.setDisplayname(matrix.client.userID, input.single),
    );
    if (success != false) {
      setState(() {
        profileFuture = null;
        profile = null;
      });
    }
  }

  void setAvatarAction(BuildContext context) async {
    MatrixFile file;
    if (PlatformInfos.isMobile) {
      final result = await ImagePicker().getImage(
          source: ImageSource.gallery,
          imageQuality: 50,
          maxWidth: 1600,
          maxHeight: 1600);
      if (result == null) return;
      file = MatrixFile(
        bytes: await result.readAsBytes(),
        name: result.path,
      );
    } else {
      final result =
          await FilePickerCross.importFromStorage(type: FileTypeCross.image);
      if (result == null) return;
      file = MatrixFile(
        bytes: result.toUint8List(),
        name: result.fileName,
      );
    }
    final matrix = Matrix.of(context);
    final success = await SimpleDialogs(context).tryRequestWithLoadingDialog(
      matrix.client.setAvatar(file),
    );
    if (success != false) {
      setState(() {
        profileFuture = null;
        profile = null;
      });
    }
  }

  void setWallpaperAction(BuildContext context) async {
    final wallpaper = await ImagePicker().getImage(source: ImageSource.gallery);
    if (wallpaper == null) return;
    Matrix.of(context).wallpaper = File(wallpaper.path);
    await Matrix.of(context)
        .store
        .setItem(SettingKeys.wallpaper, wallpaper.path);
    setState(() => null);
  }

  void deleteWallpaperAction(BuildContext context) async {
    Matrix.of(context).wallpaper = null;
    await Matrix.of(context).store.deleteItem(SettingKeys.wallpaper);
    setState(() => null);
  }

  Future<void> requestSSSSCache(BuildContext context) async {
    final handle = Matrix.of(context).client.encryption.ssss.open();
    final input = await showTextInputDialog(
      context: context,
      title: L10n.of(context).askSSSSCache,
      textFields: [
        DialogTextField(
            hintText: L10n.of(context).passphraseOrKey, obscureText: true)
      ],
    );
    if (input != null) {
      final valid = await SimpleDialogs(context)
          .tryRequestWithLoadingDialog(Future.microtask(() async {
        // make sure the loading spinner shows before we test the keys
        await Future.delayed(Duration(milliseconds: 100));
        var valid = false;
        try {
          handle.unlock(recoveryKey: input.single);
          valid = true;
        } catch (e, s) {
          debugPrint('Couldn\'t use recovery key: ' + e.toString());
          debugPrint(s.toString());
          try {
            handle.unlock(passphrase: input.single);
            valid = true;
          } catch (e, s) {
            debugPrint('Couldn\'t use recovery passphrase: ' + e.toString());
            debugPrint(s.toString());
            valid = false;
          }
        }
        return valid;
      }));

      if (valid == true) {
        await handle.maybeCacheAll();
        await showOkAlertDialog(
          context: context,
          message: L10n.of(context).cachedKeys,
        );
        setState(() {
          crossSigningCachedFuture = null;
          crossSigningCached = null;
          megolmBackupCachedFuture = null;
          megolmBackupCached = null;
        });
      } else {
        await showOkAlertDialog(
          context: context,
          message: L10n.of(context).incorrectPassphraseOrKey,
        );
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    final client = Matrix.of(context).client;
    profileFuture ??= client.ownProfile.then((p) {
      if (mounted) setState(() => profile = p);
      return p;
    });
    crossSigningCachedFuture ??=
        client.encryption.crossSigning.isCached().then((c) {
      if (mounted) setState(() => crossSigningCached = c);
      return c;
    });
    megolmBackupCachedFuture ??=
        client.encryption.keyManager.isCached().then((c) {
      if (mounted) setState(() => megolmBackupCached = c);
      return c;
    });
    return Scaffold(
      body: NestedScrollView(
        headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) =>
            <Widget>[
          SliverAppBar(
            expandedHeight: 300.0,
            floating: true,
            pinned: true,
            backgroundColor: Theme.of(context).appBarTheme.color,
            flexibleSpace: FlexibleSpaceBar(
              title: Text(
                L10n.of(context).settings,
                style: TextStyle(
                    color: Theme.of(context)
                        .appBarTheme
                        .textTheme
                        .headline6
                        .color),
              ),
              background: ContentBanner(
                profile?.avatarUrl,
                height: 300,
                defaultIcon: Icons.account_circle,
                loading: profile == null,
                onEdit: () => setAvatarAction(context),
              ),
            ),
          ),
        ],
        body: ListView(
          children: <Widget>[
            ListTile(
              title: Text(
                L10n.of(context).changeTheme,
                style: TextStyle(
                  color: Theme.of(context).primaryColor,
                  fontWeight: FontWeight.bold,
                ),
              ),
            ),
            ThemesSettings(),
            if (!kIsWeb && Matrix.of(context).store != null)
              Divider(thickness: 1),
            if (!kIsWeb && Matrix.of(context).store != null)
              ListTile(
                title: Text(
                  L10n.of(context).wallpaper,
                  style: TextStyle(
                    color: Theme.of(context).primaryColor,
                    fontWeight: FontWeight.bold,
                  ),
                ),
              ),
            if (Matrix.of(context).wallpaper != null)
              ListTile(
                title: Image.file(
                  Matrix.of(context).wallpaper,
                  height: 38,
                  fit: BoxFit.cover,
                ),
                trailing: Icon(
                  Icons.delete_forever,
                  color: Colors.red,
                ),
                onTap: () => deleteWallpaperAction(context),
              ),
            if (!kIsWeb && Matrix.of(context).store != null)
              Builder(builder: (context) {
                return ListTile(
                  title: Text(L10n.of(context).changeWallpaper),
                  trailing: Icon(Icons.wallpaper),
                  onTap: () => setWallpaperAction(context),
                );
              }),
            Divider(thickness: 1),
            ListTile(
              title: Text(
                L10n.of(context).chat,
                style: TextStyle(
                  color: Theme.of(context).primaryColor,
                  fontWeight: FontWeight.bold,
                ),
              ),
            ),
            ListTile(
              title: Text(L10n.of(context).renderRichContent),
              trailing: Switch(
                value: AppConfig.renderHtml,
                activeColor: Theme.of(context).primaryColor,
                onChanged: (bool newValue) async {
                  AppConfig.renderHtml = newValue;
                  await Matrix.of(context)
                      .store
                      .setItem(SettingKeys.renderHtml, newValue.toString());
                  setState(() => null);
                },
              ),
            ),
            ListTile(
              title: Text(L10n.of(context).hideRedactedEvents),
              trailing: Switch(
                value: AppConfig.hideRedactedEvents,
                activeColor: Theme.of(context).primaryColor,
                onChanged: (bool newValue) async {
                  AppConfig.hideRedactedEvents = newValue;
                  await Matrix.of(context).store.setItem(
                      SettingKeys.hideRedactedEvents, newValue.toString());
                  setState(() => null);
                },
              ),
            ),
            ListTile(
              title: Text(L10n.of(context).hideUnknownEvents),
              trailing: Switch(
                value: AppConfig.hideUnknownEvents,
                activeColor: Theme.of(context).primaryColor,
                onChanged: (bool newValue) async {
                  AppConfig.hideUnknownEvents = newValue;
                  await Matrix.of(context).store.setItem(
                      SettingKeys.hideUnknownEvents, newValue.toString());
                  setState(() => null);
                },
              ),
            ),
            ListTile(
              title: Text(L10n.of(context).emoteSettings),
              onTap: () async => await Navigator.of(context).push(
                AppRoute.defaultRoute(
                  context,
                  EmotesSettingsView(),
                ),
              ),
              trailing: Icon(Icons.insert_emoticon),
            ),
            Divider(thickness: 1),
            ListTile(
              title: Text(
                L10n.of(context).account,
                style: TextStyle(
                  color: Theme.of(context).primaryColor,
                  fontWeight: FontWeight.bold,
                ),
              ),
            ),
            ListTile(
              trailing: Icon(Icons.edit),
              title: Text(L10n.of(context).editDisplayname),
              subtitle: Text(profile?.displayname ?? client.userID.localpart),
              onTap: () => setDisplaynameAction(context),
            ),
            ListTile(
              trailing: Icon(Icons.phone),
              title: Text(L10n.of(context).editJitsiInstance),
              subtitle: Text(Matrix.of(context).jitsiInstance),
              onTap: () => setJitsiInstanceAction(context),
            ),
            ListTile(
              trailing: Icon(Icons.devices_other),
              title: Text(L10n.of(context).devices),
              onTap: () async => await Navigator.of(context).push(
                AppRoute.defaultRoute(
                  context,
                  DevicesSettingsView(),
                ),
              ),
            ),
            ListTile(
              trailing: Icon(Icons.block),
              title: Text(L10n.of(context).ignoredUsers),
              onTap: () async => await Navigator.of(context).push(
                AppRoute.defaultRoute(
                  context,
                  SettingsIgnoreListView(),
                ),
              ),
            ),
            ListTile(
              trailing: Icon(Icons.account_circle),
              title: Text(L10n.of(context).accountInformation),
              onTap: () => Navigator.of(context).push(
                AppRoute.defaultRoute(
                  context,
                  AppInfoView(),
                ),
              ),
            ),
            ListTile(
              trailing: Icon(Icons.bug_report),
              title: Text(L10n.of(context).sendBugReports),
              onTap: () => SentryController.toggleSentryAction(context),
            ),
            Divider(thickness: 1),
            ListTile(
              trailing: Icon(Icons.vpn_key),
              title: Text(
                L10n.of(context).changePassword,
              ),
              onTap: () => _changePasswordAccountAction(context),
            ),
            ListTile(
              trailing: Icon(Icons.exit_to_app),
              title: Text(L10n.of(context).logout),
              onTap: () => logoutAction(context),
            ),
            ListTile(
              trailing: Icon(Icons.delete_forever),
              title: Text(
                L10n.of(context).deleteAccount,
                style: TextStyle(color: Colors.red),
              ),
              onTap: () => _deleteAccountAction(context),
            ),
            Divider(thickness: 1),
            ListTile(
              title: Text(
                L10n.of(context).encryption,
                style: TextStyle(
                  color: Theme.of(context).primaryColor,
                  fontWeight: FontWeight.bold,
                ),
              ),
            ),
            ListTile(
              trailing: Icon(Icons.compare_arrows),
              title: Text(client.encryption.crossSigning.enabled
                  ? L10n.of(context).crossSigningEnabled
                  : L10n.of(context).crossSigningDisabled),
              subtitle: client.encryption.crossSigning.enabled
                  ? Text(client.isUnknownSession
                      ? L10n.of(context).unknownSessionVerify
                      : L10n.of(context).sessionVerified +
                          ', ' +
                          (crossSigningCached == null
                              ? '⌛'
                              : (crossSigningCached
                                  ? L10n.of(context).keysCached
                                  : L10n.of(context).keysMissing)))
                  : null,
              onTap: () async {
                if (!client.encryption.crossSigning.enabled) {
                  await showOkAlertDialog(
                    context: context,
                    message: L10n.of(context).noCrossSignBootstrap,
                  );
                  return;
                }
                if (client.isUnknownSession) {
                  final input = await showTextInputDialog(
                    context: context,
                    title: L10n.of(context).askSSSSVerify,
                    textFields: [
                      DialogTextField(
                          hintText: L10n.of(context).passphraseOrKey,
                          obscureText: true)
                    ],
                  );
                  if (input != null) {
                    final valid = await SimpleDialogs(context)
                        .tryRequestWithLoadingDialog(Future.microtask(() async {
                      // make sure the loading spinner shows before we test the keys
                      await Future.delayed(Duration(milliseconds: 100));
                      var valid = false;
                      try {
                        await client.encryption.crossSigning
                            .selfSign(recoveryKey: input.single);
                        valid = true;
                      } catch (_) {
                        try {
                          await client.encryption.crossSigning
                              .selfSign(passphrase: input.single);
                          valid = true;
                        } catch (_) {
                          valid = false;
                        }
                      }
                      return valid;
                    }));

                    if (valid == true) {
                      await showOkAlertDialog(
                        context: context,
                        message: L10n.of(context).verifiedSession,
                      );
                      setState(() {
                        crossSigningCachedFuture = null;
                        crossSigningCached = null;
                        megolmBackupCachedFuture = null;
                        megolmBackupCached = null;
                      });
                    } else {
                      await showOkAlertDialog(
                        context: context,
                        message: L10n.of(context).incorrectPassphraseOrKey,
                      );
                    }
                  }
                }
                if (!(await client.encryption.crossSigning.isCached())) {
                  await requestSSSSCache(context);
                }
              },
            ),
            ListTile(
              trailing: Icon(Icons.wb_cloudy),
              title: Text(client.encryption.keyManager.enabled
                  ? L10n.of(context).onlineKeyBackupEnabled
                  : L10n.of(context).onlineKeyBackupDisabled),
              subtitle: client.encryption.keyManager.enabled
                  ? Text(megolmBackupCached == null
                      ? '⌛'
                      : (megolmBackupCached
                          ? L10n.of(context).keysCached
                          : L10n.of(context).keysMissing))
                  : null,
              onTap: () async {
                if (!client.encryption.keyManager.enabled) {
                  await showOkAlertDialog(
                    context: context,
                    message: L10n.of(context).noMegolmBootstrap,
                  );
                  return;
                }
                if (!(await client.encryption.keyManager.isCached())) {
                  await requestSSSSCache(context);
                }
              },
            ),
            Divider(thickness: 1),
            ListTile(
              title: Text(
                L10n.of(context).about,
                style: TextStyle(
                  color: Theme.of(context).primaryColor,
                  fontWeight: FontWeight.bold,
                ),
              ),
            ),
            ListTile(
              trailing: Icon(Icons.help),
              title: Text(L10n.of(context).help),
              onTap: () => launch(AppConfig.supportUrl),
            ),
            ListTile(
              trailing: Icon(Icons.privacy_tip_rounded),
              title: Text(L10n.of(context).privacy),
              onTap: () => launch(AppConfig.privacyUrl),
            ),
            ListTile(
              trailing: Icon(Icons.link),
              title: Text(L10n.of(context).license),
              onTap: () => showLicensePage(
                context: context,
                applicationIcon:
                    Image.asset('assets/logo.png', width: 100, height: 100),
                applicationName: AppConfig.applicationName,
              ),
            ),
            ListTile(
              trailing: Icon(Icons.code),
              title: Text(L10n.of(context).sourceCode),
              onTap: () => launch(AppConfig.sourceCodeUrl),
            ),
          ],
        ),
      ),
    );
  }
}