using LightlessSync.API.Data;
using LightlessSync.API.Data.Comparer;
using LightlessSync.API.Dto.User;
using LightlessSync.LightlessConfiguration;
using LightlessSync.Services.Mediator;
using LightlessSync.Services.Profiles;
using LightlessSync.WebAPI;
using Microsoft.Extensions.Logging;
using Serilog.Core;
using System;
using System.Collections.Concurrent;

namespace LightlessSync.Services;
public class LightlessProfileManager : MediatorSubscriberBase
{
    public LightlessGroupProfileData LoadingProfileGroupData => _loadingProfileGroupData;
    //Const strings for default values meant in the profile screen.
    private const string _lightlessLogo = "[encoded data, 97076 characters]";
    private const string _lightlessLogoLoading = "[encoded data, 96648 characters]";
    private const string _lightlessLogoNsfw = "[encoded data, 87968 characters]";
    private const string _lightlessSupporter = "[encoded data, 8264 characters]";
    private const string _lightlessBanner = "[encoded data, 703348 characters]";
    private const string _noUserDescription = "-- User has no description set --";
    private const string _noGroupDescription = "-- Syncshell has no description set --";
    private const string _nsfwDescription = "Profile not displayed - NSFW";
    private const string _loadingData = "Loading Profile Data from server...";
    private readonly ApiController _apiController;
    private readonly ILogger<LightlessProfileManager> _logger;
    private readonly LightlessConfigService _lightlessConfigService;
    private readonly ConcurrentDictionary<UserData, LightlessUserProfileData> _lightlessUserProfiles = new(UserDataComparer.Instance);
    private readonly ConcurrentDictionary<GroupData, LightlessGroupProfileData> _lightlessGroupProfiles = new(GroupDataComparer.Instance);

    private static readonly int[] _emptyTagSet = Array.Empty<int>();
    private readonly LightlessUserProfileData _defaultProfileUserData = new(
        IsFlagged: false,
        IsNSFW: false,
        Base64ProfilePicture: _lightlessLogo,
        Base64SupporterPicture: string.Empty,
        Base64BannerPicture: _lightlessBanner,
        Description: _noUserDescription,
        Tags: _emptyTagSet);

    private readonly LightlessUserProfileData _loadingProfileUserData = new(
        IsFlagged: false,
        IsNSFW: false,
        Base64ProfilePicture: _lightlessLogoLoading,
        Base64SupporterPicture: string.Empty,
        Base64BannerPicture: _lightlessBanner,
        Description: _loadingData,
        Tags: _emptyTagSet);

    private readonly LightlessGroupProfileData _loadingProfileGroupData = new(
        IsDisabled: false,
        IsNsfw: false,
        Base64ProfilePicture: _lightlessLogoLoading,
        Base64BannerPicture: _lightlessBanner,
        Description: _loadingData,
        Tags: _emptyTagSet);

    private readonly LightlessGroupProfileData _defaultProfileGroupData = new(
        IsDisabled: false,
        IsNsfw: false,
        Base64ProfilePicture: _lightlessLogo,
        Base64BannerPicture: _lightlessBanner,
        Description: _noGroupDescription,
        Tags: _emptyTagSet);

    private readonly LightlessUserProfileData _nsfwProfileUserData = new(
        IsFlagged: false,
        IsNSFW: true,
        Base64ProfilePicture: _lightlessLogoNsfw,
        Base64SupporterPicture: string.Empty,
        Base64BannerPicture: string.Empty,
        Description: _nsfwDescription,
        Tags: _emptyTagSet);

    private readonly LightlessGroupProfileData _nsfwProfileGroupData = new(
        IsDisabled: false,
        IsNsfw: true,
        Base64ProfilePicture: _lightlessLogoNsfw,
        Base64BannerPicture: string.Empty,
        Description: _nsfwDescription,
        Tags: _emptyTagSet);

    private const string _noDescription = "-- Profile has no description set --";
    private readonly ConcurrentDictionary<UserData, LightlessProfileData> _lightlessProfiles = new(UserDataComparer.Instance);
    private readonly LightlessProfileData _defaultProfileData = new(
        IsFlagged: false,
        IsNSFW: false,
        Base64ProfilePicture: _lightlessLogo,
        Base64SupporterPicture: string.Empty,
        Base64BannerPicture: _lightlessBanner,
        Description: _noDescription,
        Tags: _emptyTagSet);

    private readonly LightlessProfileData _loadingProfileData = new(
        IsFlagged: false,
        IsNSFW: false,
        Base64ProfilePicture: _lightlessLogoLoading,
        Base64SupporterPicture: string.Empty,
        Base64BannerPicture: _lightlessBanner,
        Description: _loadingData,
        Tags: _emptyTagSet);

    private readonly LightlessProfileData _nsfwProfileData = new(
        IsFlagged: false,
        IsNSFW: false,
        Base64ProfilePicture: _lightlessLogoNsfw,
        Base64SupporterPicture: string.Empty,
        Base64BannerPicture: string.Empty,
        Description: _nsfwDescription,
        Tags: _emptyTagSet);

    public LightlessProfileManager(ILogger<LightlessProfileManager> logger, LightlessConfigService lightlessConfigService,
        LightlessMediator mediator, ApiController apiController) : base(logger, mediator)
    {
        _logger = logger;
        _lightlessConfigService = lightlessConfigService;
        _apiController = apiController;

        Mediator.Subscribe<ClearProfileUserDataMessage>(this, (msg) =>
        {
            if (msg.UserData != null)
            {
                _logger.LogTrace("Received Clear Profile for User profile {data}", msg.UserData.AliasOrUID);
                _lightlessUserProfiles.Remove(msg.UserData, out _);
                _lightlessProfiles.Remove(msg.UserData, out _);
            }
            else
            {
                _logger.LogTrace("Received Clear Profile for all User profiles");
                _lightlessUserProfiles.Clear();
                _lightlessProfiles.Clear();
            }
        });

        Mediator.Subscribe<ClearProfileGroupDataMessage>(this, (msg) =>
        {
            if (msg.GroupData != null)
            {
                _logger.LogTrace("Received Clear Profile for Group profile {data}", msg.GroupData.AliasOrGID);
                _lightlessGroupProfiles.Remove(msg.GroupData, out _);
            }
            else
            {
                _logger.LogTrace("Received Clear Profile for all Group profiles");
                _lightlessGroupProfiles.Clear();
            }

        });

        Mediator.Subscribe<DisconnectedMessage>(this, (_) =>
        {
            _logger.LogTrace("Received Disconnect, Clearing Profiles");
            _lightlessUserProfiles.Clear();
            _lightlessGroupProfiles.Clear();
            _lightlessProfiles.Clear();
        }
        );
    }

    /// <summary>
    /// Fetches User Profile from cache or API
    /// </summary>
    /// <param name="data">User Data of given user</param>
    /// <returns>LightlessUserProfileData of given user</returns>
    public LightlessUserProfileData GetLightlessUserProfile(UserData data)
    {
        if (!_lightlessUserProfiles.TryGetValue(data, out var profile))
        {
            _logger.LogTrace("Requesting User profile for {data}", data);
            _ = Task.Run(() => GetLightlessProfileFromService(data));
            return (_loadingProfileUserData);
        }

        return (profile);
    }

    public LightlessProfileData GetLightlessProfile(UserData data)
    {
        if (!_lightlessProfiles.TryGetValue(data, out var profile))
        {
            _logger.LogTrace("Requesting Lightless profile for {data}", data);
            _ = Task.Run(() => GetLightlessProfileFromService(data));
            return _loadingProfileData;
        }

        return profile;
    }


    /// <summary>
    /// Fetches Group Profile from cache or API
    /// </summary>
    /// <param name="data">Group Data of given group</param>
    /// <returns>LightlessGroupProfileData of given group</returns>
    public LightlessGroupProfileData GetLightlessGroupProfile(GroupData data)
    {
        if (!_lightlessGroupProfiles.TryGetValue(data, out var profile))
        {
            _logger.LogTrace("Requesting group profile for {data}", data);
            _ = Task.Run(() => GetLightlessProfileFromService(data));
            return (_loadingProfileGroupData);
        }

        return (profile);
    }

    /// <summary>
    /// Fetching the user profile data from the API
    /// </summary>
    /// <param name="data">User you want the profile from</param>
    /// <returns>New entry in the user profiles to fetch</returns>
    private async Task GetLightlessProfileFromService(UserData data)
    {
        try
        {
            _logger.LogTrace("Inputting loading data in _lightlessUserProfiles for User {data}", data.AliasOrUID);
            _lightlessUserProfiles[data] = _loadingProfileUserData;
            _lightlessProfiles[data] = _loadingProfileData;
            var profile = await _apiController.UserGetProfile(new API.Dto.User.UserDto(data)).ConfigureAwait(false);
            var tags = profile.Tags ?? _emptyTagSet;
            var profileData = BuildProfileData(data, profile, tags);
            var supporterImage = !string.IsNullOrEmpty(data.Alias) && !string.Equals(data.Alias, data.UID, StringComparison.Ordinal)
                ? _lightlessSupporter
                : string.Empty;
            LightlessUserProfileData profileUserData = new(
                IsFlagged: profile.Disabled,
                IsNSFW: profile.IsNSFW ?? false,
                Base64ProfilePicture: string.IsNullOrEmpty(profile.ProfilePictureBase64) ? _lightlessLogo : profile.ProfilePictureBase64,
                Base64SupporterPicture: supporterImage,
                Base64BannerPicture: string.IsNullOrEmpty(profile.BannerPictureBase64) ? _lightlessBanner : profile.BannerPictureBase64,
                Description: string.IsNullOrEmpty(profile.Description) ? _noUserDescription : profile.Description,
                Tags: tags);

            _logger.LogTrace("Replacing data in _lightlessUserProfiles for User {data}", data.AliasOrUID);
            if (profileUserData.IsNSFW && !_lightlessConfigService.Current.ProfilesAllowNsfw && !string.Equals(_apiController.UID, data.UID, StringComparison.Ordinal))
            {
                _lightlessUserProfiles[data] = _nsfwProfileUserData;
                _lightlessProfiles[data] = _nsfwProfileData;
            }
            else
            {
                _lightlessUserProfiles[data] = profileUserData;
                _lightlessProfiles[data] = profileData;
            }
        }
        catch (Exception ex)
        {
            // if fails save DefaultProfileData to dict
            Logger.LogWarning(ex, "Failed to get Profile from service for user {user}", data);
            _lightlessUserProfiles[data] = _defaultProfileUserData;
            _lightlessProfiles[data] = _defaultProfileData;
        }
    }

    /// <summary>
    /// Fetching the group profile data from the API
    /// </summary>
    /// <param name="data">Group you want the profile from</param>
    /// <returns>New entry in the group profiles to fetch</returns>
    private async Task GetLightlessProfileFromService(GroupData data)
    {
        try
        {
            _logger.LogTrace("Inputting loading data in _lightlessGroupProfiles for Group {data}", data.AliasOrGID);
            _lightlessGroupProfiles[data] = _loadingProfileGroupData;
            var profile = await _apiController.GroupGetProfile(new API.Dto.Group.GroupDto(data)).ConfigureAwait(false);
            var tags = profile.Tags ?? _emptyTagSet;

            LightlessGroupProfileData profileGroupData = new(
                IsDisabled: profile.IsDisabled ?? false,
                IsNsfw: profile.IsNsfw ?? false,
                Base64ProfilePicture: string.IsNullOrEmpty(profile.PictureBase64) ? _lightlessLogo : profile.PictureBase64,
                Base64BannerPicture: string.IsNullOrEmpty(profile.BannerBase64) ? _lightlessBanner : profile.BannerBase64,
                Description: string.IsNullOrEmpty(profile.Description) ? _noGroupDescription : profile.Description,
                Tags: tags);

            _logger.LogTrace("Replacing data in _lightlessGroupProfiles for Group {data}", data.AliasOrGID);
            if (profileGroupData.IsNsfw && !_lightlessConfigService.Current.ProfilesAllowNsfw)
            {
                _lightlessGroupProfiles[data] = _nsfwProfileGroupData;
            }
            else
            {
                _lightlessGroupProfiles[data] = profileGroupData;
            }
        }
        catch (Exception ex)
        {
            // if fails save DefaultProfileData to dict
            Logger.LogWarning(ex, "Failed to get Profile from service for syncshell {group}", data);
            _lightlessGroupProfiles[data] = _defaultProfileGroupData;
        }
    }

    private LightlessProfileData BuildProfileData(UserData data, UserProfileDto profile, IReadOnlyList<int> tags)
    {
        var supporterImage = !string.IsNullOrEmpty(data.Alias) && !string.Equals(data.Alias, data.UID, StringComparison.Ordinal)
            ? _lightlessSupporter
            : string.Empty;
        var profileData = new LightlessProfileData(
            IsFlagged: profile.Disabled,
            IsNSFW: profile.IsNSFW ?? false,
            Base64ProfilePicture: string.IsNullOrEmpty(profile.ProfilePictureBase64) ? _lightlessLogo : profile.ProfilePictureBase64,
            Base64SupporterPicture: supporterImage,
            Base64BannerPicture: string.IsNullOrEmpty(profile.BannerPictureBase64) ? _lightlessBanner : profile.BannerPictureBase64,
            Description: string.IsNullOrEmpty(profile.Description) ? _noDescription : profile.Description,
            Tags: tags);

        if (profileData.IsNSFW && !_lightlessConfigService.Current.ProfilesAllowNsfw && !string.Equals(_apiController.UID, data.UID, StringComparison.Ordinal))
        {
            return _nsfwProfileData;
        }

        return profileData;
    }

    public async Task<(UserData User, LightlessProfileData ProfileData)?> GetLightfinderProfileAsync(string hashedCid)
    {
        if (string.IsNullOrWhiteSpace(hashedCid))
            return null;

        try
        {
            var profile = await _apiController.UserGetLightfinderProfile(hashedCid).ConfigureAwait(false);
            if (profile == null)
                return null;

            if (profile.User is null)
            {
                Logger.LogWarning("Lightfinder profile response missing user info for CID {HashedCid}", hashedCid);
            }

            var userData = profile.User ?? new UserData(hashedCid, Alias: "Lightfinder User");
            var profileTags = profile.Tags ?? _emptyTagSet;
            var profileData = BuildProfileData(userData, profile, profileTags);
            _lightlessProfiles[userData] = profileData;

            return (userData, profileData);
        }
        catch (Exception ex)
        {
            Logger.LogWarning(ex, "Failed to get Lightfinder profile for CID {HashedCid}", hashedCid);
            return null;
        }
    }
}