import 'dart:async';
import 'dart:convert';
import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_sharing_intent/flutter_sharing_intent.dart';
import 'package:flutter_sharing_intent/model/sharing_file.dart';
import 'package:go_router/go_router.dart';
import 'package:twonly/locator.dart';
import 'package:twonly/src/constants/routes.keys.dart';
import 'package:twonly/src/database/tables/contacts.table.dart';
import 'package:twonly/src/database/tables/mediafiles.table.dart';
import 'package:twonly/src/services/passwordless_recovery.service.dart'
    show PasswordlessRecoveryService;
import 'package:twonly/src/services/signal/session.signal.dart';
import 'package:twonly/src/utils/log.dart';
import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/utils/qr.utils.dart';
import 'package:twonly/src/visual/components/alert.dialog.dart';
import 'package:twonly/src/visual/views/contact/add_contact_via_qr_link.view.dart';
import 'package:twonly/src/visual/views/contact/add_new_contact.view.dart';

Future<bool> handleIntentUrl(BuildContext context, Uri uri) async {
  if (!uri.scheme.startsWith('http')) return false;
  if (uri.host != 'me.twonly.eu') return false;
  if (uri.hasEmptyPath) return false;

  // Check if this is the QR code link which was
  // therefore scanned with the system camera

  if (uri.toString().startsWith(PasswordlessRecoveryService.linkPrefix)) {
    await PasswordlessRecoveryService.handleRecoveryLink(uri.toString());
    return true;
  }

  if (uri.toString().startsWith(QrCodeUtils.linkPrefix)) {
    final result = await QrCodeUtils.handleQrCodeLink(uri.toString());

    if (!context.mounted) return false;

    if (result != null) {
      final (profile, contact, verificationOk) = result;

      if (profile.username == userService.currentUser.username) {
        await context.push(Routes.settingsPublicProfile);
        return true;
      }

      if (contact != null) {
        if (verificationOk) {
          await context.push(Routes.profileContact(contact.userId));
        } else {
          await _pubKeysDoNotMatch(context, contact.username);
        }
      } else {
        await context.navPush(
          AddContactViaQrLinkView(
            profile: profile,
            qrCodeLink: uri.toString(),
          ),
        );
      }
    }

    return true;
  }

  final publicKey = uri.hasFragment ? uri.fragment : null;
  final userPaths = uri.path.split('/');
  if (userPaths.length != 2) return false;
  final username = userPaths[1];

  if (!context.mounted) return false;

  if (username == userService.currentUser.username) {
    await context.push(Routes.settingsPublicProfile);
    return true;
  }

  Log.info(
    'Opened via deep link!: username = $username public_key = ${uri.fragment}',
  );

  final contacts = await twonlyDB.contactsDao.getContactsByUsername(username);
  if (!context.mounted) return true;

  if (contacts.isEmpty) {
    // User does not yet exists, making a request...
    Uint8List? publicKeyBytes;
    if (publicKey != null) {
      publicKeyBytes = base64Url.decode(publicKey);
    }
    await context.navPush(
      AddNewUserView(
        username: username,
        publicKey: publicKeyBytes,
      ),
    );
    return true;
  }

  if (publicKey != null) {
    try {
      final contact = contacts.first;
      final storedPublicKey = await getPublicKeyFromContact(contact.userId);
      final receivedPublicKey = base64Url.decode(publicKey);
      if (storedPublicKey == null ||
          receivedPublicKey.isEmpty ||
          !context.mounted) {
        return true;
      }
      if (storedPublicKey.equals(receivedPublicKey)) {
        final markAsVerified = await showAlertDialog(
          context,
          context.lang.linkFromUsername(contact.username),
          context.lang.linkFromUsernameLong,
          customOk: context.lang.gotLinkFromFriend,
        );
        if (markAsVerified) {
          await twonlyDB.keyVerificationDao.addKeyVerification(
            contact.userId,
            VerificationType.link,
          );
        }
        if (context.mounted) {
          await context.push(Routes.profileContact(contact.userId));
        }
      } else {
        await _pubKeysDoNotMatch(context, contact.username);
      }
    } catch (e) {
      Log.warn(e);
    }
  }

  return true;
}

Future<void> _pubKeysDoNotMatch(BuildContext context, String username) async {
  await showAlertDialog(
    context,
    context.lang.couldNotVerifyUsername(username),
    context.lang.linkPubkeyDoesNotMatch,
    customCancel: '',
  );
}

StreamSubscription<List<SharedFile>> initIntentStreams(
  BuildContext context,
  void Function(Uri) onUrlCallBack,
  void Function(String, MediaType) onMediaCallBack,
) {
  FlutterSharingIntent.instance.getInitialSharing().then((f) {
    if (!context.mounted) return;
    handleIntentSharedFile(context, f, onUrlCallBack, onMediaCallBack);
  });

  return FlutterSharingIntent.instance.getMediaStream().listen(
    (f) {
      if (!context.mounted) return;
      handleIntentSharedFile(context, f, onUrlCallBack, onMediaCallBack);
    },
    // ignore: inference_failure_on_untyped_parameter
    onError: (err) {
      Log.error('getIntentDataStream error: $err');
    },
  );
}

Future<void> handleIntentSharedFile(
  BuildContext context,
  List<SharedFile> files,
  void Function(Uri) onUrlCallBack,
  void Function(String, MediaType) onMediaCallBack,
) async {
  for (final file in files) {
    if (file.value == null) {
      Log.error(
        'Got shared media, but value is empty: getMediaStream ${file.mimeType}',
      );
      continue;
    }

    Log.info('got file via intent ${file.type}');

    switch (file.type) {
      case SharedMediaType.URL:
        if (file.value?.startsWith('http') ?? false) {
          final uri = Uri.parse(file.value!);
          Log.info('Got link via handle intent share file: ${uri.scheme}');
          onUrlCallBack(uri);
        }
      case SharedMediaType.IMAGE:
        var type = MediaType.image;
        if (file.value!.endsWith('.gif')) {
          type = MediaType.gif;
        }
        onMediaCallBack(file.value!, type);
      case SharedMediaType.VIDEO:
        onMediaCallBack(file.value!, MediaType.video);
      // ignore: no_default_cases
      default:
    }
    break; // only handle one file...
  }
}
