using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Security.Principal;
using Prism.Events;
using Valg.EVA.Skanning.Contracts.Security;

namespace Valg.EVA.Skanning.Identity
{
    public class IdentityProvider : IIdentityProvider
    {
        public const string EmergencyToken = " in emergency mode";

        private readonly IEventAggregator eventAggregator;
        private WindowsIdentity identity;

        private Area.AreaDescription currentSelectedArea;
        private Role currentSelectedRole;

        public IdentityProvider(IEventAggregator eventAggregator)
        {
            this.Roles = new List<Role>();
            this.eventAggregator = eventAggregator;
        }

        public bool IsInEmergencyMode
        {
            get { return HasClaim(ClaimDefinitions.IsEmergencyModeClaimType); }
        }

        public Area.AreaDescription CurrentSelectedArea
        {
            get { return currentSelectedArea; }
            private set
            {
                currentSelectedArea = value;
                BroadCastSecurityContextHasChanged();
            }
        }

        public Role CurrentSelectedRole
        {
            get { return currentSelectedRole; }
            private set
            {
                currentSelectedRole = value;
                BroadCastSecurityContextHasChanged();
            }
        }

        public List<Role> Roles { get; private set; }

        public byte[] Token { get; private set; }

        public byte[] TokenSignature { get; private set; }

        private WindowsIdentity Identity
        {
            get
            {
                if (identity == null)
                {
                    identity = WindowsIdentity.GetCurrent();
                }
                return identity;
            }
        }

        public void BroadCastSecurityContextHasChanged()
        {
            eventAggregator
                .GetEvent<SecurityContextHasChangedEvent>()
                .Publish(EventArgs.Empty);
        }

        public string GetCurrentUserName()
        {
            if (HasClaim(ClaimDefinitions.IsEmergencyModeClaimType))
            {
                return GetCurrentUserDisplayName() + EmergencyToken;
            }
            else
            {
                return GetCurrentUserDisplayName();
            }
        }

        public string GetCurrentUserDisplayName()
        {
            var operatorNameClaimValue = ClaimValue(ClaimDefinitions.OperatorNameClaimType);
            if (string.IsNullOrWhiteSpace(operatorNameClaimValue))
            {
                return string.Empty;
            }
            else
            {
                return operatorNameClaimValue;
            }
        }

        public string[] ClaimValues(string claimType)
        {
            var result = from claim in Identity.Claims
                         where claim.Type == claimType
                         select claim.Value;
            return result.ToArray();
        }

        public bool HasClaim(string claimType)
        {
            return Identity.Claims.Any(p => p.Type == claimType);
        }

        public void AddClaims(List<Claim> claims)
        {
            Identity.AddClaims(claims);
            OnClaimsAdded();
        }

        public bool HasAreaSpecificClaim(string type)
        {
            if (CurrentSelectedArea == null)
            {
                return false;
            }
            else
            {
                return HasClaimOnCurrentRole(type, CurrentSelectedArea.TokenAreaContextId);
            }
        }

        public string ClaimValue(string type)
        {
            var claim = Identity.Claims.SingleOrDefault(p => p.Type == type);
            if (claim != null)
            {
                return claim.Value;
            }
            else
            {
                return null;
            }
        }

        public void AddRoles(List<Role> roles)
        {            
            foreach (Role role in roles)
            {                
                if (!this.Roles.Any(p => p.IsEqual(role)))
                {
                    this.Roles.Add(role);
                }
            }            
        }

        public void RemoveClaimAndRoles()
        {
            var claims = Identity.Claims.ToArray();
            foreach (var claim in claims)
            {
                Identity.TryRemoveClaim(claim);
            }

            this.Roles.Clear();
        }

        public void SetCurrentRole(Role role)
        {
            this.CurrentSelectedRole = role;
            this.CurrentSelectedArea = role.Area;
        }

        public void SetToken(byte[] token, byte[] signature)
        {
            Token = token;
            TokenSignature = signature;
        }

        private bool HasClaimOnCurrentRole(string type, string value)
        {
            var claim = CurrentSelectedRole.ClaimsInRole.SingleOrDefault(p => p.Type == type && p.Value == value);
            if (claim != null)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        private void OnClaimsAdded()
        {
            var claimsAddedEvent = this.eventAggregator.GetEvent<ClaimsAddedEvent>();
            claimsAddedEvent.Publish(EventArgs.Empty);
        }
    }
}
