using ExtensiblePortfolioSite.SDK.Git;

using System;
using System.Collections.Generic;

namespace ExtensiblePortfolioSite.SDK.Caching
{
    /// <summary>
    /// Git Service Cache Provider
    /// </summary>
    public sealed class GitServiceCache : IGitCacheProvider, IDisposable
    {
        /// <inheritdoc cref="IGitCacheProvider.ServiceProvider"/>
        public GitService ServiceProvider { get; }
        private readonly LinkedList<GitCacheItemBase> CacheItems = new();
        private Boolean _disposing = false;

        internal GitServiceCache(String Provider) : this(GitManager.GetService(Provider)) { }
        internal GitServiceCache(GitService ServiceProvider)
        {
            this.ServiceProvider = ServiceProvider;
            this.ServiceProvider.OnChange += ServiceProvider_OnChange;
        }

        private void ServiceProvider_OnChange(GitService Git)
        {
            lock(this.CacheItems)
            {
                foreach (GitCacheItemBase Item in this.CacheItems)
                    Item.Invalidate();
            }
        }
        

        private readonly SortedDictionary<String, GitCacheItemBase<IUser>> UserCaches = new();
        private readonly SortedDictionary<String, GitCacheItemBase<IRepository>> RepositoryCaches = new();


        /// <inheritdoc cref="IGitCacheProvider.GetUser(Uri)"/>
        public GitCacheItemBase<IUser> GetUser(Uri UserProfile)
        {
            String url = UserProfile.ToString();
            if (UserCaches.TryGetValue(url, out GitCacheItemBase<IUser>? cache))
                return cache;

            lock (UserCaches)
            {
                if (UserCaches.TryGetValue(url, out cache))
                    return cache;

                cache = new CacheUser(this, UserProfile);
                UserCaches.Add(url, cache);
                return cache;
            }
        }
        /// <inheritdoc cref="IGitCacheProvider.GetRepository(Uri)"/>
        public GitCacheItemBase<IRepository> GetRepository(Uri RepositoryURL)
        {
            String url = RepositoryURL.ToString();
            if (RepositoryCaches.TryGetValue(url, out GitCacheItemBase<IRepository>? cache))
                return cache;

            lock (RepositoryCaches)
            {
                if (RepositoryCaches.TryGetValue(url, out cache))
                    return cache;

                cache = new CacheRepo(this, RepositoryURL);
                RepositoryCaches.Add(url, cache);
                return cache;
            }
        }


        /// <inheritdoc cref="ICacheProvider{GitCacheItemBase}.Register(GitCacheItemBase)"/>
        public void Register(GitCacheItemBase CacheItem)
        {
            if (!_disposing)
                lock (CacheItems)
                {
                    this.CacheItems.AddFirst(CacheItem);
                }
        }
        /// <inheritdoc cref="ICacheProvider{GitCacheItemBase}.Unregister(GitCacheItemBase)"/>
        public void Unregister(GitCacheItemBase CacheItem)
        {
            if (!_disposing)
                lock (CacheItems)
                    if (this.CacheItems.Remove(CacheItem)) ;
        }


        /// <inheritdoc cref="IDisposable"/>
        public void Dispose()
        {
            _disposing = true;
            lock (CacheItems)
            {
                this.ServiceProvider.OnChange -= ServiceProvider_OnChange;
                this.RepositoryCaches.Clear();
                this.UserCaches.Clear();
                foreach (GitCacheItemBase Item in CacheItems)
                    Item.Dispose();
            }
        }
    }
}
