using Coscine.Api.Project.Controllers;
using Coscine.ApiCommons.Factories;
using Coscine.Database.Models;
using Coscine.Database.ReturnObjects;
using Coscine.Database.Util;
using Coscine.Logging;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace Coscine.Api.Project.Tests
{
    [TestFixture]
    public class ProjectControllerTests : DefaultControllerTests<ProjectController>
    {
        private static ILogger<ProjectController> projectLogger = null;

        private static ILogger<ProjectController> ProjectLogger
        {
            get
            {
                if (projectLogger == null)
                {
                    CoscineLoggerConfiguration.SetConfig();
                    projectLogger = new NullLogger<ProjectController>();
                }
                return projectLogger;
            }
        }

        public ProjectControllerTests() : base(new ProjectController(ProjectLogger))
        {
        }

        //[Test]
        public void OwnsTest()
        {
            ProjectModel projectModel = new ProjectModel();
            Assert.IsTrue(projectModel.HasAccess(Users[0], Projects[0], UserRoles.Owner));
            Assert.IsFalse(projectModel.HasAccess(Users[0], Projects[1], UserRoles.Owner));

            var all = projectModel.GetAllWhere((project) =>
                (from projectRole in project.ProjectRoles
                 where projectRole.User == Users[0]
                     && projectRole.Role.DisplayName == "Owner"
                 select projectRole).Any()
            );
            Assert.IsTrue(all.ToArray().Length == 1);
        }

        //[Test]
        public void TestControllerIndex()
        {
            var actionResult = Controller.Index().Result;
            Assert.IsTrue(actionResult.GetType() == typeof(OkObjectResult));
        }

        //[Test]
        public void TestControllerGet()
        {
            var actionResult = Controller.Get(Projects[0].Id).Result;
            Assert.IsTrue(actionResult.GetType() == typeof(OkObjectResult));

            OkObjectResult okObjectResult = (OkObjectResult)actionResult;
            Assert.IsTrue(okObjectResult.Value.GetType() == typeof(ProjectObject));

            ProjectObject projectObject = (ProjectObject)okObjectResult.Value;

            Assert.IsTrue(projectObject.Id == Projects[0].Id);
            Assert.IsTrue(projectObject.Description == Projects[0].Description);
            Assert.IsTrue(projectObject.DisplayName == Projects[0].DisplayName);
            Assert.IsTrue(projectObject.StartDate == Projects[0].StartDate);
            Assert.IsTrue(projectObject.EndDate == Projects[0].EndDate);
            Assert.IsTrue(projectObject.Keywords == Projects[0].Keywords);
        }

        //[Test]
        public void TestControllerUpdate()
        {
            var actionResult = Controller.Get(Projects[0].Id).Result;
            ProjectObject projectObject = (ProjectObject)((OkObjectResult)actionResult).Value;

            projectObject.Description = "ChangedDescription";

            Stream stream = ObjectFactory<ProjectObject>.SerializeToStream(projectObject);

            FakeControllerContext(Users[0], stream);

            var updateResult = Controller.Update(projectObject.Id, projectObject);
            Assert.IsTrue(updateResult.GetType() == typeof(OkObjectResult));

            // Cleanup
            stream.Close();

            var actionResultPrevious = Controller.Get(Projects[1].Id).Result;
            ProjectObject projectObjectPrevious = (ProjectObject)((OkObjectResult)actionResultPrevious).Value;

            stream = ObjectFactory<ProjectObject>.SerializeToStream(projectObjectPrevious);

            FakeControllerContext(Users[0], stream);

            updateResult = Controller.Update(projectObjectPrevious.Id, projectObjectPrevious);
            Assert.IsTrue(updateResult.GetType() == typeof(UnauthorizedObjectResult));

            // Cleanup
            stream.Close();
        }

        //[Test]
        public void TestControllerStore()
        {
            ProjectObject newProjectObject = new ProjectObject(
                Guid.NewGuid(),
                "NewProject",
                "NewDisplayName",
                DateTime.Now,
                DateTime.Now.AddYears(1),
                "test2;test3",
                "abc",
                "investigator",
                "grandId",
                new List<DisciplineObject>() { new DisciplineObject(Discipline.Id, Discipline.Url, Discipline.DisplayNameDe, Discipline.DisplayNameEn) },
                new List<OrganizationObject>() { new OrganizationObject(Organization, Organization) },
                new VisibilityObject(Visibility.Id, Visibility.DisplayName), Guid.NewGuid().ToString(),
                DateTime.Now,
                Guid.NewGuid(),
                Users[0].Id
                );

            var stream = ObjectFactory<ProjectObject>.SerializeToStream(newProjectObject);

            FakeControllerContext(Users[0], stream);

            var actionResult = Controller.Store(newProjectObject);
            Assert.IsTrue(actionResult.GetType() == typeof(JsonResult));

            JsonResult jsonResult = (JsonResult)actionResult;
            Assert.IsTrue(jsonResult.Value.GetType() == typeof(ProjectObject));

            ProjectObject createdProjectObject = (ProjectObject)jsonResult.Value;

            Assert.IsTrue(createdProjectObject.Description == newProjectObject.Description);
            Assert.IsTrue(createdProjectObject.DisplayName == newProjectObject.DisplayName);
            Assert.IsTrue(createdProjectObject.StartDate == newProjectObject.StartDate);
            Assert.IsTrue(createdProjectObject.EndDate == newProjectObject.EndDate);
            Assert.IsTrue(createdProjectObject.Keywords == newProjectObject.Keywords);

            // Cleanup
            stream.Close();

            ProjectModel projectModel = new ProjectModel();
            var project = projectModel.GetById(createdProjectObject.Id);
            Controller.DeleteProject(project, true);
        }

        //[Test]
        public void TestControllerDelete()
        {
            ProjectObject newProjectObject = new ProjectObject(
                Guid.NewGuid(),
                "NewProject",
                "NewDisplayName",
                DateTime.Now,
                DateTime.Now.AddYears(1),
                "test2;test3",
                "abc",
                "investigator",
                "grandId",
                new List<DisciplineObject>() { new DisciplineObject(Discipline.Id, Discipline.Url, Discipline.DisplayNameDe, Discipline.DisplayNameEn) },
                new List<OrganizationObject>() { new OrganizationObject(Organization, Organization) },
                new VisibilityObject(Visibility.Id, Visibility.DisplayName), Guid.NewGuid().ToString(),
                DateTime.Now,
                creator: Users[0].Id
                );

            var stream = ObjectFactory<ProjectObject>.SerializeToStream(newProjectObject);

            FakeControllerContext(Users[0], stream);

            var actionResult = Controller.Store(newProjectObject);
            Assert.IsTrue(actionResult.GetType() == typeof(JsonResult));

            JsonResult result = (JsonResult)actionResult;
            Assert.IsTrue(result.Value.GetType() == typeof(ProjectObject));

            ProjectObject createdProjectObject = (ProjectObject)result.Value;
            stream.Close();

            stream = ObjectFactory<ProjectObject>.SerializeToStream(createdProjectObject);

            FakeControllerContext(Users[0], stream);

            ProjectModel projectModel = new ProjectModel();
            var project = projectModel.GetById(createdProjectObject.Id);

            actionResult = Controller.Delete(createdProjectObject.Id);
            Assert.IsTrue(actionResult.GetType() == typeof(JsonResult));

            result = (JsonResult)actionResult;
            Assert.IsTrue(result.Value.GetType() == typeof(ProjectObject));

            stream.Close();
            Controller.DeleteProject(project, true);
        }

        //[Test]
        public void TestControllerDeleteWithSubProjects()
        {
            ProjectObject newProjectObject = new ProjectObject(
                Guid.NewGuid(),
                "NewProject",
                "NewDisplayName",
                DateTime.Now,
                DateTime.Now.AddYears(1),
                "test2;test3",
                "abc",
                "investigator",
                "grandId",
                new List<DisciplineObject>() { new DisciplineObject(Discipline.Id, Discipline.Url, Discipline.DisplayNameDe, Discipline.DisplayNameEn) },
                new List<OrganizationObject>() { new OrganizationObject(Organization, Organization) },
                new VisibilityObject(Visibility.Id, Visibility.DisplayName), Guid.NewGuid().ToString(),
                DateTime.Now,
                creator: Users[0].Id
                );

            var stream = ObjectFactory<ProjectObject>.SerializeToStream(newProjectObject);

            FakeControllerContext(Users[0], stream);

            var actionResult = Controller.Store(newProjectObject);

            JsonResult result = (JsonResult)actionResult;
            ProjectObject createdProjectObject = (ProjectObject)result.Value;
            stream.Close();

            newProjectObject = new ProjectObject(
                Guid.NewGuid(),
                "NewProject",
                "NewDisplayName",
                DateTime.Now,
                DateTime.Now.AddYears(1),
                "test2;test3",
                "abc",
                "investigator",
                "grandId",
                new List<DisciplineObject>() { new DisciplineObject(Discipline.Id, Discipline.Url, Discipline.DisplayNameDe, Discipline.DisplayNameEn) },
                new List<OrganizationObject>() { new OrganizationObject(Organization, Organization) },
                new VisibilityObject(Visibility.Id, Visibility.DisplayName), Guid.NewGuid().ToString(),
                DateTime.Now,
                createdProjectObject.Id,
                Users[0].Id
                );

            stream = ObjectFactory<ProjectObject>.SerializeToStream(newProjectObject);

            FakeControllerContext(Users[0], stream);

            actionResult = Controller.Store(newProjectObject);

            stream = ObjectFactory<ProjectObject>.SerializeToStream(createdProjectObject);

            FakeControllerContext(Users[0], stream);

            var projectModel = new ProjectModel();
            var project = projectModel.GetById(createdProjectObject.Id);

            actionResult = Controller.Delete(createdProjectObject.Id);
            Assert.IsTrue(actionResult.GetType() == typeof(JsonResult));

            result = (JsonResult)actionResult;
            Assert.IsTrue(result.Value.GetType() == typeof(ProjectObject));

            stream.Close();
            Controller.DeleteProject(project, true);
        }

        //[Test]
        public void TestControllerStoreWithSubProject()
        {
            ProjectObject newProjectObject = new ProjectObject(
                Guid.NewGuid(),
                "NewProject",
                "NewDisplayName",
                DateTime.Now,
                DateTime.Now.AddYears(1),
                "test2;test3",
                "abc",
                "investigator",
                "grandId",
                new List<DisciplineObject>() { new DisciplineObject(Discipline.Id, Discipline.Url, Discipline.DisplayNameDe, Discipline.DisplayNameEn) },
                new List<OrganizationObject>() { new OrganizationObject(Organization, Organization) },
                new VisibilityObject(Visibility.Id, Visibility.DisplayName), Guid.NewGuid().ToString(),
                DateTime.Now,
                Guid.NewGuid(),
                Users[0].Id
                );

            var stream = ObjectFactory<ProjectObject>.SerializeToStream(newProjectObject);
            FakeControllerContext(Users[0], stream);
            var actionResult = Controller.Store(newProjectObject);

            JsonResult result = (JsonResult)actionResult;
            ProjectObject createdProjectObject = (ProjectObject)result.Value;

            ProjectObject newSubProjectObject = new ProjectObject(
                Guid.NewGuid(),
                "NewProject",
                "NewDisplayName",
                DateTime.Now,
                DateTime.Now.AddYears(1),
                "test2;test3",
                "abc",
                "investigator",
                "grandId",
                new List<DisciplineObject>() { new DisciplineObject(Discipline.Id, Discipline.Url, Discipline.DisplayNameDe, Discipline.DisplayNameEn) },
                new List<OrganizationObject>() { new OrganizationObject(Organization, Organization) },
                new VisibilityObject(Visibility.Id, Visibility.DisplayName), Guid.NewGuid().ToString(),
                DateTime.Now,
                createdProjectObject.Id,
                Users[0].Id
                );

            var subStream = ObjectFactory<ProjectObject>.SerializeToStream(newSubProjectObject);
            FakeControllerContext(Users[0], subStream);
            var subActionResult = Controller.Store(newProjectObject);

            JsonResult resultSubProject = (JsonResult)subActionResult;
            ProjectObject createdSubProjectObject = (ProjectObject)resultSubProject.Value;

            SubProjectModel subProjectModel = new SubProjectModel();
            var subProjects = subProjectModel.GetAllWhere((x) => x.ProjectId == createdProjectObject.Id);
            foreach (var subProject in subProjects)
            {
                Assert.IsTrue(subProject.SubProjectId == createdSubProjectObject.Id);
                Assert.IsTrue(subProject.ProjectId == createdProjectObject.Id);
            }

            // Cleanup
            stream.Close();
            subStream.Close();

            ProjectModel projectModel = new ProjectModel();
            var project = projectModel.GetById(createdProjectObject.Id);
            Controller.DeleteProject(project, true);
        }

        //[Test]
        public void CountTest()
        {
            ProjectModel projectModel = new ProjectModel();
            Assert.IsTrue(Previous == projectModel.GetAll().ToArray().Length - Projects.Count);
        }
    }
}