using DevExpress.Utils;
using DevExpress.Web;
using DevExpress.Web.ASPxThemes;
using DevExpress.Web.Mvc;
using DevExpress.XtraPrinting;
using DevExpress.XtraReports.UI;
using GreenTree.Nachtragsmanagement.Core.Authentication;
using GreenTree.Nachtragsmanagement.Core.Domain.Deviation;
using GreenTree.Nachtragsmanagement.Core.Domain.User;
using GreenTree.Nachtragsmanagement.Services.Appendix;
using GreenTree.Nachtragsmanagement.Services.Configuration;
using GreenTree.Nachtragsmanagement.Services.Deviation;
using GreenTree.Nachtragsmanagement.Services.Logging;
using GreenTree.Nachtragsmanagement.Services.Site;
using GreenTree.Nachtragsmanagement.Services.User;
using GreenTree.Nachtragsmanagement.Web.Extensions;
using GreenTree.Nachtragsmanagement.Web.Framework.Authorization;
using GreenTree.Nachtragsmanagement.Web.Models.Global;
using GreenTree.Nachtragsmanagement.Web.Models.Site;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Mime;
using System.Web;
using System.Web.Mvc;
using System.Web.UI;
using System.Web.UI.WebControls;
using static GreenTree.Nachtragsmanagement.Web.Extensions.MVCxGridViewGeneratorHelper;

namespace GreenTree.Nachtragsmanagement.Web.Controllers
{
    public class SiteController : Controller
    {
        private readonly ISiteService _siteService;
        private readonly IDeviationService _deviationService;
        private readonly IAppendixService _appendixService;
        private readonly IUserService _userService;
        private readonly IUserHelper _userHelper;
        private readonly ILogger _logger;
        private readonly IConfigurationService _configurationService;

        public SiteController(
            ISiteService siteService,
            IDeviationService deviationService,
            IAppendixService appendixService,
            IUserService userService,
            IUserHelper userHelper,
            ILogger logger,
            IConfigurationService configurationService)
        {
            _siteService = siteService;
            _deviationService = deviationService;
            _appendixService = appendixService;
            _userService = userService;
            _userHelper = userHelper;
            _logger = logger;
            _configurationService = configurationService;

            ViewData["AllUsers"] = _userService.GetAllUsers();
            ViewData["AllUsersWithRole"] =
                _userService.GetAllUsers()
                    .Select(u => new
                    {
                        Id = u.Id,
                        Description = String.Format("{0} - {1}", u.Lastname,
                            String.Join(", ", u.Roles
                                .Select(r => r.Description)))
                    })
                    .ToList();
            ViewData["DefaultAppendixState"] = _appendixService.GetDefaultState();
        }

        #region Sites

        /// <summary>
        /// Basic site view function
        /// </summary>
        [FunctionAuthorize(true, "Site-Sites")]
        public ActionResult ViewSites()
        {
            var currentUser = _userHelper.FromCookiesOrSession();

            var sites = _siteService.GetAllUserAssignedSites(currentUser);
            var siteModels = sites
                .Select(u => SiteDataModel.FromSite(u, false, _configurationService))
                .ToList();

            return View("~/Views/Sites/View.cshtml", siteModels);
        }

        /// <summary>
        /// Get JSON data of specific site
        /// </summary>
        /// <param name="id">Site id.</param>
        public ActionResult GetSite(int id = -1)
        {
            var site = _siteService.GetSiteById(id);
            if (site == null)
                return new JsonResult
                {
                    Data = "notFound",
                    JsonRequestBehavior = JsonRequestBehavior.AllowGet
                };

            var siteModel = SiteDataModel.FromSite(site, false, _configurationService);
            var siteTreeModel = SiteTreeDataModel.TreeFromSite(site);

            siteModel.SiteTreeData = siteTreeModel;
            
            return new JsonResult
            {
                Data = JsonConvert.SerializeObject(siteModel),
                JsonRequestBehavior = JsonRequestBehavior.AllowGet
            };
        }

        /// <summary>
        /// Gets a full deviation description of a given site
        /// </summary>
        /// <param name="id">The site id.</param>
        public ActionResult GetDeviationDescription(int id)
        {
            var result = new JsonResult
            {
                JsonRequestBehavior = JsonRequestBehavior.AllowGet,
                Data = String.Empty
            };

            var site = _siteService.GetSiteById(id);
            var siteModel = SiteDataModel.FromSite(site, false, _configurationService);

            result.Data = siteModel.HtmlDeviationDescription;

            return result;
        }

        /// <summary>
        /// Callback result for site grid
        /// </summary>
        /// <param name="scrollHeight">The height of the grid scrollable component.</param>
        public ActionResult PartialSites(int scrollHeight = -1)
        {
            var currentUser = _userHelper.FromCookiesOrSession();

            var sites = _siteService.GetAllUserAssignedSites(currentUser);
            var siteModels = sites
                .Select(u => SiteDataModel.FromSite(u, false, _configurationService))
                .ToList();

            ViewData["ScrollHeight"] = scrollHeight;

            return PartialView("~/Views/Sites/_SiteGridPartial.cshtml", siteModels);
        }

        /// <summary>
        /// Export result for site grid
        /// </summary>
        [HttpPost]
        public ActionResult ExportPartialSites(string displayMode, string exportformat)
        {
            if (String.IsNullOrEmpty(displayMode))
                return new EmptyResult();

            var currentUser = _userHelper.FromCookiesOrSession();

            var sites = _siteService.GetAllUserAssignedSites(currentUser);
            var siteModels = sites
                .Select(u => SiteDataModel.FromSite(u, false, _configurationService))
                .ToList();

            var viewContext = new ViewContext();
            var viewPage = new ViewPage();
            var htmlHelper = new System.Web.Mvc.HtmlHelper(viewContext, viewPage);

            MVCxGridViewState gridViewState = (MVCxGridViewState)Session["SiteGridViewState"];

            var settings = GridViewSettingsHelper.SiteGridViewSettings(htmlHelper);

            if (gridViewState != null)
            {
                var generator = new MVCReportGeneratonHelper();

                generator.CustomizeColumnsCollection += new CustomizeColumnsCollectionEventHandler(generator_CustomizeColumnsCollection);
                generator.CustomizeGroupColumnSummary += new CustomizeColumnGroupSummaryEventHandler(generator_CustomizeGroupColumnSummary);
                generator.CustomizeTotalColumnSummary += new CustomizeColumnTotalSummaryEventHandler(generator_CustomizeTotalColumnSummary);
                generator.PageSummaryGetResult += new SummaryGetResultHandler(generator_PageSummaryGetResult);

                var report = generator.GenerateMVCReport(gridViewState, siteModels, "Baustellenliste");

                if (displayMode == "popup")
                {
                    return PartialView("~/Views/Shared/_PrintPopupPartial.cshtml",
                        new PrintGridModel(report, "devGridViewSite",
                            new { Controller = "Site", Action = "ExportPartialSites",
                                displayMode = "callback", exportformat = String.Empty },
                            new { Controller = "Site", Action = "ExportPartialSites",
                                displayMode = "export", exportformat = String.Empty }));
                }
                else if (displayMode == "callback")
                {
                    return PartialView("~/Views/Shared/_PrintDocumentViewerPartial.cshtml",
                        new PrintGridModel(report, "devGridViewSite",
                            new { Controller = "Site", Action = "ExportPartialSites",
                                displayMode = "callback", exportformat = String.Empty },
                            new { Controller = "Site", Action = "ExportPartialSites",
                                displayMode = "export", exportformat = String.Empty }));
                }
                else if (displayMode == "export")
                {
                    switch (exportformat.ToLower())
                    {
                        case "xlsx":
                            settings.TotalSummary["DeviationValue"].DisplayFormat = "{0:c2}";
                            settings.TotalSummary["Description"].DisplayFormat = "Anzahl = {0:n0}";
                            return GridViewExtension.ExportToXlsx(settings, siteModels);
                        case "xls":
                            settings.TotalSummary["DeviationValue"].DisplayFormat = "{0:c2}";
                            settings.TotalSummary["Description"].DisplayFormat = "Anzahl = {0:n0}";
                            return GridViewExtension.ExportToXls(settings, siteModels);
                        case "pdf":
                            report.Name = "Baustellenliste";
                            return DocumentViewerExtension.ExportTo(report);
                    }
                }

                return new EmptyResult();
            }
            else
                return new EmptyResult();
        }

        private decimal accumulatedCustomSummaryDeviationValue = 0;
        private decimal accumulatedCustomSummaryCount = 0;

        /// <summary>
        /// Set custom summary result for page
        /// </summary>
        /// <param name="source"></param>
        /// <param name="e"></param>
        private void generator_PageSummaryGetResult(object source, SummaryGetResultEventArgs e)
        {
            var label = (XRLabel)source;

            if (label.Tag.ToString() == "DeviationValue")
            {
                accumulatedCustomSummaryDeviationValue += e.CalculatedValues.OfType<decimal>().Sum();
                e.Result = accumulatedCustomSummaryDeviationValue;
            }

            if (label.Tag.ToString() == "Description")
            {
                accumulatedCustomSummaryCount += e.CalculatedValues.Count;
                e.Result = accumulatedCustomSummaryCount;
            }

            e.Handled = true;
        }

        /// <summary>
        /// Customize created columns
        /// </summary>
        private void generator_CustomizeColumnsCollection(object source, ColumnsCreationEventArgs e)
        {
            foreach (var column in e.ColumnsInfo)
            {
                if (column.ColumnCaption == "#") { column.IsVisible = false; }
                if (column.FieldName == "CustomNumber") { column.ColumnWidth = 60; }
                if (column.FieldName == "SiteDescription") { column.ColumnWidth = 80; }
                if (column.FieldName == "Start") { column.ColumnWidth = 65; }
                if (column.FieldName == "End") { column.ColumnWidth = 65; }
                if (column.FieldName == "DeviationDescription") { column.IsVisible = false; column.IsDetail = true; }
                if (column.FieldName == "AppendixDescription") { column.ColumnWidth = 60; }
                if (column.FieldName == "DeviationValue") { column.ColumnWidth = 70; }
                if (column.FieldName == "UserDescription") { column.ColumnWidth = 120; }
                if (column.FieldName == "Comment") { column.ColumnWidth = 100; }
            }
        }

        /// <summary>
        /// Customize column summary
        /// </summary>
        private void generator_CustomizeGroupColumnSummary(object source, ColumnSummaryCreationEventArgs e)
        {
            if (e.FieldName == "DeviationValue") { e.Summary.FormatString = "VA-Wert ∑ = {0:c2}"; }
            if (e.FieldName == "Description") { e.Summary.FormatString = "Alle = {0:n0}"; }
        }

        /// <summary>
        /// Customize column summary
        /// </summary>
        private void generator_CustomizeTotalColumnSummary(object source, ColumnSummaryCreationEventArgs e)
        {
            if (e.FieldName == "DeviationValue") { e.Summary.FormatString = "{0:c2}"; }
            if (e.FieldName == "Description") { e.Summary.FormatString = "Alle = {0:n0}"; }
        }

        /// <summary>
        /// Callback result for site appendices
        /// </summary>
        /// <param name="siteId">Id of site.</param>
        public ActionResult PartialDeviationAppendices(int siteId)
        {
            var site = _siteService.GetSiteById(siteId);

            var siteModel = SiteDataModel.FromSite(site, false, _configurationService);
            var siteTreeModel = SiteTreeDataModel.TreeFromSite(site);

            siteModel.SiteTreeData = siteTreeModel;

            return PartialView("~/Views/Sites/_SiteEditTreePartial.cshtml", siteModel);
        }

        /// <summary>
        /// Partial edit for editing of existing or for new site
        /// </summary>
        /// <param name="id">Id for existing site, otherweise -1.</param>
        public ActionResult EditSite(int id = -1)
        {
            var site = _siteService.GetSiteById(id);
            var siteModel = SiteDataModel.FromSite(site, true, _configurationService);

            var siteTreeModel = SiteTreeDataModel.TreeFromSite(site);

            siteModel.SiteTreeData = siteTreeModel;

            return PartialView("~/Views/Sites/_SiteEditPartial.cshtml", siteModel);
        }

        /// <summary>
        /// Partial edit result if ModelState is valid, otherwise simple JSON result for success
        /// </summary>
        /// <param name="siteModel">Site model to be saved.</param>
        [HttpPost, ValidateInput(false)]
        public ActionResult EditSite(SiteDataModel siteModel)
        {
            try
            {
                if (!ModelState.IsValid)
                {
                    foreach (var role in siteModel.UserValues)
                        siteModel.UserDescriptions.Add(
                            ((IList<User>)ViewData["AllUsers"])
                                .First(r => r.Id == role).Lastname);

                    if (siteModel.Id != -1)
                    {
                        var site = _siteService.GetSiteById(siteModel.Id);

                        var siteTreeModel = SiteTreeDataModel.TreeFromSite(site);

                        siteModel.SiteTreeData = siteTreeModel;
                    }

                    return PartialView("~/Views/Sites/_SiteEditPartial.cshtml", siteModel);
                }

                var selectedUsers = _userService.GetUsersByIds(siteModel.UserValues.ToArray());

                if (siteModel.Id == -1)
                {
                    var site = siteModel.ToSite();

                    site.SetUsers(selectedUsers);

                    _siteService.InsertSite(site);

                    _logger.Entity(site, Core.Domain.Logging.LogEntityActivity.Insert, _userHelper.FromCookiesOrSession());
                }
                else
                {
                    var site = _siteService.GetSiteById(siteModel.Id);

                    site.CustomNumber = siteModel.CustomNumber;
                    site.Description = siteModel.Description;
                    site.Start = siteModel.Start;
                    site.End = siteModel.End;
                    site.Comment = siteModel.Comment;

                    site.SetUsers(selectedUsers);

                    _siteService.UpdateSite(site);

                    _logger.Entity(site, Core.Domain.Logging.LogEntityActivity.Update, _userHelper.FromCookiesOrSession());
                }

                return new JsonResult
                {
                    Data = "success"
                };
            }
            catch (Exception ex)
            {
                _logger.Error("Fehler bei Speicherung einer Baustelle.", ex, _userHelper.FromCookiesOrSession());

                return PartialView("~/Views/Shared/_PopupError.cshtml", ex);
            }
        }


        /// <summary>
        /// Partial edit result if ModelState is valid, otherwise simple JSON result for success
        /// </summary>
        /// <param name="siteModel">Site model to be saved.</param>
        [HttpPost, ValidateInput(false)]
        public ActionResult EditSiteForAppend(SiteDataModel siteModel)
        {
            try
            {
                if (!ModelState.IsValid)
                {
                    foreach (var role in siteModel.UserValues)
                        siteModel.UserDescriptions.Add(
                            ((IList<User>)ViewData["AllUsers"])
                                .First(r => r.Id == role).Lastname);

                    return PartialView("~/Views/Sites/_SiteEditPartial.cshtml", siteModel);
                }

                var selectedUsers = _userService.GetUsersByIds(siteModel.UserValues.ToArray());
                var siteId = siteModel.Id;

                if (siteModel.Id == -1)
                {
                    var site = siteModel.ToSite();

                    site.SetUsers(selectedUsers);

                    _siteService.InsertSite(site);

                    siteId = site.Id;

                    _logger.Entity(site, Core.Domain.Logging.LogEntityActivity.Insert, _userHelper.FromCookiesOrSession());
                }

                return EditSite(siteId);
            }
            catch (Exception ex)
            {
                _logger.Error("Fehler bei Speicherung einer Baustelle für einen Nachtrag.", ex, _userHelper.FromCookiesOrSession());

                return PartialView("~/Views/Shared/_PopupError.cshtml", ex);
            }
        }

        /// <summary>
        /// Simple JSON result for deleting a specific site
        /// </summary>
        /// <param name="id">Site id.</param>
        [HttpPost]
        public ActionResult DeleteSite(int id)
        {
            try
            {
                var site = _siteService.GetSiteById(id);

                if (site != null)
                    _siteService.DeleteSite(site);

                _logger.Entity(site, Core.Domain.Logging.LogEntityActivity.Delete, _userHelper.FromCookiesOrSession());

                return new JsonResult
                {
                    Data = "success"
                };
            }
            catch (Exception ex)
            {
                _logger.Error("Fehler bei Löschung einer Baustelle.", ex, _userHelper.FromCookiesOrSession());

                return PartialView("~/Views/Shared/_PopupError.cshtml", ex);
            }
        }

        #endregion

        #region Assigning

        /// <summary>
        /// Assigns a deviation to an existing entity of type site or appendix
        /// </summary>
        /// <param name="siteId">The siteId in which the context works.</param>
        /// <param name="sourceKey">The sourceKey of the deviation.</param>
        /// <param name="targetKey">The targetKey of the site or appendix.</param>
        public ActionResult AssignDeviationToEntity(int siteId, string sourceKey, string targetKey)
        {
            if (!sourceKey.StartsWith("d"))
                return new EmptyResult();

            if (sourceKey == targetKey)
                return new EmptyResult();

            var determinedTargetKey = targetKey;

            if (targetKey.StartsWith("d"))
            {
                return new EmptyResult();
                //var targetDeviationId = Convert.ToInt32(sourceKey.Replace("d_", String.Empty));
                //var targetDeviation = _deviationService.GetDeviationById(targetDeviationId);

                //if (targetDeviation == null)
                //    return new EmptyResult();

                //if (targetDeviation.Site != null)
                //    determinedTargetKey = "a_0";

                //if (targetDeviation.Appendix != null)
                //    determinedTargetKey = String.Format("a_{0}", targetDeviation.AppendixId);
            }

            var deviationId = Convert.ToInt32(sourceKey.Replace("d_", String.Empty));
            var deviation = _deviationService.GetDeviationById(deviationId);

            if (determinedTargetKey == "a_0")
            {
                try
                {
                    var site = _siteService.GetSiteById(siteId);

                    if (deviation.Site != null)
                        return new EmptyResult();

                    deviation.Appendix = null;
                    deviation.AppendixId = null;

                    site.Deviations.Add(deviation);

                    _siteService.UpdateSite(site);
                    _deviationService.UpdateDeviation(deviation);

                    _logger.Entity(deviation, Core.Domain.Logging.LogEntityActivity.Update, _userHelper.FromCookiesOrSession());
                }
                catch (Exception ex)
                {
                    _logger.Error("Fehler bei Zuweisung einer VA zu einer Baustelle.", ex, _userHelper.FromCookiesOrSession());

                    return PartialView("~/Views/Shared/_PopupError.cshtml", ex);
                }
            }
            else
            {
                try
                {
                    var appendixId = Convert.ToInt32(determinedTargetKey.Replace("a_", String.Empty));
                    var appendix = _appendixService.GetAppendixById(appendixId);

                    if (deviation.AppendixId == appendixId)
                        return new EmptyResult();

                    deviation.Site = null;
                    deviation.SiteId = null;

                    appendix.Deviations.Add(deviation);

                    _appendixService.UpdateAppendix(appendix);
                    _deviationService.UpdateDeviation(deviation);

                    _logger.Entity(deviation, Core.Domain.Logging.LogEntityActivity.Update, _userHelper.FromCookiesOrSession());
                }
                catch (Exception ex)
                {
                    _logger.Error("Fehler bei Zuweisung einer VA zu einem Nachtrag.", ex, _userHelper.FromCookiesOrSession());

                    return PartialView("~/Views/Shared/_PopupError.cshtml", ex);
                }
            }

            return new JsonResult
            {
                Data = "success"
            };
        }

        #endregion
    }
}