// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Collections.Generic;
using System.IO;
using Internal.Microsoft.DotNet.PlatformAbstractions;

namespace Internal.Microsoft.Extensions.DependencyModel.Resolution
{
    internal class PackageCompilationAssemblyResolver: ICompilationAssemblyResolver
    {
        private readonly IFileSystem _fileSystem;
        private readonly string[] _nugetPackageDirectories;

        public PackageCompilationAssemblyResolver()
            : this(EnvironmentWrapper.Default, FileSystemWrapper.Default)
        {
        }

        public PackageCompilationAssemblyResolver(string nugetPackageDirectory)
            : this(FileSystemWrapper.Default, new string[] { nugetPackageDirectory })
        {
        }

        internal PackageCompilationAssemblyResolver(IEnvironment environment,
            IFileSystem fileSystem)
            : this(fileSystem, GetDefaultProbeDirectories(environment))
        {
        }

        internal PackageCompilationAssemblyResolver(IFileSystem fileSystem, string[] nugetPackageDirectories)
        {
            _fileSystem = fileSystem;
            _nugetPackageDirectories = nugetPackageDirectories;
        }

        private static string[] GetDefaultProbeDirectories(IEnvironment environment) =>
            GetDefaultProbeDirectories(RuntimeEnvironment.OperatingSystemPlatform, environment);

        internal static string[] GetDefaultProbeDirectories(Platform osPlatform, IEnvironment environment)
        {
           var probeDirectories = AppContext.GetData("PROBING_DIRECTORIES");

           var listOfDirectories = probeDirectories as string;

           if (!string.IsNullOrEmpty(listOfDirectories))
           {
               return listOfDirectories.Split(new char [] { Path.PathSeparator }, StringSplitOptions.RemoveEmptyEntries );
           }

           var packageDirectory = environment.GetEnvironmentVariable("NUGET_PACKAGES");

           if (!string.IsNullOrEmpty(packageDirectory))
           {
               return new string[] { packageDirectory };
           }

           string basePath;
           if (osPlatform == Platform.Windows)
           {
               basePath = environment.GetEnvironmentVariable("USERPROFILE");
           }
           else
           {
               basePath = environment.GetEnvironmentVariable("HOME");
           }

           if (string.IsNullOrEmpty(basePath))
           {
               return new string[] { string.Empty };
           }

           return new string[] { Path.Combine(basePath, ".nuget", "packages") };

        }

        public bool TryResolveAssemblyPaths(CompilationLibrary library, List<string> assemblies)
        {
            if (_nugetPackageDirectories == null || _nugetPackageDirectories.Length == 0 ||
                !string.Equals(library.Type, "package", StringComparison.OrdinalIgnoreCase))
            {
                return false;
            }

            foreach (var directory in _nugetPackageDirectories)
            {
                string packagePath;

                if (ResolverUtils.TryResolvePackagePath(_fileSystem, library, directory, out packagePath))
                {
                    IEnumerable<string> fullPathsFromPackage;
                    if (TryResolveFromPackagePath(_fileSystem, library, packagePath, out fullPathsFromPackage))
                    {
                        assemblies.AddRange(fullPathsFromPackage);
                        return true;
                    }
                }
            }
            return false;
        }

        private static bool TryResolveFromPackagePath(IFileSystem fileSystem, CompilationLibrary library, string basePath, out IEnumerable<string> results)
        {
            var paths = new List<string>();

            foreach (var assembly in library.Assemblies)
            {
                string fullName;
                if (!ResolverUtils.TryResolveAssemblyFile(fileSystem, basePath, assembly, out fullName))
                {
                    // if one of the files can't be found, skip this package path completely.
                    // there are package paths that don't include all of the "ref" assemblies 
                    // (ex. ones created by 'dotnet store')
                    results = null;
                    return false;
                }

                paths.Add(fullName);
            }

            results = paths;
            return true;
        }
    }
}
