// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;

namespace Microsoft.Extensions.ApiDescription.Client
{
    /// <summary>
    /// Adds or corrects ClassName, FirstForGenerator, Namespace, and OutputPath metadata in OpenApiReference items.
    /// Also stores final metadata as SerializedMetadata.
    /// </summary>
    public class GetOpenApiReferenceMetadata : Task
    {
        private const string TypeScriptLanguageName = "TypeScript";

        /// <summary>
        /// Extension to use in default OutputPath metadata value. Ignored when generating TypeScript.
        /// </summary>
        [Required]
        public string Extension { get; set; }

        /// <summary>
        /// Default Namespace metadata value.
        /// </summary>
        [Required]
        public string Namespace { get; set; }

        /// <summary>
        /// Default directory for OutputPath values.
        /// </summary>
        public string OutputDirectory { get; set; }

        /// <summary>
        /// The OpenApiReference items to update.
        /// </summary>
        [Required]
        public ITaskItem[] Inputs { get; set; }

        /// <summary>
        /// The updated OpenApiReference items. Will include ClassName, Namespace and OutputPath metadata.
        /// </summary>
        [Output]
        public ITaskItem[] Outputs{ get; set; }

        /// <inheritdoc />
        public override bool Execute()
        {
            var outputs = new List<ITaskItem>(Inputs.Length);
            var codeGenerators = new HashSet<string>();
            var destinations = new HashSet<string>();

            foreach (var item in Inputs)
            {
                var codeGenerator = item.GetMetadata("CodeGenerator");
                if (string.IsNullOrEmpty(codeGenerator))
                {
                    // This case occurs when user overrides the required metadata with an empty string.
                    var type = string.IsNullOrEmpty(item.GetMetadata("SourceProject")) ?
                        "OpenApiReference" :
                        "OpenApiProjectReference";

                    Log.LogError(Resources.FormatInvalidEmptyMetadataValue("CodeGenerator", type, item.ItemSpec));
                    continue;
                }

                var newItem = new TaskItem(item);
                outputs.Add(newItem);

                if (codeGenerators.Add(codeGenerator))
                {
                    newItem.SetMetadata("FirstForGenerator", "true");
                }
                else
                {
                    newItem.SetMetadata("FirstForGenerator", "false");
                }

                var outputPath = item.GetMetadata("OutputPath");
                if (string.IsNullOrEmpty(outputPath))
                {
                    // No need to further sanitize this path because the file must exist.
                    var filename = item.GetMetadata("Filename");
                    var isTypeScript = codeGenerator.EndsWith(
                        TypeScriptLanguageName,
                        StringComparison.OrdinalIgnoreCase);

                    outputPath = $"{filename}Client{(isTypeScript ? ".ts" : Extension)}";
                }

                // Place output file in correct directory (relative to project directory).
                if (!Path.IsPathRooted(outputPath) && !string.IsNullOrEmpty(OutputDirectory))
                {
                    outputPath = Path.Combine(OutputDirectory, outputPath);
                }

                if (!destinations.Add(outputPath))
                {
                    // This case may occur when user is experimenting e.g. with multiple code generators or options.
                    // May also occur when user accidentally duplicates OutputPath metadata.
                    Log.LogError(Resources.FormatDuplicateFileOutputPaths(outputPath));
                    continue;
                }

                MetadataSerializer.SetMetadata(newItem, "OutputPath", outputPath);

                var className = item.GetMetadata("ClassName");
                if (string.IsNullOrEmpty(className))
                {
                    var outputFilename = Path.GetFileNameWithoutExtension(outputPath);

                    className = CSharpIdentifier.SanitizeIdentifier(outputFilename);
                    MetadataSerializer.SetMetadata(newItem, "ClassName", className);
                }

                var @namespace = item.GetMetadata("Namespace");
                if (string.IsNullOrEmpty(@namespace))
                {
                    MetadataSerializer.SetMetadata(newItem, "Namespace", Namespace);
                }

                // Add metadata which may be used as a property and passed to an inner build.
                newItem.RemoveMetadata("SerializedMetadata");
                newItem.SetMetadata("SerializedMetadata", MetadataSerializer.SerializeMetadata(newItem));
            }

            Outputs = outputs.ToArray();

            return !Log.HasLoggedErrors;
        }
    }
}
