using Coscine.Action;
using Coscine.Action.EventArgs;
using Coscine.Action.Utils;
using Coscine.Api.Project.ParameterObjects;
using Coscine.Api.Project.ReturnObjects;
using Coscine.ApiCommons;
using Coscine.ApiCommons.Factories;
using Coscine.Configuration;
using Coscine.Database.DataModel;
using Coscine.Database.Models;
using Coscine.Database.ReturnObjects;
using Coscine.Database.Util;
using Coscine.Logging;
using Coscine.Metadata;
using Coscine.ResourceLoader;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Coscine.Api.Project.Controllers
{

    /// <summary>
    ///  /// This controller represents the actions which can be taken with a project object.
    /// </summary>
    [Authorize]
    public class ProjectController : Controller
    {
        private readonly Authenticator _authenticator;
        private readonly ProjectModel _projectModel;
        private readonly IConfiguration _configuration;
        private readonly Emitter _emitter;
        private readonly ActivatedFeaturesModel _activatedFeaturesModel;
        private readonly ProjectRoleModel _projectRoleModel;
        private readonly ProjectQuotaModel _projectQuotaModel;
        private readonly ResourceTypeModel _resourceTypeModel;
        private readonly ResourceModel _resourceModel;
        private readonly CoscineLogger _coscineLogger;
        private readonly VisibilityModel _visibilityModel;
        private readonly InvitationModel _invitationModel;
        private readonly RoleModel _roleModel;
        private readonly UserModel _userModel;
        private readonly int _maxAvailable = 100;
        private readonly string _userUrlPrefix = "https://purl.org/coscine/users";
        private readonly Uri _orgPrefixUrl = new Uri("http://www.w3.org/ns/org#");
        private readonly RdfStoreConnector _rdfStoreConnector;

        /// <summary>
        /// ProjectController constructor
        /// </summary>
        /// <param name="logger">Logger</param>
        public ProjectController(ILogger<ProjectController> logger)
        {
            _authenticator = new Authenticator(this, Program.Configuration);
            _configuration = Program.Configuration;
            _projectModel = new ProjectModel();
            _emitter = new Emitter(_configuration);
            _activatedFeaturesModel = new ActivatedFeaturesModel();
            _projectRoleModel = new ProjectRoleModel();
            _resourceTypeModel = new ResourceTypeModel();
            _resourceModel = new ResourceModel();
            _projectQuotaModel = new ProjectQuotaModel();
            _coscineLogger = new CoscineLogger(logger);
            _visibilityModel = new VisibilityModel();
            _rdfStoreConnector = new RdfStoreConnector(Program.Configuration.GetString("coscine/local/virtuoso/additional/url"));
            _invitationModel = new InvitationModel();
            _roleModel = new RoleModel();
            _userModel = new UserModel();
        }

        /// <summary>
        /// Returns all available projects (including sub projects)
        /// </summary>
        /// <returns>Ok</returns>
        [Route("[controller]")]
        public ActionResult<IEnumerable<ProjectObject>> Index()
        {
            var user = _authenticator.GetUser();
            var result = _projectModel.GetWithAccess(user, UserRoles.Member, UserRoles.Owner).ToList()
                .Select((project) => _projectModel.CreateReturnObjectFromDatabaseObject(project))
                .OrderBy(element => element.DisplayName);

            return Ok(result);
        }

        /// <summary>
        /// Retrieves all top level projects
        /// </summary>
        /// <returns>Ok</returns>
        [Route("[controller]/-/topLevel")]
        public ActionResult<IEnumerable<ProjectObject>> GetTopLevelProjects()
        {
            var user = _authenticator.GetUser();
            var projects = _projectModel.GetTopLevelWithAccess(user, UserRoles.Member, UserRoles.Owner).ToList()
                .Select((project) => _projectModel.CreateReturnObjectFromDatabaseObject(project))
                .OrderBy(element => element.DisplayName);

            if (Request.Query != null && Request.Query["noanalyticslog"] != "true")
            {
                LogAnalyticsViewHome(projects.Select(x => x.Id.ToString()).ToList());
            }

            return Ok(projects);
        }

        /// <summary>
        /// This returns the the project if the user has access to it
        /// </summary>
        /// <param name="id">Id of the resource</param>
        /// <returns>Ok or Statuscode 401</returns>
        [HttpGet("[controller]/{id}")]
        public ActionResult<ProjectObject> Get(string id)
        {
            var user = _authenticator.GetUser();
            var project = _projectModel.GetById(Guid.Parse(id));
            if (_projectModel.HasAccess(user, project, UserRoles.Member, UserRoles.Owner))
            {
                SubProjectModel subProjectModel = new SubProjectModel();
                var subProjectRel = subProjectModel.GetAllWhere((subProject) => subProject.SubProjectId == project.Id && project.Deleted == false);

                var parentProjectRelation = subProjectRel.FirstOrDefault();
                if (parentProjectRelation != null && _projectModel.HasAccess(user, parentProjectRelation.ProjectId, UserRoles.Member, UserRoles.Owner))
                {
                    return Ok(_projectModel.CreateReturnObjectFromDatabaseObject(project, parentProjectRelation.ProjectId));
                }
                return Ok(_projectModel.CreateReturnObjectFromDatabaseObject(project));
            }
            else
            {
                return Unauthorized($"User is not allowed to see given the project {id}");
            }
        }

        /// <summary>
        /// Gets the resources
        /// </summary>
        /// <param name="id">Id of the resource</param>
        /// <returns>Json object or Statuscode 401</returns>
        [HttpGet("[controller]/{id}/resources")]
        public ActionResult<IEnumerable<ResourceObject>> GetResources(string id)
        {
            var project = _projectModel.GetById(Guid.Parse(id));
            var user = _authenticator.GetUser();

            var resourceModel = new ResourceModel();
            var resourceTypeModel = new ResourceTypeModel();
            if (_projectModel.HasAccess(user, project, UserRoles.Member, UserRoles.Owner))
            {
                var resources = resourceModel.GetAllWhere((resource) =>
                        (from projectResource in resource.ProjectResources
                         where projectResource.ProjectId == project.Id
                         select projectResource).Any())
                        .Select((resource) =>
                        {
                            return resourceModel.CreateReturnObjectFromDatabaseObject(resource);
                        }).OrderBy(element => element.DisplayName);
                if (Request.Query != null && Request.Query["noanalyticslog"] != "true")
                {
                    var projectObject = _projectModel.CreateReturnObjectFromDatabaseObject(_projectModel.GetById(project.Id));
                    LogAnalyticsViewProject(project, resources.ToList(), projectObject.Disciplines, projectObject.Organizations, user);
                }
                return Json(resources);
            }
            else
            {
                return Unauthorized($"User is not allowed to see given the project {id}");
            }
        }

        /// <summary>
        /// Retrieves the quota for the selected project.
        /// </summary>
        /// <param name="projectId">Id of the project.</param>
        /// <returns>List of project quotas</returns>
        [HttpGet("[controller]/{projectId}/quota/-/all")]
        public ActionResult<IEnumerable<ProjectQuota>> Quotas(string projectId)
        {
            var user = _authenticator.GetUser();

            if (!Guid.TryParse(projectId, out Guid projectGuid))
            {
                return BadRequest($"{projectId} is not a guid.");
            }

            var project = _projectModel.GetById(projectGuid);

            if (project == null)
            {
                return NotFound($"Could not find project with id: {projectId}");
            }

            if (!_projectModel.HasAccess(user, project, UserRoles.Owner))
            {
                return Unauthorized("The user is not authorized to perform a get on the selected project!");
            }

            var resourceTypes = _resourceTypeModel.GetAllWhere(x => x.Enabled.HasValue && x.Enabled.Value);

            return Json(resourceTypes.Select(x => CreateProjectQuotaReturnObject(x, projectGuid)));
        }

        private ProjectQuotaReturnObject CreateProjectQuotaReturnObject(ResourceType x, Guid projectGuid)
        {
            var projectQuota = _projectQuotaModel.GetWhere((y) =>
                    y.ProjectId == projectGuid &&
                    y.ResourceTypeId == x.Id);
            return new ProjectQuotaReturnObject
            {
                Id = x.Id,
                Name = x.DisplayName,
                Used = CalculateUsed(x, projectGuid),
                Allocated = projectQuota == null ? 0 : projectQuota.Quota
            };
        }

        private int CalculateUsed(ResourceType resourceType, Guid projectId)
        {
            var resourceTypeDefinition = ResourceTypeFactory.CreateResourceTypeObject(resourceType.DisplayName, _configuration);

            var resources = _resourceModel.GetAllWhere((resource) =>
                        (from projectResource in resource.ProjectResources
                         where projectResource.ProjectId == projectId
                         select projectResource).Any() &&
                         resource.TypeId == resourceType.Id);

            var used = resources.Sum(y => resourceTypeDefinition.GetResourceQuotaAvailable(y.Id.ToString(), _resourceModel.GetResourceTypeOptions(y.Id)).Result);
            return (int)used;
        }

        /// <summary>
        /// Retrieves the quota for the selected project and resource Type.
        /// </summary>
        /// <param name="projectId">Id of the project</param>
        /// <param name="resourceTypeId">Id of the resource type</param>
        /// <returns>The project quota for the resource type.</returns>
        [HttpGet("[controller]/{projectId}/quota/{resourceTypeId}")]
        public ActionResult<ProjectQuotaReturnObject> Quota(string projectId, string resourceTypeId)
        {
            var user = _authenticator.GetUser();

            if (!Guid.TryParse(projectId, out Guid projectGuid))
            {
                return BadRequest($"{projectId} is not a guid.");
            }

            var project = _projectModel.GetById(projectGuid);

            if (project == null)
            {
                return NotFound($"Could not find project with id: {projectId}");
            }

            if (!_projectModel.HasAccess(user, project, UserRoles.Owner))
            {
                return Unauthorized("The user is not authorized to perform a get on the selected project!");
            }

            if (!Guid.TryParse(resourceTypeId, out Guid resourceTypeGuid))
            {
                return BadRequest($"{resourceTypeId} is not a guid.");
            }

            var resourceType = _resourceTypeModel.GetById(resourceTypeGuid);

            if (resourceType == null || !resourceType.Enabled.HasValue || !resourceType.Enabled.Value)
            {
                return NotFound($"Could not find resourceType with id: {resourceTypeId}");
            }

            var projectQuota =
                _projectQuotaModel.GetWhere((x) =>
                    x.ProjectId == projectGuid &&
                    x.ResourceTypeId == resourceTypeGuid);

            var projectQuotaReturnObject = new ProjectQuotaReturnObject
            {
                Id = resourceTypeGuid,
                Name = resourceType.DisplayName,
                Used = CalculateUsed(resourceType, projectGuid),
                Allocated = projectQuota.Quota
            };

            return Json(projectQuotaReturnObject);
        }

        /// <summary>
        /// Get the max quota for a resource type.
        /// </summary>
        /// <param name="projectId">Id of the project.</param>
        /// <param name="resourceTypeId">Id of the resource</param>
        /// <returns>The maximum value for the quota.</returns>
        [HttpGet("[controller]/{projectId}/quota/{resourceTypeId}/max")]
        public ActionResult<MaxProjectQuota> GetQuotaMax(string projectId, string resourceTypeId)
        {
            var user = _authenticator.GetUser();

            if (!Guid.TryParse(projectId, out Guid projectGuid))
            {
                return BadRequest($"{projectId} is not a guid.");
            }

            var project = _projectModel.GetById(projectGuid);

            if (project == null)
            {
                return NotFound($"Could not find project with id: {projectId}");
            }

            if (!_projectModel.HasAccess(user, project, UserRoles.Owner))
            {
                return Unauthorized("The user is not authorized to perform a get on the selected project!");
            }

            if (!Guid.TryParse(resourceTypeId, out Guid resourceTypeGuid))
            {
                return BadRequest($"{resourceTypeId} is not a guid.");
            }

            var resourceType = _resourceTypeModel.GetById(resourceTypeGuid);

            if (resourceType == null || !resourceType.Enabled.HasValue || !resourceType.Enabled.Value)
            {
                return NotFound($"Could not find resourceType with id: {resourceTypeId}");
            }

            return Json(new MaxProjectQuota { Id = resourceTypeGuid, Available = _maxAvailable });
        }

        /// <summary>
        /// Update the project quota.
        /// </summary>
        /// <param name="projectId">Id of the project.</param>
        /// <param name="resourceTypeId">Id of the resource.</param>
        /// <param name="updateProjectQuotaObject">Object containing the update values.</param>
        /// <returns>NoContent (204).</returns>
        [HttpPost("[controller]/{projectId}/quota/{resourceTypeId}")]
        public IActionResult UpdateQuota(string projectId, string resourceTypeId, [FromBody] UpdateProjectQuotaObject updateProjectQuotaObject)
        {
            var user = _authenticator.GetUser();

            if (!Guid.TryParse(projectId, out Guid projectGuid))
            {
                return BadRequest($"{projectId} is not a guid.");
            }

            var project = _projectModel.GetById(projectGuid);

            if (project == null)
            {
                return NotFound($"Could not find project with id: {projectId}");
            }

            if (!_projectModel.HasAccess(user, project, UserRoles.Owner))
            {
                return Unauthorized("The user is not authorized to perform a get on the selected project!");
            }

            if (!Guid.TryParse(resourceTypeId, out Guid resourceTypeGuid))
            {
                return BadRequest($"{resourceTypeId} is not a guid.");
            }

            var resourceType = _resourceTypeModel.GetById(resourceTypeGuid);

            if (resourceType == null || !resourceType.Enabled.HasValue || !resourceType.Enabled.Value)
            {
                return NotFound($"Could not find resourceType with id: {resourceTypeId}");
            }

            if (resourceType.DisplayName.Equals("rdss3"))
            {
                return BadRequest($"Cannot adjust quota for rdss3.");
            }

            if (updateProjectQuotaObject.Allocated < 0)
            {
                return BadRequest($"Allocated {updateProjectQuotaObject.Allocated}. Cannot be less than 0.");
            }

            var projectQuotaForCurrent = _projectQuotaModel.GetWhere(x => x.ProjectId == projectGuid && x.ResourceTypeId == resourceTypeGuid);
            var used = CalculateUsed(resourceType, projectGuid);

            if (used > updateProjectQuotaObject.Allocated)
            {
                return BadRequest($"Cannot set quota ({updateProjectQuotaObject.Allocated}) below the used value ({used}).");
            }

            if (updateProjectQuotaObject.Allocated > _maxAvailable)
            {
                return BadRequest($"Cannot set quota to {updateProjectQuotaObject.Allocated}. It would exceed the limit of {_maxAvailable}");
            }

            projectQuotaForCurrent.Quota = updateProjectQuotaObject.Allocated;
            _projectQuotaModel.Update(projectQuotaForCurrent);

            return NoContent();
        }

        /// <summary>
        /// Updates the selected project 
        /// </summary>
        /// <param name="id">Id of the resource</param>
        /// <returns>Ok or Statuscode 401</returns>
        [HttpPost("[controller]/{id}")]
        public IActionResult Update(string id)
        {
            var user = _authenticator.GetUser();
            var projectObject = ObjectFactory<ProjectObject>.DeserializeFromStream(Request.Body);
            var project = _projectModel.GetById(Guid.Parse(id));
            if (_projectModel.HasAccess(user, project, UserRoles.Owner))
            {
                LogAnalyticsEditProject(project, _projectModel.GetMetadataCompleteness(projectObject), projectObject.Disciplines, projectObject.Organizations, user);
                return Ok(_projectModel.UpdateByObject(project, projectObject));
            }
            else
            {
                return Unauthorized("The user is not authorized to perform an update on the selected project!");
            }
        }

        /// <summary>
        /// Deletes the selected project
        /// </summary>
        /// <param name="id">Id of the resource</param>
        /// <returns>Json object or Statuscode 401</returns>
        [HttpDelete("[controller]/{id}")]
        public IActionResult Delete(string id)
        {
            var user = _authenticator.GetUser();
            var project = _projectModel.GetById(Guid.Parse(id));
            if (_projectModel.HasAccess(user, project, UserRoles.Owner))
            {
                var projectObject = _projectModel.CreateReturnObjectFromDatabaseObject(_projectModel.GetById(project.Id));
                LogAnalyticsDeleteProject(project, projectObject.Disciplines, projectObject.Organizations, user);
                DeleteProject(project);
                return Json(_projectModel.CreateReturnObjectFromDatabaseObject(project));
            }
            else
            {
                return Unauthorized("The user is not authorized to perform an update on the selected project!");
            }
        }

        /// <summary>
        /// Deletes the project
        /// </summary>
        /// <param name="project">Project</param>
        /// <param name="isHard">isHard</param>
        /// <param name="propegateAction">propegate Action</param>
        public void DeleteProject(Database.DataModel.Project project, bool isHard = false, bool propegateAction = true)
        {
            var subProjectModel = new SubProjectModel();
            foreach (var subProject in subProjectModel.GetAllWhere(
                (subProject) => subProject.ProjectId == project.Id
                                && (subProject.SubProjectNavigation.Deleted == false || isHard)
            ))
            {
                Database.DataModel.Project subProjectObject;
                if (isHard)
                {
                    subProjectObject = _projectModel.GetByIdIncludingDeleted(subProject.SubProjectId);
                    subProjectModel.Delete(subProject);
                }
                else
                {
                    subProjectObject = _projectModel.GetById(subProject.SubProjectId);
                }
                DeleteProject(subProjectObject, isHard, propegateAction);
            }

            foreach (var subProject in subProjectModel.GetAllWhere((subProject) => subProject.SubProjectId == project.Id))
            {
                if (isHard)
                {
                    subProjectModel.Delete(subProject);
                }
            }

            if (isHard)
            {
                var projectResourceModel = new ProjectResourceModel();
                var resourceModel = new ResourceModel();
                var resourceTypeModel = new ResourceTypeModel();
                foreach (var projectResource in projectResourceModel.GetAllWhere((projectResource) => projectResource.ProjectId == project.Id))
                {
                    var resource = resourceModel.GetById(projectResource.ResourceId);
                    var resourceTypeOptions = resourceModel.GetResourceTypeOptions(projectResource.ResourceId);
                    var resourceTypeDefinition = ResourceTypeFactory.CreateResourceTypeObject(resourceTypeModel.GetById(resource.TypeId).DisplayName, _configuration);
                    resourceTypeDefinition.DeleteResource(projectResource.ResourceId.ToString(), resourceTypeOptions);
                    projectResourceModel.Delete(projectResource);
                    resourceModel.Delete(resource);
                }

                var projectRoleModel = new ProjectRoleModel();
                foreach (var projectRole in projectRoleModel.GetAllWhere((projectRole) => projectRole.ProjectId == project.Id))
                {
                    projectRoleModel.Delete(projectRole);
                }

                var projectDisciplineModel = new ProjectDisciplineModel();
                foreach (var projectDiscipline in projectDisciplineModel.GetAllWhere((projectDiscipline) => projectDiscipline.ProjectId == project.Id))
                {
                    projectDisciplineModel.Delete(projectDiscipline);
                }

                var projectInstituteModel = new ProjectInstituteModel();
                foreach (var projectInstitute in projectInstituteModel.GetAllWhere((projectInstitute) => projectInstitute.ProjectId == project.Id))
                {
                    projectInstituteModel.Delete(projectInstitute);
                }

                foreach (var projectQuota in _projectQuotaModel.GetAllWhere((Quota) => Quota.ProjectId == project.Id))
                {
                    _projectQuotaModel.Delete(projectQuota);
                }

                foreach (var invitation in _invitationModel.GetAllWhere((x) => x.Project == project.Id))
                {
                    _invitationModel.Delete(invitation);
                }

                _activatedFeaturesModel.DeactivateAllFeatures(project);

                if (propegateAction)
                {
                    _emitter.EmitProjectDelete(new ProjectEventArgs(_configuration)
                    {
                        Project = project
                    });
                }

                _projectModel.HardDelete(project);
            }
            else
            {
                _projectModel.Delete(project);
            }
        }

        /// <summary>
        /// Creates a project
        /// </summary>
        /// <returns>Json object or Statuscode 401</returns>
        [HttpPost("[controller]")]
        public IActionResult Store()
        {
            var user = _authenticator.GetUser();
            var projectObject = ObjectFactory<ProjectObject>.DeserializeFromStream(Request.Body);

            if (projectObject?.ParentId != new Guid()
                && !_projectModel.HasAccess(user, _projectModel.GetById(projectObject.ParentId), UserRoles.Owner))
            {
                return Unauthorized("User is not allowed to create SubProjects.");
            }

            var project = _projectModel.StoreFromObject(projectObject, user, _rdfStoreConnector.GetQuotaDefault(user.Id.ToString()));

            if (projectObject.ParentId != new Guid()
                // for now, only an owner can add subprojects to projects
                && _projectModel.HasAccess(user, _projectModel.GetById(projectObject.ParentId), UserRoles.Owner))
            {
                var subProjectModel = new SubProjectModel();
                subProjectModel.LinkSubProject(projectObject.ParentId, project.Id);
            }

            _emitter.EmitProjectCreate(new ProjectEventArgs(_configuration)
            {
                Project = project,
                ProjectOwner = user
            });

            LogAnalyticsAddProject(project, _projectModel.GetMetadataCompleteness(projectObject), projectObject.Disciplines, projectObject.Organizations, user);

            return Json(_projectModel.CreateReturnObjectFromDatabaseObject(project));
        }

        /// <summary>
        /// List all invitations of a project.
        /// </summary>
        /// <param name="projectId">Project id of the project</param>
        /// <returns>List of invitations</returns>
        [HttpGet("[controller]/invitation/list/{projectId}")]
        public ActionResult<IEnumerable<InvitationReturnObject>> ListInvitations(Guid projectId)
        {
            var project = _projectModel.GetById(projectId);

            if (project == null)
            {
                return NotFound($@"The project ""{projectId}"" was not found.");
            }

            var user = _authenticator.GetUser();

            if (!_projectModel.HasAccess(user, project, UserRoles.Owner))
            {
                return Unauthorized($"You are not an owner of the project.");
            }

            var invitations = _invitationModel.GetAllWhere(x => x.Project == projectId)
                .Select(x => new InvitationReturnObject
                {
                    Id = x.Id,
                    Expiration = x.Expiration,
                    Issuer = x.Issuer,
                    ProjectId = x.Project,
                    RoleId = x.Role,
                    UserMail = x.InviteeEmail 
                });

            return new ActionResult<IEnumerable<InvitationReturnObject>>(invitations);
        }

        /// <summary>
        /// Create and send an invitation to specified mail.
        /// </summary>
        /// <param name="sendInvitationObject">Informations for sending an invitation</param>
        /// <returns>NoContent</returns>
        [HttpPost("[controller]/invitation")]
        public IActionResult SendInvitation([FromBody] SendInvitationObject sendInvitationObject)
        {
            var user = _authenticator.GetUser();

            if (!IsValidEmail(sendInvitationObject.Email))
            {
                return BadRequest($@"The email ""{sendInvitationObject.Email}"" is invalid.");
            }

            var project = _projectModel.GetById(sendInvitationObject.Project);

            if (project == null)
            {
                return NotFound($@"The project ""{sendInvitationObject.Project}"" was not found.");
            }

            if (_roleModel.GetById(sendInvitationObject.Role) == null)
            {
                return NotFound($@"The role ""{sendInvitationObject.Role}"" was not found.");
            }

            if (!_projectModel.HasAccess(user, project, UserRoles.Owner))
            {
                return Unauthorized($"You are not an owner of the project.");
            }

            var invitations = _invitationModel.GetAllWhere(
                x => x.Project == sendInvitationObject.Project &&
                     x.InviteeEmail == sendInvitationObject.Email &&
                     x.Expiration > DateTime.UtcNow
            );

            if (invitations.Any())
            {
                return BadRequest("This invitee already has a valid invitation to this project.");
            }

            var expiredInvitations = _invitationModel.GetAllWhere(
                x => x.Project == sendInvitationObject.Project &&
                     x.InviteeEmail == sendInvitationObject.Email &&
                     x.Expiration <= DateTime.UtcNow
            );

            foreach (var expiredInvitation in expiredInvitations)
            {
                _invitationModel.Delete(expiredInvitation);
            }

            var token = _invitationModel.CreateInvitation(sendInvitationObject.Project, user.Id, sendInvitationObject.Role, sendInvitationObject.Email);

            var body = new JObject
            {
                ["Args"] = new JObject()
                {
                    ["placeholder"] = new JObject()
                    {
                        ["confirmation_link"] = $@"{_configuration.GetString("coscine/local/api/additional/url")}/SitePages/Home.aspx?token={token}"
                    }
                }
            };

            NotificationBusUtil.Send(Program.Configuration, "user_invitation", NotificationBusUtil.GetUserList(new User { EmailAddress = sendInvitationObject.Email }), sendInvitationObject.Project.ToString(), body);

            return NoContent();
        }

        /// <summary>
        /// Deletes an invitation.
        /// </summary>
        /// <param name="invitationId">Id of a invitation</param>
        /// <returns>NoContent</returns>
        [HttpDelete("[controller]/invitation/{invitationId}")]
        public IActionResult DeleteInvitation(Guid invitationId)
        {
            var invitation = _invitationModel.GetById(invitationId);

            if(invitation == null)
            {
                return NotFound("Invitation was not found.");
            }

            var user = _authenticator.GetUser();

            if (!_projectModel.HasAccess(user, _projectModel.GetById(invitation.Project), UserRoles.Owner))
            {
                return Unauthorized($"You are not an owner of this project.");
            }

            _invitationModel.Delete(invitation);

            return NoContent();
        }

        /// <summary>
        /// Resolve an invitation for the current user.
        /// </summary>
        /// <param name="token">Token of a invitation</param>
        /// <returns>NoContent</returns>
        [HttpGet("[controller]/invitation/resolve/{token}")]
        public IActionResult ResolveInvitation(Guid token)
        {
            var user = _authenticator.GetUser();

            var invitation = _invitationModel.GetByToken(token);

            if(invitation == null)
            {
                return NotFound("Invitation was not found.");
            }

            if (invitation.Expiration < DateTime.UtcNow)
            {
                return BadRequest("The invitation has expired");
            }

            var project = _projectModel.GetById(invitation.Project);

            if (!_projectModel.HasAccess(_userModel.GetById(invitation.Issuer), project, UserRoles.Owner))
            {
                return Unauthorized($"The issuer is not an owner of the project.");
            }

            if (_projectRoleModel.GetAllWhere(x => x.ProjectId == invitation.Project && x.UserId == user.Id).Any())
            {
                return BadRequest($"The invitee is already part of the project.");
            }

            var role = _roleModel.GetById(invitation.Role);

            _emitter.EmitUserAdd(new UserEventArgs(_configuration)
            {
                Project = project,
                Role = role,
                User = user,
            });

            var projectRole = new ProjectRole()
            {
                RelationId = Guid.NewGuid(),
                ProjectId = invitation.Project,
                UserId = user.Id,
                RoleId = invitation.Role
            };

            _projectRoleModel.Insert(projectRole);

            _invitationModel.Delete(invitation);

            return NoContent();
        }

        private static bool IsValidEmail(string email)
        {
            try
            {
                return new System.Net.Mail.MailAddress(email).Address == email;
            }
            catch
            {
                return false;
            }
        }
        
        private void LogAnalyticsViewHome(List<string> projectIds)
        {
            _coscineLogger.AnalyticsLog(
                new AnalyticsLogObject
                {
                    Type = "Action",
                    Operation = "View Home",
                    ProjectList = projectIds
                });
        }

        private void LogAnalyticsViewProject(Database.DataModel.Project project, List<ResourceObject> resources, IEnumerable<DisciplineObject> disciplines, IEnumerable<OrganizationObject> organizations, User user)
        {
            var resourceTypes = _resourceTypeModel.GetAllWhere(x => x.Enabled.HasValue && x.Enabled.Value);

            var objects = resourceTypes.Select(x => CreateProjectQuotaReturnObject(x, project.Id));

            _coscineLogger.AnalyticsLog(
                new AnalyticsLogObject
                {
                    Type = "Action",
                    Operation = "View Project",
                    RoleId = _projectRoleModel.GetGetUserRoleForProject(project.Id, user.Id).ToString(),
                    ProjectId = project.Id.ToString(),
                    QuotaSize = objects.Select(x => $"{x.Name}: {x.Used}/{x.Allocated}").ToList(),
                    Disciplines = disciplines.Select(x => x.DisplayNameEn).ToList(),
                    Organizations = organizations.Select(x => x.DisplayName).ToList(),
                    Visibility = project.VisibilityId.HasValue ? _visibilityModel.GetById(project.VisibilityId.Value)?.DisplayName : null,
                    ResourceList = resources.Select(x => x.Id.ToString()).ToList(),
                });
        }

        private void LogAnalyticsEditProject(Database.DataModel.Project project, string metadataCompletness, IEnumerable<DisciplineObject> disciplines, IEnumerable<OrganizationObject> organizations, User user)
        {
            var resourceTypes = _resourceTypeModel.GetAllWhere(x => x.Enabled.HasValue && x.Enabled.Value);

            var objects = resourceTypes.Select(x => CreateProjectQuotaReturnObject(x, project.Id));

            _coscineLogger.AnalyticsLog(
                new AnalyticsLogObject
                {
                    Type = "Action",
                    Operation = "Edit Project",
                    RoleId = _projectRoleModel.GetGetUserRoleForProject(project.Id, user.Id).ToString(),
                    ProjectId = project.Id.ToString(),
                    QuotaSize = objects.Select(x => $"{x.Name}: {x.Used}/{x.Allocated}").ToList(),
                    MetadataCompleteness = metadataCompletness,
                    Disciplines = disciplines.Select(x => x.DisplayNameEn).ToList(),
                    Organizations = organizations.Select(x => x.DisplayName).ToList(),
                    Visibility = project.VisibilityId.HasValue ? _visibilityModel.GetById(project.VisibilityId.Value)?.DisplayName : null,
                });
        }

        private void LogAnalyticsAddProject(Database.DataModel.Project project, string metadataCompletness, IEnumerable<DisciplineObject> disciplines, IEnumerable<OrganizationObject> organizations, User user)
        {
            var resourceTypes = _resourceTypeModel.GetAllWhere(x => x.Enabled.HasValue && x.Enabled.Value);

            var objects = resourceTypes.Select(x => CreateProjectQuotaReturnObject(x, project.Id));

            _coscineLogger.AnalyticsLog(
                new AnalyticsLogObject
                {
                    Type = "Action",
                    Operation = "Add Project",
                    RoleId = _projectRoleModel.GetGetUserRoleForProject(project.Id, user.Id).ToString(),
                    ProjectId = project.Id.ToString(),
                    QuotaSize = objects.Select(x => $"{x.Name}: {x.Used}/{x.Allocated}").ToList(),
                    MetadataCompleteness = metadataCompletness,
                    Disciplines = disciplines.Select(x => x.DisplayNameEn).ToList(),
                    Organizations = organizations.Select(x => x.DisplayName).ToList(),
                    Visibility = project.VisibilityId.HasValue ? _visibilityModel.GetById(project.VisibilityId.Value)?.DisplayName : null,
                });
        }

        private void LogAnalyticsDeleteProject(Database.DataModel.Project project, IEnumerable<DisciplineObject> disciplines, IEnumerable<OrganizationObject> organizations, User user)
        {
            var resourceTypes = _resourceTypeModel.GetAllWhere(x => x.Enabled.HasValue && x.Enabled.Value);

            var objects = resourceTypes.Select(x => CreateProjectQuotaReturnObject(x, project.Id));

            _coscineLogger.AnalyticsLog(
                new AnalyticsLogObject
                {
                    Type = "Action",
                    Operation = "Delete Project",
                    RoleId = _projectRoleModel.GetGetUserRoleForProject(project.Id, user.Id).ToString(),
                    ProjectId = project.Id.ToString(),
                    QuotaSize = objects.Select(x => $"{x.Name}: {x.Used}/{x.Allocated}").ToList(),
                    Disciplines = disciplines.Select(x => x.DisplayNameEn).ToList(),
                    Organizations = organizations.Select(x => x.DisplayName).ToList(),
                    Visibility = project.VisibilityId.HasValue ? _visibilityModel.GetById(project.VisibilityId.Value)?.DisplayName : null,
                });
        }
    }
}