using Coscine.Api.LegacySharePoint.Models;
using Coscine.Api.LegacySharePoint.Utils;
using Microsoft.AspNetCore.Mvc;
using Microsoft.SharePoint;
using System;
using System.Linq;

namespace Coscine.Api.LegacySharePoint.Controllers
{
    public class GroupController : Controller
    {
        public GroupController()
        {
        }

        [HttpPost("[controller]/onUserSet")]
        public IActionResult OnUserSet([FromBody] SharePointEventObject sharePointEventObject)
        {
            ProjectSharePointContext((projectWeb, spUser) =>
            {
                RemoveUserFromAllGroups(projectWeb, spUser);
                AddUserToGroup(sharePointEventObject.Role, projectWeb, spUser);
            }, sharePointEventObject);
            return Ok();
        }

        [HttpPost("[controller]/onUserDelete")]
        public IActionResult OnUserDelete([FromBody] SharePointEventObject sharePointEventObject)
        {
            ProjectSharePointContext((projectWeb, spUser) =>
            {
                RemoveUserFromAllGroups(projectWeb, spUser);
            }, sharePointEventObject);
            return Ok();
        }

        private void AddUserToGroup(string groupName, SPWeb projectWeb, SPUser spUser)
        {
            projectWeb.SiteGroups[groupName].AddUser(spUser);
        }

        private void RemoveUserFromAllGroups(SPWeb projectWeb, SPUser spUser)
        {
            //XXX: Why not directly iterate through spUser.Groups?
            foreach (SPGroup possibleGroup in projectWeb.SiteGroups)
            {
                if (IsUserInGroup(spUser, possibleGroup))
                {
                    possibleGroup.RemoveUser(spUser);
                }
            }
        }

        private bool IsUserInGroup(SPUser user, SPGroup group)
        {
            return user.Groups.Cast<SPGroup>()
              .Any(g => g.ID == group.ID);
        }

        private SPUser TranslateUser(SPWeb web, SharePointEventObject sharePointEventObject)
        {
            // User iteration since Linq commands like .First() don't work with SPUserCollection
            foreach (SPUser siteUser in web.SiteUsers)
            {
                if (siteUser.LoginName.StartsWith("i:0") && siteUser.LoginName.Contains("|") && siteUser.LoginName.Substring(0, siteUser.LoginName.IndexOf("|")).EndsWith("t"))
                {
                    var prefix = siteUser.LoginName.Substring(0, siteUser.LoginName.IndexOf("|"));
                    return EnsureUser(sharePointEventObject.UserId, web, prefix);
                }
            }

            try
            {
                if (!string.IsNullOrWhiteSpace(sharePointEventObject.StsPrefix))
                {
                    return EnsureUser(sharePointEventObject.UserId, web, sharePointEventObject.StsPrefix);
                }
                throw new ArgumentNullException();
            }
            catch (Exception)
            {
                return EnsureUser(sharePointEventObject.UserId, web, "i:0^.t");
            }
        }

        private SPUser EnsureUser(string userId, SPWeb web, string prefix)
        {
            var loginName = prefix + "|coscine sts|" + userId.ToString();
            web.EnsureUser(loginName);
            return web.SiteUsers[loginName];
        }

        private void ProjectSharePointContext(Action<SPWeb, SPUser> action, SharePointEventObject sharePointEventObject)
        {
            SPUserToken systemAccount = SPUserToken.SystemAccount;
            using (SPSite site = new SPSite(sharePointEventObject.SharePointSite, systemAccount))
            {
                var projectUrl = SharePointUtil.EnsureProjectUrl(site, sharePointEventObject);
                using (SPSite projectSite = new SPSite(projectUrl, systemAccount))
                {
                    var spUser = TranslateUser(projectSite.RootWeb, sharePointEventObject);
                    action(projectSite.RootWeb, spUser);
                }
            }
        }
    }
}