using LightlessSync.API.Data.Enum;

namespace LightlessSync.LightlessConfiguration.Configurations;

public class TransientConfig : ILightlessConfiguration
{
    public Dictionary<string, TransientPlayerConfig> TransientConfigs { get; set; } = [];
    public int Version { get; set; } = 2;

    public class TransientPlayerConfig
    {
        public List<string> GlobalPersistentCache { get; set; } = [];
        public Dictionary<uint, List<string>> JobSpecificCache { get; set; } = [];
        public Dictionary<uint, List<string>> JobSpecificPetCache { get; set; } = [];

        private readonly object _cacheLock = new();

        public TransientPlayerConfig()
        {

        }

        private bool ElevateIfNeeded(uint jobId, string gamePath)
        {
            // check if it's in the job cache of other jobs and elevate if needed
            foreach (var kvp in JobSpecificCache)
            {
                if (kvp.Key == jobId) continue;

                // elevate if the gamepath is included somewhere else
                if (kvp.Value.Contains(gamePath, StringComparer.Ordinal))
                {
                    JobSpecificCache[kvp.Key].Remove(gamePath);
                    GlobalPersistentCache.Add(gamePath);
                    return true;
                }
            }

            return false;
        }

        public int RemovePath(string gamePath, ObjectKind objectKind)
        {
            lock (_cacheLock)
            {
                int removedEntries = 0;
                if (objectKind == ObjectKind.Player)
                {
                    if (GlobalPersistentCache.Remove(gamePath)) removedEntries++;
                    foreach (var kvp in JobSpecificCache)
                    {
                        if (kvp.Value.Remove(gamePath)) removedEntries++;
                    }
                }
                if (objectKind == ObjectKind.Pet)
                {
                    foreach (var kvp in JobSpecificPetCache)
                    {
                        if (kvp.Value.Remove(gamePath)) removedEntries++;
                    }
                }
                return removedEntries;
            }
        }

        public void AddOrElevate(uint jobId, string gamePath)
        {
            lock (_cacheLock)
            {
                // check if it's in the global cache, if yes, do nothing
                if (GlobalPersistentCache.Contains(gamePath, StringComparer.Ordinal))
                {
                    return;
                }

                if (ElevateIfNeeded(jobId, gamePath)) return;

                // check if the jobid is already in the cache to start
                if (!JobSpecificCache.TryGetValue(jobId, out var jobCache))
                {
                    JobSpecificCache[jobId] = jobCache = new();
                }

                // check if the path is already in the job specific cache
                if (!jobCache.Contains(gamePath, StringComparer.Ordinal))
                {
                    jobCache.Add(gamePath);
                }
            }
        }

        public bool NormalizePaths(out int removedEntries)
        {
            bool changed = false;
            removedEntries = 0;

            GlobalPersistentCache = NormalizeList(GlobalPersistentCache, ref changed, ref removedEntries);

            foreach (var jobId in JobSpecificCache.Keys.ToList())
            {
                JobSpecificCache[jobId] = NormalizeList(JobSpecificCache[jobId], ref changed, ref removedEntries);
            }

            foreach (var jobId in JobSpecificPetCache.Keys.ToList())
            {
                JobSpecificPetCache[jobId] = NormalizeList(JobSpecificPetCache[jobId], ref changed, ref removedEntries);
            }

            return changed;
        }

        private static List<string> NormalizeList(List<string> entries, ref bool changed, ref int removedEntries)
        {
            if (entries.Count == 0)
                return entries;

            var result = new List<string>(entries.Count);
            var seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

            foreach (var entry in entries)
            {
                var normalized = NormalizePath(entry);
                if (string.IsNullOrEmpty(normalized))
                {
                    changed = true;
                    continue;
                }

                if (!string.Equals(entry, normalized, StringComparison.Ordinal))
                {
                    changed = true;
                }

                if (seen.Add(normalized))
                {
                    result.Add(normalized);
                }
                else
                {
                    changed = true;
                }
            }

            removedEntries += entries.Count - result.Count;

            return result;
        }

        private static string NormalizePath(string path)
        {
            if (string.IsNullOrEmpty(path))
                return string.Empty;

            return path.Replace("\\", "/", StringComparison.Ordinal).ToLowerInvariant();
        }
    }
}
