import 'dart:convert';
import 'dart:io';

import 'package:Capsule/utils/storage/get_file.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:pointycastle/impl.dart';
import 'package:shared_preferences/shared_preferences.dart';

import '/utils/encryption/aes_helper.dart';
import '/utils/encryption/crypto_utils.dart';

// TODO: Replace this with the prod url when server is deployed
String defaultServerUrl = dotenv.env['SERVER_URL'] ?? 'http://localhost:8080/';

class MyProfile {
  String id;
  String username;
  String? friendId;
  RSAPrivateKey? privateKey;
  RSAPublicKey? publicKey;
  String? symmetricKey;
  DateTime? loggedInAt;
  File? image;
  String? imageLink;
  String messageExpiryDefault = 'no_expiry';

  MyProfile({
    required this.id,
    required this.username,
    this.friendId,
    this.privateKey,
    this.publicKey,
    this.symmetricKey,
    this.loggedInAt,
    this.image,
    this.imageLink,
    required this.messageExpiryDefault,
  });

  factory MyProfile._fromJson(Map<String, dynamic> json) {
    DateTime loggedInAt = DateTime.now();
    if (json.containsKey('logged_in_at')) {
      loggedInAt = DateTime.parse(json['logged_in_at']);
    }

    RSAPrivateKey privateKey = CryptoUtils.rsaPrivateKeyFromPem(json['asymmetric_private_key']);
    RSAPublicKey publicKey = CryptoUtils.rsaPublicKeyFromPem(json['asymmetric_public_key']);

    return MyProfile(
        id: json['user_id'],
        username: json['username'],
        privateKey: privateKey,
        publicKey: publicKey,
        symmetricKey: json['symmetric_key'],
        loggedInAt: loggedInAt,
        messageExpiryDefault: json['message_expiry_default'],
        image: json['file'] != null ? File(json['file']) : null,
        imageLink: json['image_link'],
    );
  }

  @override
  String toString() {
    return '''
      user_id: $id
      username: $username
      logged_in_at: $loggedInAt
      public_key: $publicKey
      private_key: $privateKey
    ''';
  }

  String toJson() {
    return jsonEncode(<String, dynamic>{
      'user_id': id,
      'username': username,
      'asymmetric_private_key': privateKey != null ?
          CryptoUtils.encodeRSAPrivateKeyToPem(privateKey!) :
          null,
      'asymmetric_public_key': publicKey != null ?
          CryptoUtils.encodeRSAPublicKeyToPem(publicKey!) :
          null,
      'symmetric_key': symmetricKey,
      'logged_in_at': loggedInAt?.toIso8601String(),
      'message_expiry_default': messageExpiryDefault,
      'file': image?.path,
      'image_link': imageLink,
    });
  }

  static Future<MyProfile> login(Map<String, dynamic> json, String password) async {
    json['asymmetric_private_key'] = AesHelper.aesDecrypt(
        password,
        base64.decode(json['asymmetric_private_key'])
    );

    json['symmetric_key'] = base64.encode(CryptoUtils.rsaDecrypt(
      base64.decode(json['symmetric_key']),
      CryptoUtils.rsaPrivateKeyFromPem(json['asymmetric_private_key']),
    ));

    if (json['image_link'] != '') {
      File profileIcon = await getFile(
        '$defaultServerUrl/files/${['image_link']}',
        json['user_id'],
        json['symmetric_key'],
      );

      json['file'] = profileIcon.path;
    }

    MyProfile profile = MyProfile._fromJson(json);

    final preferences = await SharedPreferences.getInstance();
    preferences.setString('profile', profile.toJson());
    return profile;
  }

  static Future<void> logout() async {
    final preferences = await SharedPreferences.getInstance();
    preferences.remove('profile');
  }

  static Future<MyProfile> getProfile() async {
    final preferences = await SharedPreferences.getInstance();
    String? profileJson = preferences.getString('profile');
    if (profileJson == null) {
      throw Exception('No profile');
    }
    return MyProfile._fromJson(json.decode(profileJson));
  }

  static Future<bool> isLoggedIn() async {
    MyProfile profile = await MyProfile.getProfile();
    if (profile.loggedInAt == null) {
      return false;
    }

    return profile.loggedInAt!.add(const Duration(hours: 12)).isAfter(
        (DateTime.now())
    );
  }

  static Future<RSAPrivateKey> getPrivateKey() async {
    MyProfile profile = await MyProfile.getProfile();
    if (profile.privateKey == null) {
      throw Exception('Could not get privateKey');
    }
    return profile.privateKey!;
  }

  static setServerUrl(String url) async {
    final preferences = await SharedPreferences.getInstance();
    preferences.setString('server_url', url);
  }

  static Future<Uri> getServerUrl(String path) async {
    final preferences = await SharedPreferences.getInstance();

    String? baseUrl = preferences.getString('server_url');
    if (baseUrl == null) {
      setServerUrl(defaultServerUrl);
      return Uri.parse('$defaultServerUrl$path');
    }

    return Uri.parse('$baseUrl$path');
  }
}

