using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Text;
using System.Threading.Tasks;
using Microsoft.InformationProtection;
using Microsoft.InformationProtection.File;
using Microsoft.InformationProtection.Policy;
using Microsoft.InformationProtection.Policy.Actions;

namespace AipGateway.AIP
{
    public class PolicyManager : AbstractManager
    {
        private IPolicyProfile _profile = null;
        private IPolicyEngine _engine = null;
        public PolicyManager()
        {
        }
        ~PolicyManager() => this.Dispose(false);

        public override void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize((object)this);

        }
        protected virtual void Dispose(bool disposing)
        {
            lock (this)
            {
                if (_profile != null && _engine != null)
                {
                    //_profile.UnloadEngineAsync(_engine.Settings.Id).Wait();
                    _profile.Dispose();
                    _engine.Dispose();

                }
                _engine = null;
                _profile = null;
            }
        }

        public override bool CreateProfile(ref MipContext mipContext)
        {
            try
            {
                var profileSettings = new PolicyProfileSettings(mipContext,
                    CacheStorageType.OnDiskEncrypted);

                // IFileProfile은 특정 애플리케이션에 대한 모든 SDK 작업의 루트입니다.
                _profile = Task.Run(async () => await MIP.LoadPolicyProfileAsync(profileSettings)).Result;
            }
            catch (Exception e)
            {
                SetError(1, "PolicyManager::CreateProfile Failed.", e.Message);
                return false;
            }
            return _profile != null;
        }

        public override bool CreateEngine(ref Identity identity, ref AuthDelegateImplementation authDelegate)
        {
            try
            {
                authDelegate.ResetError();
                var engineSettings = new PolicyEngineSettings(identity.Email, authDelegate, string.Empty, "en-US")
                {
                    // Provide the identity for service discovery.
                    Identity = identity
                };

                _engine = Task.Run(async () => await _profile.AddEngineAsync(engineSettings)).Result;

                //Console.WriteLine("Policy Engine Sensitivity Labels ======================================================");
                //var labels = _engine.ListSensitivityLabels();
                //for (int ii = 0; ii < labels.Count; ii++)
                //{
                //    Console.WriteLine("{0}: {1}, {2}", ii.ToString(), labels[ii].Id + " : " + labels[ii].Name, labels[ii].IsActive);
                //    Label label = _engine.GetLabelById(labels[ii].Id);
                //    if (label.Children.Count > 0)
                //    {
                //        for (int jj = 0; jj < label.Children.Count; jj++)
                //        {
                //            Console.WriteLine("\t{0}: {1}, {2}", jj.ToString(), label.Children[jj].Id + " : " + label.Children[jj].Name, label.Children[jj].IsActive);
                //        }
                //    }
                //}
                //Console.WriteLine("=======================================================================");
            }
            catch (Exception e)
            {
                if (authDelegate.LastErrNo != 0)
                {
                    SetError(authDelegate.LastErrNo, "PolicyManager::CreateEngine Failed.", authDelegate.LastErrMsg);
                }
                else
                {
                    SetError(2, "PolicyManager::CreateEngine Failed.", e.Message);
                }

                return false;
            }
            return _engine != null;
        }

        public IEnumerable<Label> ListSensitivityLabels()
        {
            // 사용자 주체의 경우 이는 사용자별로 다릅니다.
            // 서비스 주체의 경우 이는 서비스별로 또는 전역적일 수 있습니다.
            return _engine.ListSensitivityLabels();
        }

        private IPolicyHandler CreatePolicyHandler(ExecutionStateOptions options)
        {
            try
            {
                var handler = _engine.CreatePolicyHandler(options.generateAuditEvent);
                return handler;
            }
            catch (Exception ex)
            {
                SetError(91, "PolicyManager::CreatePolicyHandler Failed.", ex.Message);
            }
            return null;
        }
        public ReadOnlyCollection<Microsoft.InformationProtection.Policy.Actions.Action> ComputeActions(ExecutionStateOptions options)
        {
            var handler = CreatePolicyHandler(options);
            if (handler == null)
            {
                return null;
            }

            try
            {
                ExecutionStateImplementation state = new ExecutionStateImplementation(options);
                var actions = handler.ComputeActions(state);

                if (actions.Count == 0 && options.generateAuditEvent)
                {
                    handler.NotifyCommittedActions(state);
                }

                return actions;
            }
            catch (Exception ex)
            {
                SetError(92, "PolicyManager::ComputeActions Failed.", ex.Message);
            }
            return null;
        }

        public bool ComputeActionLoop(ExecutionStateOptions options)
        {
            ExecutionStateImplementation state = new ExecutionStateImplementation(options);

            var handler = CreatePolicyHandler(options);
            var actions = handler.ComputeActions(state);

            while (actions.Count > 0)
            {
                //Console.WriteLine("Action Count: {0}", actions.Count);
                foreach (var action in actions)
                {
                    switch (action.ActionType)
                    {
                        case ActionType.Metadata:
                            var derivedMetadataAction = (MetadataAction)action;
                            if (derivedMetadataAction.MetadataToRemove.Count > 0)
                            {
                                Console.WriteLine("*** Action: Remove Metadata.");
                                //Rather than iterate, in the same we just remove it all.
                                options.metadata.Clear();
                            }

                            if (derivedMetadataAction.MetadataToAdd.Count > 0)
                            {
                                Console.WriteLine("*** Action: Apply Metadata.");
                                //Iterate through metadata and add to options
                                foreach (var item in derivedMetadataAction.MetadataToAdd)
                                {
                                    options.metadata.Add(item.Key, item.Value);
                                    Console.WriteLine("*** Added: {0} - {1}", item.Key, item.Value);
                                }
                            }
                            break;

                        case ActionType.ProtectByTemplate:
                            var derivedProtectbyTemplateAction = (ProtectByTemplateAction)action;
                            options.templateId = derivedProtectbyTemplateAction.TemplateId;
                            Console.WriteLine("*** Action: Protect by Template: {0}", derivedProtectbyTemplateAction.TemplateId);
                            break;

                        case ActionType.RemoveProtection:
                            var derivedRemoveProtectionAction = (RemoveProtectionAction)action;
                            options.templateId = string.Empty;
                            Console.Write("*** Action: Remove Protection.");
                            break;

                        case ActionType.Justify:
                            var derivedJustificationAction = (JustifyAction)action;
                            Console.WriteLine("*** Justification Required!");
                            Console.Write("Provide Justification: ");
                            string justificationMessage = Console.ReadLine();
                            options.isDowngradeJustified = true;
                            options.downgradeJustification = justificationMessage;
                            break;

                        case ActionType.AddContentFooter:
                        // Any other actions must be explicitly defined after this.
                            break;

                        default:
                            break;
                    }
                }

                state = new ExecutionStateImplementation(options);
                actions = handler.ComputeActions(state);
                Console.WriteLine("*** Remaining Action Count: {0}", actions.Count);
            }
            if (options.generateAuditEvent && actions.Count == 0)
            {
                handler.NotifyCommittedActions(state);
            }
            return true;
        }

        public Label GetLabelById(string labelId)
        {
            Label label;
            try
            {
                label = _engine.GetLabelById(labelId);
            }
            catch (Exception ex)
            {
                SetError(99, "FileManager::GetLabel Failed. Request Label Id: " + labelId, ex.Message);
                return null;
            }

            return label;
        }
        public bool SetComputeAction(string fileName, string actualFileName, string email, string labelId, string comments)
        {
            ExecutionStateOptions options = new ExecutionStateOptions();
            Label label = GetLabelById(labelId);
            options.newLabel = label;   // 레벨은 선택할 수 있다.
            options.actionSource = ActionSource.Manual;
            options.assignmentMethod = AssignmentMethod.Standard;
            options.contentFormat = Microsoft.InformationProtection.Policy.ContentFormat.File;
            options.contentIdentifier = fileName;
            options.dataState = DataState.Use;
            options.isDowngradeJustified = false;
            options.generateAuditEvent = true;
            options.metadata = new Dictionary<string, string>();

            if (options.newLabel == null)
            {
                return false;
            }

            var initialActions = ComputeActions(options);
            // 추가 작업이 필요한 경우 ExecutionStateImplementation.cs에서 GetSupportedActions를 수정하세요.
            // 그런 다음 관심 있는 작업(예: 머리글, 바닥글 또는 워터마크 적용)에 대한 작업을 반복합니다.
            // 파생된 작업에서 콘텐츠 표시 정보를 얻을 수 있습니다.
            foreach (var item in initialActions)
            {
                switch (item.ActionType)
                {
                    case ActionType.Metadata:
                        options.metadata.Clear();
                        foreach (var data in ((MetadataAction)item).MetadataToAdd)
                        {
                            options.metadata.Add(data.Key, data.Value);
                        }
                        break;

                    case ActionType.ProtectByTemplate:
                        options.templateId = ((ProtectByTemplateAction)item).TemplateId;
                        break;
                    default:
                        break;
                }
            }
            options.newLabel = GetLabelById(labelId);

            var result = ComputeActionLoop(options);
            return result;
        }

    }

}
