// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using AWS.Deploy.CLI.Common.UnitTests.IO;
using AWS.Deploy.Common;
using AWS.Deploy.Common.IO;
using AWS.Deploy.Common.Recipes;
using AWS.Deploy.Common.Recipes.Validation;
using AWS.Deploy.Orchestration;
using Moq;
using Xunit;

namespace AWS.Deploy.CLI.Common.UnitTests.Recipes.Validation
{
    /// <summary>
    /// Tests for the recipe-level validation between the Dockerfile path and
    /// docker execution recipe options, <see cref="DockerfilePathValidator"/>
    /// </summary>
    public class DockerfilePathValidationTests
    {
        private readonly IServiceProvider _serviceProvider;
        private readonly RecipeDefinition _recipeDefinition;
        private readonly IDirectoryManager _directoryManager;
        private readonly IFileManager _fileManager;

        public DockerfilePathValidationTests()
        {
            _serviceProvider = new Mock<IServiceProvider>().Object;
            _directoryManager = new TestDirectoryManager();
            _fileManager = new TestFileManager();

            _recipeDefinition = new Mock<RecipeDefinition>(
                It.IsAny<string>(),
                It.IsAny<string>(),
                It.IsAny<string>(),
                It.IsAny<DeploymentTypes>(),
                It.IsAny<DeploymentBundleTypes>(),
                It.IsAny<string>(),
                It.IsAny<string>(),
                It.IsAny<string>(),
                It.IsAny<string>(),
                It.IsAny<string>()).Object;
        }

        public static IEnumerable<object[]> DockerfilePathTestData => new List<object[]>()
        {
            // Dockerfile path | Docker execution directory | expected to be valid?

            // We generate a Dockerfile later if one isn't specified, so not invalid at this point
            new object[] { "", Path.Combine("C:", "project"), true },

            // We compute the execution directory later if one isn't specified, so not invalid at this point
            new object[] { Path.Combine("C:", "project", "Dockerfile"), "", true },

            // Dockerfile is in the execution directory, with absolute paths
            new object[] { Path.Combine("C:", "project", "Dockerfile"), Path.Combine("C:", "project"), true },

            // Dockerfile is in the execution directory, with relative paths
            new object[] { Path.Combine(".", "Dockerfile"), Path.Combine("."), true },

            // Dockerfile is further down in execution directory, with absolute paths
            new object[] { Path.Combine("C:", "project", "child", "Dockerfile"), Path.Combine("C:", "project"), true },

            // Dockerfile is further down in execution directory, with relative paths
            new object[] { Path.Combine(".", "child", "Dockerfile"), Path.Combine("."), true },

            // Dockerfile is outside of the execution directory, which is invalid
            new object[] { Path.Combine("C:", "project", "Dockerfile"), Path.Combine("C:", "foo"), false }
        };

        /// <summary>
        /// Tests for <see cref="DockerfilePathValidator"/>, which validates the relationship
        /// between a Dockerfile path and the Docker execution directory
        /// </summary>
        [Theory]
        [MemberData(nameof(DockerfilePathTestData))]
        public async Task DockerfilePathValidationHelperAsync(string dockerfilePath, string dockerExecutionDirectory, bool expectedToBeValid)
        {
            var projectPath = Path.Combine("C:", "project", "test.csproj");
            var options = new List<OptionSettingItem>()
            {
                new OptionSettingItem("DockerfilePath", "", "", "")
            };
            var projectDefintion = new ProjectDefinition(null, projectPath, "", "");
            var recommendation = new Recommendation(_recipeDefinition, projectDefintion, 100, new Dictionary<string, object>());
            var validator = new DockerfilePathValidator(_directoryManager, _fileManager);

            recommendation.DeploymentBundle.DockerExecutionDirectory = dockerExecutionDirectory;
            recommendation.DeploymentBundle.DockerfilePath = dockerfilePath;

            // "Write" to the TestFileManager so that "Exists" returns true
            if (Path.IsPathRooted(dockerfilePath))
                await _fileManager.WriteAllTextAsync(dockerfilePath, "");
            else
                await _fileManager.WriteAllTextAsync(Path.Combine(recommendation.GetProjectDirectory(), dockerfilePath), "");

            var validationResult = await validator.Validate(recommendation, null);

            Assert.Equal(expectedToBeValid, validationResult.IsValid);
        }
    }
}