using ExtensiblePortfolioSite.SDK;
using ExtensiblePortfolioSite.SDK.Git;
using ExtensiblePortfolioSite.SDK.Plugins;

using System.Text.Json;
using System.Text.RegularExpressions;

namespace GithubPlugin
{
    [GitProvider("Github")]
    [Serializable]
    public class GithubProvider : IGitProvider
    {
        private HttpClient httpClient = new HttpClient();

        private Regex userPattern = new Regex("^(http(s)*:\\/\\/)(www\\.)*(github\\.com\\/[A-z]+)$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
        private Regex repoPattern = new Regex("^(http(s)*:\\/\\/)(www\\.)*(github\\.com\\/[A-z]+\\/[A-z]+)$", RegexOptions.Compiled | RegexOptions.IgnoreCase);

        public Dictionary<string, GithubUser> Users = new();
        public Dictionary<string, GithubRepo> Repositories = new();

        public GithubProvider()
        {
            this.httpClient.BaseAddress = new("https://api.github.com/");
            this.httpClient.DefaultRequestHeaders.Add("User-Agent", "KoromaruKoruko");
            this.httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer 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");
        }

        public IGitObject GetByReference(GitReference Reference)
        {
            switch(Reference.Kind)
            {
                case GitReferenceKind.User:
                    if(TryGetUserByName(Reference.ReferenceString, out IUser? user))
                        return user!;
                    else
                        return null;
                case GitReferenceKind.Repository:
                    {
                        if (TryGetRepositoryByName(
                            Reference.ReferenceString.Split('/')[0], Reference.ReferenceString.Split('/')[1], out IRepository? repo))
                            return repo!;
                        else
                            return null;
                    }
                case GitReferenceKind.Commit:
                    {
                        if (TryGetRepositoryByName(
                            Reference.ReferenceString.Split('/')[0], Reference.ReferenceString.Split('/')[1], out IRepository? repo))
                        {
                            if (repo!.TryGetCommitByRef(Reference.ReferenceString.Split('/')[2], out ICommit? commit))
                                return commit!;
                            else
                                return null;
                        }
                        else
                            return null;
                    }
            }
            return null;
        }

        public bool TryGetRepositoryByName(string Username, string RepositoryName, out IRepository? Repository)
        {
            if (TryGetUserByName(Username, out IUser? User))
                return TryGetRepositoryByName(User!, RepositoryName, out Repository);
            Repository = null;
            return false;
        }

        public bool TryGetRepositoryByName(IUser User, string RepositoryName, out IRepository? Repository)
        {
            if(User is GithubUser _user)
            {
                if (Repositories.TryGetValue(RepositoryName, out GithubRepo? _repo))
                {
                    Repository = _repo;
                    return true;
                } else
                {
                    var response = httpClient.GetAsync($"repos/{_user.Name}/{RepositoryName}").Result;
                    if (!response.IsSuccessStatusCode)
                    {
                        Repository = null;
                        return false;
                    }
                    GithubRepo tempRepo = Json.Deserialize<GithubRepo>(response.Content.ReadAsStream())!;
                    tempRepo.Provider = this;
                    tempRepo.Owner = _user;
                    Repository = tempRepo;
                    return true;
                }
            }
            Repository = null;
            return false;
        }

        public bool TryGetUserByName(string Username, out IUser? User)
        {
            if(Users.TryGetValue(Username, out GithubUser? _user))
            {
                User = _user;
                return true;
            } else
            {
                var response = httpClient.GetAsync($"users/{Username}").Result;
                if(!response.IsSuccessStatusCode)
                {
                    User = null;
                    return false;
                }
                GithubUser tempUser = Json.Deserialize<GithubUser>(response.Content.ReadAsStringAsync().Result)!;
                tempUser.Provider = this;
                User = tempUser;
                return true;
            }
        }

        public bool TryGetRepositoryByURL(Uri RepoUrl, out IRepository? Repository)
        {
            if (repoPattern.IsMatch(RepoUrl.OriginalString))
            {
                var split = RepoUrl.OriginalString.Split("://")[1].Split('/');
                return TryGetRepositoryByName(split[1], split[2], out Repository);
            } else
            {
                Repository = null;
                return false;
            }
        }

        public bool TryGetUserByURL(Uri UserProfile, out IUser? User)
        {
            if (userPattern.IsMatch(UserProfile.OriginalString))
            {
                var split = UserProfile.OriginalString.Split("://")[1].Split('/');
                return TryGetUserByName(split[1], out User);
            }
            else
            {
                User = null;
                return false;
            }
        }

        public HttpResponseMessage GetAPIResource(string resourceLocation)
        {
            return httpClient.GetAsync(resourceLocation).Result;
        }

    }
}