
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using NuGet.Packaging.Core;
using NuGet.Versioning;
using isn.Data;
using isn.Helpers;

namespace isn.Controllers
{

    public partial class PackagesController
    {

        [HttpPut("packages")]
        public async Task<IActionResult> Put()
        {
            try
            {
                var clientVersionId = Request.Headers["X-NuGet-Client-Version"];
                var apiKey = Request.Headers["X-NuGet-ApiKey"];
                ViewData["versionId"] = typeof(PackagesController).Assembly.FullName;
                var files = new List<string>();
                ViewData["files"] = files;

                var clearkey = protector.Unprotect(apiKey);
                var apikey = dbContext.ApiKeys.SingleOrDefault(k => k.Id == clearkey);
                if (apikey == null)
                {
                    logger.LogError("403 : no api-key");
                    return Unauthorized();
                }

                foreach (var file in Request.Form.Files)
                {
                    string initpath = Path.Combine(Environment.GetEnvironmentVariable("TEMP") ??
                    Environment.GetEnvironmentVariable("TMP") ?? "/tmp",
                     $"isn-{Guid.NewGuid()}.nupkg");

                    using (FileStream fw = new FileStream(initpath, FileMode.Create))
                    {
                        file.CopyTo(fw);
                    }

                    using (FileStream fw = new FileStream(initpath, FileMode.Open))
                    {
                        var archive = new ZipArchive(fw);

                        var nuspec = archive.Entries.FirstOrDefault(e => e.FullName.EndsWith(".nuspec"));
                        if (nuspec == null) return BadRequest(new { error = "no nuspec from archive" });
                        string pkgpath;
                        NuGetVersion version;
                        string pkgid;
                        string fullpath;

                        using (var specstr = nuspec.Open())
                        {
                            NuspecCoreReader reader = new NuspecCoreReader(specstr);

                            string pkgdesc = reader.GetDescription();
                            var types = reader.GetPackageTypes();
                            pkgid = reader.GetId();
                            version = reader.GetVersion();

                            string pkgidpath = Path.Combine(nugetSettings.PackagesRootDir,
                                pkgid);
                            pkgpath = Path.Combine(pkgidpath, version.ToFullString());
                            string name = $"{pkgid}-{version}.nupkg";
                            fullpath = Path.Combine(pkgpath, name);

                            var destpkgiddir = new DirectoryInfo(pkgidpath);
                            Package package = dbContext.Packages.SingleOrDefault(p => p.Id == pkgid);
                            if (package != null)
                            {
                                if (package.OwnerId != apikey.UserId)
                                {
                                    return new ForbidResult();
                                }
                                package.Description = pkgdesc;
                            }
                            else
                            {
                                package = new Package
                                {
                                    Id = pkgid,
                                    Description = pkgdesc,
                                    OwnerId = apikey.UserId
                                };
                                dbContext.Packages.Add(package);
                            }
                            if (!destpkgiddir.Exists) destpkgiddir.Create();

                            var source = new FileInfo(initpath);
                            var dest = new FileInfo(fullpath);
                            var destdir = new DirectoryInfo(dest.DirectoryName);
                            if (dest.Exists)
                            {
                                ViewData["msg"] = "existant";
                                ViewData["ecode"] = 1;
                                logger.LogWarning("400 : existant");
                                return base.BadRequest(new { error = ModelState });
                            }
                            else
                            {
                                destdir.Create();
                                source.MoveTo(fullpath);
                                files.Add(name);
                                string fullstringversion = version.ToFullString();
                                var pkgvers = dbContext.PackageVersions.Where
                                (v => v.PackageId == package.Id && v.FullString == fullstringversion);
                                if (pkgvers.Count() > 0)
                                {
                                    foreach (var v in pkgvers.ToArray())
                                        dbContext.PackageVersions.Remove(v);
                                }
                                foreach (var type in types)
                                {
                                    var pkgver = new PackageVersion
                                    {
                                        Package = package,
                                        Major = version.Major,
                                        Minor = version.Minor,
                                        Patch = version.Patch,
                                        IsPrerelease = version.IsPrerelease,
                                        FullString = version.ToFullString(),
                                        Type = type.Name
                                    };
                                    dbContext.PackageVersions.Add(pkgver);
                                }
                                await dbContext.SaveChangesAsync();

                                logger.LogInformation($"new package : {nuspec.Name}");
                            }
                        }
                        using (var shacrypto = System.Security.Cryptography.SHA512.Create())
                        {
                            using (var stream = System.IO.File.OpenRead(fullpath))
                            {
                                var hash = shacrypto.ComputeHash(stream);
                                var shafullname = fullpath + ".sha512";
                                var hashtext = Convert.ToBase64String(hash);
                                var hashtextbytes = Encoding.ASCII.GetBytes(hashtext);

                                using (var shafile = System.IO.File.OpenWrite(shafullname))
                                {
                                    shafile.Write(hashtextbytes, 0, hashtextbytes.Length);
                                }
                            }
                        }
                        nuspec.ExtractToFile(Path.Combine(pkgpath, pkgid + ".nuspec"));
                    }

                }
                return Ok(ViewData);
            }
            catch (Exception ex)
            {
                logger.LogError(ex.Message);
                logger.LogError("Stack Trace: " + ex.StackTrace);
                return new ObjectResult(new { ViewData, ex.Message })
                { StatusCode = 500 };
            }
        }
    }
}
