using ExtensiblePortfolioSite.SDK.Caching;
using ExtensiblePortfolioSite.SDK.Plugins;

using System;
using System.Collections.Generic;

namespace ExtensiblePortfolioSite.SDK.Git
{
    /// <summary>
    /// Git Service Changed Delegate/Callback
    /// </summary>
    /// <param name="Git">Sender</param>
    public delegate void GitServiceChangedDelegate(GitService Git);

    /// <summary>
    /// Git Service
    /// </summary>
    public sealed class GitService
    {
        /// <summary>
        /// Git Service Name
        /// </summary>
        public String Service { get; }

        /// <summary>
        /// OnChanged Event Callback
        /// </summary>
        public event GitServiceChangedDelegate? OnChange;

        internal GitService(String Service) => this.Service = Service;

        private readonly SortedDictionary<String, IGitProvider> Providers = new();

        internal void I_RemoveProvider(Plugin FromPlugin, IGitProvider provider)
        {
            lock (this.Providers)
                this.Providers.Add(FromPlugin.Info.Name, provider);

            OnChange?.Invoke(this);
        }
        internal void I_AddProvider(Plugin FromPlugin, IGitProvider provider)
        {
            lock (this.Providers)
                this.Providers.Add(FromPlugin.Info.Name, provider);

            OnChange?.Invoke(this);
        }

        private GitServiceCache? ServiceCache;

        /// <summary>
        /// Gets the Service Cache
        /// </summary>
        /// <returns></returns>
        public GitServiceCache GetCache() => this.ServiceCache ??= Cache.GetGitServiceCache(this.Service);

        /// <summary>
        /// Attempt to Retrive a GitProvider by its PluginName
        /// </summary>
        /// <param name="PluginName"></param>
        /// <param name="Provider"></param>
        /// <returns></returns>
        public void TryGetProviderByPlugin(String PluginName, out IGitProvider? Provider) => this.Providers.TryGetValue(PluginName, out Provider);

        /// <summary>
        /// Retrives a Specified User by URL
        /// </summary>
        /// <param name="URL"></param>
        /// <returns></returns>
        /// <exception cref="GitObjectNotFoundException"></exception>
        public IUser GetUser(Uri URL)
        {
            lock (this.Providers)
                foreach (IGitProvider Provider in this.Providers.Values)
                    if (Provider.TryGetUserByURL(URL, out IUser? User))
                        return User!;

            throw new GitObjectNotFoundException(GitReferenceKind.User, URL);

        }

        /// <summary>
        /// Retrives a Specified Repository by URL
        /// </summary>
        /// <param name="URL"></param>
        /// <returns></returns>
        /// <exception cref="GitObjectNotFoundException"></exception>
        public IRepository GetRepository(Uri URL)
        {
            lock (this.Providers)
                foreach (IGitProvider Provider in this.Providers.Values)
                    if (Provider.TryGetRepositoryByURL(URL, out IRepository? Repository))
                        return Repository!;

            throw new GitObjectNotFoundException(GitReferenceKind.Repository, URL);

        }
    }
}
