using Managing.Application.Abstractions.Repositories;
using Managing.Application.Abstractions.Services;
using Managing.Common;
using Managing.Domain.Accounts;
using Managing.Domain.Users;
using Microsoft.Extensions.Logging;

namespace Managing.Application.Users;

public class UserService : IUserService
{
    private readonly IEvmManager _evmManager;
    private readonly IUserRepository _userRepository;
    private readonly IAccountService _accountService;
    private readonly ILogger<UserService> _logger;

    private string[] authorizedAddresses =
    [
        "0x6781920674dA695aa5120d95D80c4B1788046806", // Macbook
        "0xA2B43AFF0992a47838DF2e6099A8439981f0B717", // Phone
        "0xAD4bcf258852e9d47E580798d312E1a52D59E721", // Razil
        "0xAd6D6c80910096b40e45690506a9f1052e072dCB", // Teru
        "0x309b9235edbe1C6f840816771c6C21aDa6c275EE", // Cowchain
        "0x23AA99254cfaA2c374bE2bA5B55C68018cCdFCb3", // Local optiflex
        "0x932167388dD9aad41149b3cA23eBD489E2E2DD78", // Embedded wallet 
        "0x66CB57Fe3f53cE57376421106dFDa2D39186cBd0", // Embedded wallet optiflex
        "0x7baBf95621f22bEf2DB67E500D022Ca110722FaD",  // DevCowchain
        "0xc8bC497534d0A43bAb2BBA9BA94d46D9Ddfaea6B" // DevCowchain2
    ];

    public UserService(
        IEvmManager evmManager,
        IUserRepository userRepository,
        IAccountService accountService, ILogger<UserService> logger)
    {
        _evmManager = evmManager;
        _userRepository = userRepository;
        _accountService = accountService;
        _logger = logger;
    }

    public async Task<User> Authenticate(string name, string address, string message, string signature)
    {
        var recoveredAddress = _evmManager.VerifySignature(signature, message);

        // Verify message 
        if (!message.Equals("KaigenTeamXCowchain"))
        {
            _logger.LogWarning($"Message {message} not starting with KaigenTeamXCowchain");
            throw new Exception($"Message not good : {message} - Address : {address} - User : {name} - Signature : {signature}");
        }

        // if (!authorizedAddresses.Contains(recoveredAddress))
        // {
        //     _logger.LogWarning($"Address {recoveredAddress} not authorized");
        //     throw new Exception("Address not authorized");
        // }

        if (recoveredAddress == null || !recoveredAddress.Equals(address))
        {
            _logger.LogWarning($"Address {recoveredAddress} not corresponding");
            throw new Exception("Address not corresponding");
        }

        // Check if account exist
        var user = await _userRepository.GetUserByNameAsync(name);

        if (user != null)
        {
            if (!user.Name.Equals(name))
                throw new Exception("Name not corresponding");

            // User and account found
            user.Accounts = _accountService.GetAccountsByUser(user).ToList();

            // Check if recoverred address owns the account
            var account = user.Accounts.FirstOrDefault(a => a.Key.Equals(recoveredAddress));

            if (account == null)
            {
                throw new Exception("Account not found");
            }

            return user;
        }
        else
        {
            // First login
            user = new User
            {
                Name = name
            };

            await _userRepository.InsertUserAsync(user);

            // Create embedded account to authenticate user
            var account = await _accountService.CreateAccount(user, new Account
            {
                Name = $"{name}-embedded",
                Key = recoveredAddress,
                Exchange = Enums.TradingExchanges.Evm,
                Type = Enums.AccountType.Privy
            });

            user.Accounts = new List<Account>()
            {
                account
            };
        }

        return user;
    }

    public async Task<User> GetUserByAddressAsync(string address)
    {
        var account = await _accountService.GetAccountByKey(address, true, false);
        var user = await _userRepository.GetUserByNameAsync(account.User.Name);
        user.Accounts = _accountService.GetAccountsByUser(user).ToList();
        return user;
    }
}
