using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using Microsoft.Identity.Client;
using Microsoft.InformationProtection;
using Microsoft.InformationProtection.File;
using Microsoft.InformationProtection.Policy;
using LogLevel = Microsoft.InformationProtection.LogLevel;

namespace AipGateway.AIP
{
    public class FileAipManager
    {
        private static string _mipData;

        private string _mipPath;
        private readonly ApplicationInfo _appInfo;
        private readonly AuthDelegateImplementation _authDelegate;
        private IFileProfile _fileProfile = null;
        private IFileEngine _fileEngine = null;
        private IPolicyProfile _policyProfile = null;
        private IPolicyEngine _policyEngine = null;
        private MipContext _mipContext = null;

        public FileAipManager(
            string clientId, 
            string applicationName, 
            string applicationVersion, 
            string tenantId, 
            string clientSecret, 
            string mipData, 
            string eamil,
            ClaimsPrincipal claimsPrincipal)
        {
            _appInfo = new ApplicationInfo()
            {
                ApplicationId = clientId,
                ApplicationName = applicationName,
                ApplicationVersion = applicationVersion
            };
            _mipData = mipData;
            _mipPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, _mipData);

            _authDelegate = new AuthDelegateImplementation(clientId, clientSecret, claimsPrincipal);

            // var path = Path.Combine(
            //     Directory.GetParent(Path.GetDirectoryName(new Uri(System.Reflection.Assembly.GetExecutingAssembly().CodeBase).LocalPath)).FullName,
            //     Environment.Is64BitProcess ? "x64" : "x86");
            
            var path = Path.Combine(
                Directory.GetParent(Path.GetDirectoryName(new Uri(Environment.CurrentDirectory).LocalPath)).FullName,
                Environment.Is64BitProcess ? "x64" : "x86");

            MIP.Initialize(MipComponent.File);//, path);

            MipConfiguration mipConfiguration = new MipConfiguration(_appInfo, _mipData, LogLevel.Trace, false);
            _mipContext = MIP.CreateMipContext(mipConfiguration);

            Identity identity = new Identity(eamil);

            // _policyProfile = CreatePolicyProfile(_appInfo, ref _authDelegate);
            // _policyEngine = CreatePolicyEngine(identity);

            _fileProfile = CreateFileProfile();

            // _fileEngine = CreateFileEngine(ClaimsPrincipal.Current.FindFirst(ClaimTypes.Upn).Value, "", "en-US");
            _fileEngine = CreateFileEngine(identity, "", "en-US");
        }

        public void Dispose()
        {
            _policyProfile.UnloadEngineAsync(_policyEngine.Settings.Id).Wait();
            _policyEngine.Dispose();
            _policyProfile.Dispose();
            _fileEngine.Dispose();
            _fileEngine.Dispose();
            _mipContext.ShutDown();
            _mipContext.Dispose();
        }

        private IPolicyProfile CreatePolicyProfile(ApplicationInfo appInfo, ref AuthDelegateImplementation authDelegate)
        {
            var profileSettings = new PolicyProfileSettings(_mipContext, CacheStorageType.OnDiskEncrypted);
            var profile = Task.Run(async () => await MIP.LoadPolicyProfileAsync(profileSettings)).Result;
            return profile;
        }

        private IPolicyEngine CreatePolicyEngine(Identity identity)
        {
            var engineSettings = new PolicyEngineSettings(identity.Email, _authDelegate, "", "en-US")
            {
                Identity = identity
            };

            var engine = Task.Run(async () => await _policyProfile.AddEngineAsync(engineSettings)).Result;
            return engine;
        }

        private IFileProfile CreateFileProfile()
        {
            try
            {
                var profileSettings = new FileProfileSettings(_mipContext, CacheStorageType.OnDisk, new ConsentDelegateImplementation());
                // var profileSettings = new FileProfileSettings(_mipContext, CacheStorageType.OnDiskEncrypted, new ConsentDelegateImplementation());

                var fileProfile = Task.Run(async () => await MIP.LoadFileProfileAsync(profileSettings)).Result;
                return fileProfile;
            }
            catch (Exception ex)
            {
                //throw ex;
            }
            return null;
        }
        // public Identity GetUserIdentity()
        // {
        //     //AuthenticationResult result = AcquireTokenAsync("https://login.microsoftonline.com/common", "https://graph.microsoft.com", null).Result;
        //     return new Identity(result.Account.Username);
        // }

        private IFileEngine CreateFileEngine(Identity identity, string clientData, string locale)
        {
            try
            {
                var configuredFunctions = new Dictionary<FunctionalityFilterType, bool>();
                configuredFunctions.Add(FunctionalityFilterType.DoubleKeyProtection, true);

                var engineSettings = new FileEngineSettings(identity.Email, _authDelegate, clientData, locale)
                {
                    Identity = identity,
                    ConfiguredFunctionality = configuredFunctions,
                    ProtectionOnlyEngine = true
                };

                var fileEngine = Task.Run(async () => await _fileProfile.AddEngineAsync(engineSettings)).Result;
                return fileEngine;
            }
            catch (Exception ex)
            {
                //throw ex;
            }
            return null;
        }

        private IFileHandler CreateFileHandler(Stream stream, string fileName)
        {
            IFileHandler handler;

            try
            {
                if (stream != null)
                {
                    handler = Task.Run(async () => await _fileEngine.CreateFileHandlerAsync(stream, fileName, true)).Result;
                }
                else
                {
                    handler = Task.Run(async () => await _fileEngine.CreateFileHandlerAsync(fileName, fileName, true)).Result;
                }

                return handler;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public bool ApplyLabel(Stream stream, Stream outputStream, string fileName, string labelId, string justificationMessage)
        {
            IFileHandler handler;

            try
            {
                // Try to create an IFileHandler using private CreateFileHandler().
                if (stream != null)
                {
                    handler = CreateFileHandler(stream, fileName);
                }

                // Try to create an IFileHandler using private CreateFileHandler().
                else
                {
                    handler = CreateFileHandler(null, fileName);
                }

                // Applying a label requires LabelingOptions. Hard coded values here, but could be provided by user. 
                LabelingOptions labelingOptions = new LabelingOptions()
                {
                    JustificationMessage = justificationMessage,
                    AssignmentMethod = AssignmentMethod.Standard,
                    ExtendedProperties = new List<KeyValuePair<string, string>>()
                };

                // Set the label on the input stream or file.
                handler.SetLabel(_fileEngine.GetLabelById(labelId), labelingOptions, new ProtectionSettings());

                // Call CommitAsync to write result to output stream. 
                // Returns a bool to indicate true or false.
                var result = Task.Run(async () => await handler.CommitAsync(outputStream)).Result;

                if (result)
                {
                    // Submit an audit event if the change was successful.
                    handler.NotifyCommitSuccessful(fileName);
                }
                
                return result;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public List<AipLabel> ListAllLabels()
        {
            try
            {
                //var labels = _policyEngine.ListSensitivityLabels();
                var labels = _fileEngine.SensitivityLabels;
                var returnLabels = new List<AipLabel>();

                foreach (var label in labels)
                {
                    var _label = new AipLabel()
                    {
                        Name = label.Name,
                        Id = label.Id,
                        Description = label.Description,
                        Sensitivity = label.Sensitivity
                    };

                    _label.Children = new List<AipLabel>();

                    // If the label has an children, iterate through each. 
                    if (label.Children.Count > 0)
                    {
                        foreach (var child in label.Children)
                        {
                            var _child = new AipLabel()
                            {
                                Name = child.Name,
                                Id = child.Id,
                                Description = child.Description,
                                Sensitivity = child.Sensitivity
                            };
                            _label.Children.Add(_child);
                        }
                    }
                    returnLabels.Add(_label);
                }

                return returnLabels;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
    }
}
