// @dart=2.9

import 'dart:async';
import 'dart:io';
import 'dart:ui';

import 'package:ente_auth/core/configuration.dart';
import 'package:ente_auth/ente_theme_data.dart';
import 'package:ente_auth/models/code.dart';
import 'package:ente_auth/services/authenticator_service.dart';
import 'package:ente_auth/services/local_authentication_service.dart';
import 'package:ente_auth/store/code_store.dart';
import 'package:ente_auth/theme/ente_theme.dart';
import 'package:ente_auth/ui/account/change_email_dialog.dart';
import 'package:ente_auth/ui/account/password_entry_page.dart';
import 'package:ente_auth/ui/account/recovery_key_page.dart';
import 'package:ente_auth/ui/components/captioned_text_widget.dart';
import 'package:ente_auth/ui/components/expandable_menu_item_widget.dart';
import 'package:ente_auth/ui/components/menu_item_widget.dart';
import 'package:ente_auth/ui/settings/common_settings.dart';
import 'package:ente_auth/utils/dialog_util.dart';
import 'package:ente_auth/utils/navigation_util.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter_sodium/flutter_sodium.dart';
import 'package:logging/logging.dart';
import 'package:share_plus/share_plus.dart';

class AccountSectionWidget extends StatelessWidget {
  final _logger = Logger("AccountSectionWidget");

  final _codeFile = File(
    Configuration.instance.getTempDirectory() + "ente-authenticator-codes.txt",
  );

  AccountSectionWidget({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ExpandableMenuItemWidget(
      title: "Account",
      selectionOptionsWidget: _getSectionOptions(context),
      leadingIcon: Icons.account_circle_outlined,
    );
  }

  Column _getSectionOptions(BuildContext context) {
    List<Widget> children = [];
    children.addAll([
      sectionOptionSpacing,
      MenuItemWidget(
        captionedTextWidget: const CaptionedTextWidget(
          title: "Recovery key",
        ),
        pressedColor: getEnteColorScheme(context).fillFaint,
        trailingIcon: Icons.chevron_right_outlined,
        trailingIconIsMuted: true,
        onTap: () async {
          final hasAuthenticated = await LocalAuthenticationService.instance
              .requestLocalAuthentication(
            context,
            "Please authenticate to view your recovery key",
          );
          if (hasAuthenticated) {
            String recoveryKey;
            try {
              recoveryKey =
                  Sodium.bin2base64(Configuration.instance.getRecoveryKey());
            } catch (e) {
              showGenericErrorDialog(context);
              return;
            }
            routeToPage(
              context,
              RecoveryKeyPage(
                recoveryKey,
                "OK",
                showAppBar: true,
                onDone: () {},
              ),
            );
          }
        },
      ),
      sectionOptionSpacing,
      MenuItemWidget(
        captionedTextWidget: const CaptionedTextWidget(
          title: "Change email",
        ),
        pressedColor: getEnteColorScheme(context).fillFaint,
        trailingIcon: Icons.chevron_right_outlined,
        trailingIconIsMuted: true,
        onTap: () async {
          final hasAuthenticated = await LocalAuthenticationService.instance
              .requestLocalAuthentication(
            context,
            "Please authenticate to change your email",
          );
          if (hasAuthenticated) {
            showDialog(
              context: context,
              builder: (BuildContext context) {
                return const ChangeEmailDialog();
              },
              barrierColor: Colors.black.withOpacity(0.85),
              barrierDismissible: false,
            );
          }
        },
      ),
      sectionOptionSpacing,
      MenuItemWidget(
        captionedTextWidget: const CaptionedTextWidget(
          title: "Change password",
        ),
        pressedColor: getEnteColorScheme(context).fillFaint,
        trailingIcon: Icons.chevron_right_outlined,
        trailingIconIsMuted: true,
        onTap: () async {
          final hasAuthenticated = await LocalAuthenticationService.instance
              .requestLocalAuthentication(
            context,
            "Please authenticate to change your password",
          );
          if (hasAuthenticated) {
            Navigator.of(context).push(
              MaterialPageRoute(
                builder: (BuildContext context) {
                  return const PasswordEntryPage(
                    mode: PasswordEntryMode.update,
                  );
                },
              ),
            );
          }
        },
      ),
      sectionOptionSpacing,
      MenuItemWidget(
        captionedTextWidget: const CaptionedTextWidget(
          title: "Import codes",
        ),
        pressedColor: getEnteColorScheme(context).fillFaint,
        trailingIcon: Icons.chevron_right_outlined,
        trailingIconIsMuted: true,
        onTap: () async {
          _showImportInstructionDialog(context);
        },
      ),
      sectionOptionSpacing,
      MenuItemWidget(
        captionedTextWidget: const CaptionedTextWidget(
          title: "Export codes",
        ),
        pressedColor: getEnteColorScheme(context).fillFaint,
        trailingIcon: Icons.chevron_right_outlined,
        trailingIconIsMuted: true,
        onTap: () async {
          _showExportWarningDialog(context);
        },
      ),
      sectionOptionSpacing,
    ]);
    return Column(
      children: children,
    );
  }

  Future<void> _showImportInstructionDialog(BuildContext context) async {
    final AlertDialog alert = AlertDialog(
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
      title: Text(
        "Import codes",
        style: Theme.of(context).textTheme.headline6,
      ),
      content: SingleChildScrollView(
        child: Column(
          children: [
            const Text(
              "Please select a file that contains a list of secrets in the following format",
            ),
            const SizedBox(
              height: 20,
            ),
            Container(
              color: Theme.of(context).colorScheme.gNavBackgroundColor,
              child: Padding(
                padding: const EdgeInsets.all(8),
                child: Text(
                  "otpauth://totp/provider.com:you@email.com?secret=YOUR_SECRET",
                  style: TextStyle(
                    fontFeatures: const [FontFeature.tabularFigures()],
                    fontFamily: Platform.isIOS ? "Courier" : "monospace",
                    fontSize: 13,
                  ),
                ),
              ),
            ),
            const SizedBox(
              height: 20,
            ),
            const Text(
              "The secrets can be separated by a comma or a new line",
            ),
          ],
        ),
      ),
      actions: [
        TextButton(
          child: const Text(
            "Cancel",
            style: TextStyle(
              color: Colors.red,
            ),
          ),
          onPressed: () {
            Navigator.of(context, rootNavigator: true).pop('dialog');
          },
        ),
        TextButton(
          child: const Text(
            "Select file",
          ),
          onPressed: () {
            Navigator.of(context, rootNavigator: true).pop('dialog');
            _pickImportFile(context);
          },
        ),
      ],
    );

    return showDialog(
      context: context,
      builder: (BuildContext context) {
        return alert;
      },
      barrierColor: Colors.black12,
    );
  }

  Future<void> _showExportWarningDialog(BuildContext context) async {
    final AlertDialog alert = AlertDialog(
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
      title: Text(
        "Warning",
        style: Theme.of(context).textTheme.headline6,
      ),
      content: const Text(
        "The exported file contains sensitive information. Please store this safely.",
      ),
      actions: [
        TextButton(
          child: const Text(
            "I understand",
            style: TextStyle(
              color: Colors.red,
            ),
          ),
          onPressed: () {
            Navigator.of(context, rootNavigator: true).pop('dialog');
            _exportCodes(context);
          },
        ),
        TextButton(
          child: const Text(
            "Cancel",
          ),
          onPressed: () {
            Navigator.of(context, rootNavigator: true).pop('dialog');
          },
        ),
      ],
    );

    return showDialog(
      context: context,
      builder: (BuildContext context) {
        return alert;
      },
      barrierColor: Colors.black12,
    );
  }

  Future<void> _exportCodes(BuildContext context) async {
    final hasAuthenticated =
        await LocalAuthenticationService.instance.requestLocalAuthentication(
      context,
      "Please authenticate to export your codes",
    );
    if (!hasAuthenticated) {
      return;
    }
    if (_codeFile.existsSync()) {
      await _codeFile.delete();
    }
    final codes = await CodeStore.instance.getAllCodes();
    String data = "";
    for (final code in codes) {
      data += code.rawData + "\n";
    }
    _codeFile.writeAsStringSync(data);
    await Share.shareFiles([_codeFile.path]);
    Future.delayed(const Duration(seconds: 15), () async {
      if (_codeFile.existsSync()) {
        _codeFile.deleteSync();
      }
    });
  }

  Future<void> _pickImportFile(BuildContext context) async {
    FilePickerResult result = await FilePicker.platform.pickFiles();
    if (result == null) {
      return;
    }
    File file = File(result.files.single.path);
    final codes = await file.readAsString();
    List<String> splitCodes = codes.split(",");
    if (splitCodes.length == 1) {
      splitCodes = codes.split("\n");
    }
    int errors = 0;
    final parsedCodes = [];
    for (final code in splitCodes) {
      try {
        parsedCodes.add(Code.fromRawData(code));
      } catch (e) {
        _logger.severe("Could not parse code", e);
        errors++;
      }
    }
    final dialog = createProgressDialog(context, "Please wait...");
    await dialog.show();
    try {
      for (final code in parsedCodes) {
        await CodeStore.instance.addCode(code, shouldSync: false);
      }
      unawaited(AuthenticatorService.instance.sync());
      await dialog.hide();
      await showConfettiDialog(
        context: context,
        builder: (BuildContext context) {
          return AlertDialog(
            shape:
                RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
            title: Text(
              "Yay!",
              style: Theme.of(context).textTheme.headline6,
            ),
            content: Text(
              "You have imported " + parsedCodes.length.toString() + " codes!",
            ),
            actions: [
              TextButton(
                child: Text(
                  "Okay",
                  style: TextStyle(
                    color: Theme.of(context).colorScheme.onSurface,
                  ),
                ),
                onPressed: () {
                  Navigator.of(context, rootNavigator: true).pop('dialog');
                },
              ),
            ],
          );
        },
      );
    } catch (e) {
      await dialog.hide();
      showErrorDialog(context, "Oops", "Something went wrong");
    }
    _logger.info(parsedCodes);
  }
}
