using System;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
using StardewModdingAPI.Common;
using StardewModdingAPI.Web.Framework.Clients.GitHub;
using StardewModdingAPI.Web.ViewModels;

namespace StardewModdingAPI.Web.Controllers
{
    /// <summary>Provides an info/download page about SMAPI.</summary>
    [Route("")]
    [Route("install")]
    internal class IndexController : Controller
    {
        /*********
        ** Properties
        *********/
        /// <summary>The cache in which to store release data.</summary>
        private readonly IMemoryCache Cache;

        /// <summary>The GitHub API client.</summary>
        private readonly IGitHubClient GitHub;

        /// <summary>The cache time for release info.</summary>
        private readonly TimeSpan CacheTime = TimeSpan.FromSeconds(1);

        /// <summary>The GitHub repository name to check for update.</summary>
        private readonly string RepositoryName = "Pathoschild/SMAPI";


        /*********
        ** Public methods
        *********/
        /// <summary>Construct an instance.</summary>
        /// <param name="cache">The cache in which to store release data.</param>
        /// <param name="github">The GitHub API client.</param>
        public IndexController(IMemoryCache cache, IGitHubClient github)
        {
            this.Cache = cache;
            this.GitHub = github;
        }

        /// <summary>Display the index page.</summary>
        [HttpGet]
        public async Task<ViewResult> Index()
        {
            // fetch SMAPI releases
            IndexVersionModel stableVersion = await this.Cache.GetOrCreateAsync("stable-version", async entry =>
            {
                entry.AbsoluteExpiration = DateTimeOffset.UtcNow.Add(this.CacheTime);
                GitRelease release = await this.GitHub.GetLatestReleaseAsync(this.RepositoryName, includePrerelease: false);
                return new IndexVersionModel(release.Name, release.Body, this.GetMainDownloadUrl(release), this.GetDevDownloadUrl(release));
            });
            IndexVersionModel betaVersion = await this.Cache.GetOrCreateAsync("beta-version", async entry =>
            {
                entry.AbsoluteExpiration = DateTimeOffset.UtcNow.Add(this.CacheTime);
                GitRelease release = await this.GitHub.GetLatestReleaseAsync(this.RepositoryName, includePrerelease: true);
                return release.IsPrerelease
                    ? this.GetBetaDownload(release)
                    : null;
            });

            // render view
            var model = new IndexModel(stableVersion, betaVersion);
            return this.View(model);
        }


        /*********
        ** Private methods
        *********/
        /// <summary>Get the main download URL for a SMAPI release.</summary>
        /// <param name="release">The SMAPI release.</param>
        private string GetMainDownloadUrl(GitRelease release)
        {
            // get main download URL
            foreach (GitAsset asset in release.Assets ?? new GitAsset[0])
            {
                if (Regex.IsMatch(asset.FileName, @"SMAPI-[\d\.]+-installer.zip"))
                    return asset.DownloadUrl;
            }

            // fallback just in case
            return "https://github.com/pathoschild/SMAPI/releases";
        }

        /// <summary>Get the for-developers download URL for a SMAPI release.</summary>
        /// <param name="release">The SMAPI release.</param>
        private string GetDevDownloadUrl(GitRelease release)
        {
            // get dev download URL
            foreach (GitAsset asset in release.Assets ?? new GitAsset[0])
            {
                if (Regex.IsMatch(asset.FileName, @"SMAPI-[\d\.]+-installer-for-developers.zip"))
                    return asset.DownloadUrl;
            }

            // fallback just in case
            return "https://github.com/pathoschild/SMAPI/releases";
        }

        /// <summary>Get the latest beta download for a SMAPI release.</summary>
        /// <param name="release">The SMAPI release.</param>
        private IndexVersionModel GetBetaDownload(GitRelease release)
        {
            // get download with the latest version
            SemanticVersionImpl latestVersion = null;
            string latestUrl = null;
            foreach (GitAsset asset in release.Assets ?? new GitAsset[0])
            {
                // parse version
                Match versionMatch = Regex.Match(asset.FileName, @"SMAPI-([\d\.]+(?:-.+)?)-installer.zip");
                if (!versionMatch.Success || !SemanticVersionImpl.TryParse(versionMatch.Groups[1].Value, out SemanticVersionImpl version))
                    continue;

                // save latest version
                if (latestVersion == null || latestVersion.CompareTo(version) < 0)
                {
                    latestVersion = version;
                    latestUrl = asset.DownloadUrl;
                }
            }

            // return if prerelease
            return latestVersion?.Tag != null
                ? new IndexVersionModel(latestVersion.ToString(), release.Body, latestUrl, null)
                : null;
        }
    }
}