using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using Microsoft.DotNet.SwaggerGenerator.Modeler;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Readers;
using Task = Microsoft.Build.Utilities.Task;

namespace Microsoft.DotNet.SwaggerGenerator.MSBuild
{
    public class GenerateSwaggerCode : Microsoft.Build.Utilities.Task
    {
        [Required]
        public string SwaggerDocumentUri { get; set; }

        [Required]
        public string RootNamespace { get; set; }

        [Required]
        public string ClientName { get; set; }

        [Required]
        public string OutputDirectory { get; set; }

        [Output]
        public ITaskItem[] GeneratedFiles { get; set; }

        public override bool Execute()
        {
            try
            {
                ExecuteAsync().GetAwaiter().GetResult();
            }
            catch (Exception ex)
            {
                Log.LogErrorFromException(ex, true, true, null);
            }

            return !Log.HasLoggedErrors;
        }

        private async System.Threading.Tasks.Task ExecuteAsync()
        {
            var options = new GeneratorOptions
            {
                LanguageName = "csharp",
                Namespace = RootNamespace,
                ClientName = ClientName
            };

            Log.LogMessage(MessageImportance.Low, $"Reading swagger document {SwaggerDocumentUri}");
            var (diagnostic, document) = await GetSwaggerDocument(SwaggerDocumentUri);
            if (diagnostic.Errors.Any())
            {
                foreach (var error in diagnostic.Errors)
                {
                    Log.LogWarning(null, null, null, error.Pointer, 0, 0, 0, 0, error.Message);
                }
            }


            Log.LogMessage(MessageImportance.Low, $"Generating client code model");
            var generator = new ServiceClientModelFactory(options);
            ServiceClientModel model = generator.Create(document);

            Log.LogMessage(MessageImportance.Low, $"Generating code files for language '{options.LanguageName}'");
            var codeFactory = new ServiceClientCodeFactory();
            List<CodeFile> code = codeFactory.GenerateCode(model, options);

            Log.LogMessage(MessageImportance.High, $"Generating {SwaggerDocumentUri} -> {OutputDirectory}");
            var outputDirectory = new DirectoryInfo(OutputDirectory);
            outputDirectory.Create();

            var generatedFiles = new List<ITaskItem>();
            foreach ((string path, string contents) in code)
            {
                string fullPath = Path.Combine(outputDirectory.FullName, path);
                var file = new FileInfo(fullPath);
                file.Directory.Create();
                Log.LogMessage(MessageImportance.Normal, $"Writing file '{file.FullName}'");
                File.WriteAllText(file.FullName, contents);
                generatedFiles.Add(new TaskItem(file.FullName));
            }

            GeneratedFiles = generatedFiles.ToArray();
        }

        private static async Task<(OpenApiDiagnostic, OpenApiDocument)> GetSwaggerDocument(string input)
        {
            using (var client = new HttpClient(new HttpClientHandler { CheckCertificateRevocationList = true }))
            {
                using (Stream docStream = await client.GetStreamAsync(input))
                {
                    var doc = ServiceClientModelFactory.ReadDocument(docStream, out OpenApiDiagnostic diagnostic);

                    return (diagnostic, doc);
                }
            }
        }
    }
}
