using LightlessSync.API.Data;
using LightlessSync.API.Data.Enum;
using LightlessSync.API.Data.Extensions;
using LightlessSync.API.Dto.Group;

namespace LightlessSync.PlayerData.Pairs;

/// <summary>
///     core models for the pair system
/// </summary>
public sealed class PairState
{
    public CharacterData? CharacterData { get; set; }
    public Guid? TemporaryCollectionId { get; set; }

    public bool IsEmpty => CharacterData is null && (TemporaryCollectionId is null || TemporaryCollectionId == Guid.Empty);
}

public readonly record struct PairUniqueIdentifier(string UserId);

/// <summary>
///     link between a pair id and character ident
/// </summary>
public sealed record PairRegistration(PairUniqueIdentifier PairIdent, string? CharacterIdent);

/// <summary>
///     per group membership info for a pair
/// </summary>
public sealed class GroupPairRelationship
{
    public GroupPairRelationship(string groupId, GroupPairUserInfo? info)
    {
        GroupId = groupId;
        UserInfo = info;
    }

    public string GroupId { get; }
    public GroupPairUserInfo? UserInfo { get; private set; }

    public void SetUserInfo(GroupPairUserInfo? info)
    {
        UserInfo = info;
    }
}

/// <summary>
///     runtime view of a single pair connection
/// </summary>
public sealed class PairConnection
{
    public PairConnection(UserData user)
    {
        User = user;
        Groups = new Dictionary<string, GroupPairRelationship>(StringComparer.Ordinal);
    }

    public UserData User { get; }
    public bool IsOnline { get; private set; }
    public string? Ident { get; private set; }
    public UserPermissions SelfToOtherPermissions { get; private set; } = UserPermissions.NoneSet;
    public UserPermissions OtherToSelfPermissions { get; private set; } = UserPermissions.NoneSet;
    public IndividualPairStatus? IndividualPairStatus { get; private set; }
    public Dictionary<string, GroupPairRelationship> Groups { get; }

    public bool IsPaused => SelfToOtherPermissions.IsPaused();
    public bool IsDirectlyPaired => IndividualPairStatus is not null && IndividualPairStatus != API.Data.Enum.IndividualPairStatus.None;
    public bool IsOneSided => IndividualPairStatus == API.Data.Enum.IndividualPairStatus.OneSided;
    public bool HasAnyConnection => IsDirectlyPaired || Groups.Count > 0;

    public void SetOnline(string? ident)
    {
        IsOnline = true;
        Ident = ident;
    }

    public void SetOffline()
    {
        IsOnline = false;
    }

    public void UpdatePermissions(UserPermissions own, UserPermissions other)
    {
        SelfToOtherPermissions = own;
        OtherToSelfPermissions = other;
    }

    public void UpdateStatus(IndividualPairStatus? status)
    {
        IndividualPairStatus = status;
    }

    public void EnsureGroupRelationship(string groupId, GroupPairUserInfo? info)
    {
        if (Groups.TryGetValue(groupId, out var relationship))
        {
            relationship.SetUserInfo(info);
        }
        else
        {
            Groups[groupId] = new GroupPairRelationship(groupId, info);
        }
    }

    public void RemoveGroupRelationship(string groupId)
    {
        Groups.Remove(groupId);
    }
}

/// <summary>
///     syncshell metadata plus member connections
/// </summary>
public sealed class Syncshell
{
    public Syncshell(GroupFullInfoDto dto)
    {
        GroupFullInfo = dto;
        Users = new Dictionary<string, PairConnection>(StringComparer.Ordinal);
    }

    public GroupFullInfoDto GroupFullInfo { get; private set; }
    public Dictionary<string, PairConnection> Users { get; }

    public void Update(GroupFullInfoDto dto)
    {
        GroupFullInfo = dto;
    }
}

/// <summary>
///     simple success/failure result
/// </summary>
public readonly struct PairOperationResult
{
    private PairOperationResult(bool success, string? error)
    {
        Success = success;
        Error = error;
    }

    public bool Success { get; }
    public string? Error { get; }

    public static PairOperationResult Ok() => new(true, null);

    public static PairOperationResult Fail(string error) => new(false, error);
}

/// <summary>
///     typed success/failure result
/// </summary>
public readonly struct PairOperationResult<T>
{
    private PairOperationResult(bool success, T value, string? error)
    {
        Success = success;
        Value = value;
        Error = error;
    }

    public bool Success { get; }
    public T Value { get; }
    public string? Error { get; }

    public static PairOperationResult<T> Ok(T value) => new(true, value, null);

    public static PairOperationResult<T> Fail(string error) => new(false, default!, error);
}

/// <summary>
///     state of which optional plugin warnings were shown
/// </summary>
public record OptionalPluginWarning
{
    public bool ShownHeelsWarning { get; set; } = false;
    public bool ShownCustomizePlusWarning { get; set; } = false;
    public bool ShownHonorificWarning { get; set; } = false;
    public bool ShownMoodlesWarning { get; set; } = false;
    public bool ShowPetNicknamesWarning { get; set; } = false;
}

/// <summary>
///     tracks the handler registered pairs for an ident
/// </summary>
internal sealed class PairHandlerEntry
{
    private readonly HashSet<PairUniqueIdentifier> _pairs = new();

    public PairHandlerEntry(string ident, IPairHandlerAdapter handler)
    {
        Ident = ident;
        Handler = handler;
    }

    public string Ident { get; }
    public IPairHandlerAdapter Handler { get; }

    public bool HasPairs => _pairs.Count > 0;
    public int PairCount => _pairs.Count;

    public void AddPair(PairUniqueIdentifier pair)
    {
        _pairs.Add(pair);
    }

    public bool RemovePair(PairUniqueIdentifier pair)
    {
        return _pairs.Remove(pair);
    }

    public IReadOnlyCollection<PairUniqueIdentifier> SnapshotPairs()
    {
        if (_pairs.Count == 0)
        {
            return Array.Empty<PairUniqueIdentifier>();
        }

        return _pairs.ToArray();
    }
}
