using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Logging;
using Microsoft.AspNetCore.Authorization;
using Sockeye.Models;
using Sockeye.Api.ControllerHelpers;
using Sockeye.Biz;
using Newtonsoft.Json.Linq;



namespace Sockeye.Api.Controllers
{

    /// <summary>
    /// 
    /// </summary>
    [ApiController]
    [ApiVersion("8.0")]
    [Route("api/v{version:apiVersion}/tag-list")]
    [Produces("application/json")]
    [Authorize]
    public class TagController : ControllerBase
    {
        private readonly AyContext ct;
        private readonly ILogger<TagController> log;
        private readonly ApiServerState serverState;


        /// <summary>
        /// ctor
        /// </summary>
        /// <param name="dbcontext"></param>
        /// <param name="logger"></param>
        /// <param name="apiServerState"></param>
        public TagController(AyContext dbcontext, ILogger<TagController> logger, ApiServerState apiServerState)
        {
            ct = dbcontext;
            log = logger;
            serverState = apiServerState;
        }


        /// <summary>
        /// Get tag list
        /// </summary>
        /// <param name="query">The query to filter the returned list by</param>
        /// <returns>Filtered list (maximum 25 items are returned for any query)</returns>
        [HttpGet("list")]
        public async Task<IActionResult> GetList([FromQuery] string query)
        {
            if (serverState.IsClosed)
                return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
            return Ok(ApiOkResponse.Response(await TagBiz.TagListFilteredAsync(ct, query)));
        }

        /// <summary>
        /// Get tag cloud list
        /// </summary>
        /// <param name="query">The query to filter the returned list by</param>
        /// <returns>List</returns>
        [HttpGet("cloudlist")]
        public async Task<IActionResult> GetCloudList([FromQuery] string query)
        {
            if (serverState.IsClosed)
                return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
            return Ok(ApiOkResponse.Response(await TagBiz.CloudListFilteredAsync(ct, query)));
        }




        /////////////////////////////////////////////////////////////
        //BATCH OPS
        //
        //

        /// <summary>
        /// Batch add tags to list of object id's specified
        /// </summary>      
        /// <param name="tag"></param>
        /// <param name="selectedRequest"></param>
        /// <returns>Job Id</returns>
        [HttpPost("batch-add/{tag}")]
        public async Task<IActionResult> BatchAdd([FromRoute] string tag, [FromBody] DataListSelectedRequest selectedRequest)
        {
            if (!serverState.IsOpen)
                return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
            if (!ModelState.IsValid)
                return BadRequest(new ApiErrorResponse(ModelState));

            if (!selectedRequest.SockType.HasAttribute(typeof(CoreBizObjectAttribute)))
                return BadRequest(new ApiErrorResponse(ApiErrorCode.INVALID_OPERATION, null, "Not a taggable object type"));

            if (!Authorized.HasModifyRole(HttpContext.Items, selectedRequest.SockType))
                return StatusCode(403, new ApiNotAuthorizedResponse());

            tag = TagBiz.NormalizeTag(tag);
            if (string.IsNullOrWhiteSpace(tag))
                return BadRequest(new ApiErrorResponse(ApiErrorCode.VALIDATION_REQUIRED, null, "tag required"));

            //Rehydrate id list if necessary
            if (selectedRequest.SelectedRowIds.Length == 0)
                selectedRequest.SelectedRowIds = await DataListSelectedProcessingOptions.RehydrateIdList(
                    selectedRequest,
                    ct,
                    UserRolesFromContext.Roles(HttpContext.Items),
                    log,
                    UserIdFromContext.Id(HttpContext.Items),
                    UserTranslationIdFromContext.Id(HttpContext.Items));

            var JobName = $"LT:BatchJob LT:Add LT:Tag \"{tag}\" LT:{selectedRequest.SockType} ({selectedRequest.SelectedRowIds.LongLength}) LT:User {UserNameFromContext.Name(HttpContext.Items)}";
            JObject o = JObject.FromObject(new
            {
                idList = selectedRequest.SelectedRowIds,
                tag = tag
            });

            OpsJob j = new OpsJob();
            j.Name = JobName;
            j.SockType = selectedRequest.SockType;
            j.JobType = JobType.BatchCoreObjectOperation;
            j.SubType = JobSubType.TagAdd;
            j.Exclusive = false;
            j.JobInfo = o.ToString();
            await JobsBiz.AddJobAsync(j);
            await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserIdFromContext.Id(HttpContext.Items), 0, SockType.ServerJob, SockEvent.Created, JobName), ct);
            return Accepted(new { JobId = j.GId });
        }

        /// <summary>
        /// Batch add tags to all objects of type specified
        /// </summary>
        /// <param name="sockType"></param>
        /// <param name="tag"></param>        
        /// <returns>Job Id</returns>
        [HttpPost("batch-add-any/{sockType}/{tag}")]
        public async Task<IActionResult> BatchAddAny([FromRoute] SockType sockType, [FromRoute] string tag)
        {
            if (!serverState.IsOpen)
                return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
            if (!ModelState.IsValid)
                return BadRequest(new ApiErrorResponse(ModelState));
            if (!sockType.HasAttribute(typeof(CoreBizObjectAttribute)))
                return BadRequest(new ApiErrorResponse(ApiErrorCode.INVALID_OPERATION, null, "Not a taggable object type"));
            if (!Authorized.HasModifyRole(HttpContext.Items, sockType))
                return StatusCode(403, new ApiNotAuthorizedResponse());

            tag = TagBiz.NormalizeTag(tag);
            if (string.IsNullOrWhiteSpace(tag))
                return BadRequest(new ApiErrorResponse(ApiErrorCode.VALIDATION_REQUIRED, null, "tag"));

            var JobName = $"LT:BatchJob LT:Add LT:Tag \"{tag}\" LT:{sockType.ToString()} (LT:GridRowFilterDropDownAllItem) LT:User {UserNameFromContext.Name(HttpContext.Items)}";
            JObject o = JObject.FromObject(new
            {
                tag = tag
            });

            OpsJob j = new OpsJob();
            j.Name = JobName;
            j.SockType = sockType;
            j.JobType = JobType.BatchCoreObjectOperation;
            j.SubType = JobSubType.TagAddAny;
            j.Exclusive = false;
            j.JobInfo = o.ToString();
            await JobsBiz.AddJobAsync(j);
            await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserIdFromContext.Id(HttpContext.Items), 0, SockType.ServerJob, SockEvent.Created, JobName), ct);
            return Accepted(new { JobId = j.GId });
        }

        /// <summary>
        /// Batch remove tags to list of object id's specified
        /// </summary>        
        /// <param name="tag"></param>
        /// <param name="selectedRequest"></param>
        /// <returns>Job Id</returns>
        [HttpPost("batch-remove/{tag}")]
        public async Task<IActionResult> BatchRemove([FromRoute] string tag, [FromBody] DataListSelectedRequest selectedRequest)
        {
            if (!serverState.IsOpen)
                return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
            if (!ModelState.IsValid)
                return BadRequest(new ApiErrorResponse(ModelState));

            if (!selectedRequest.SockType.HasAttribute(typeof(CoreBizObjectAttribute)))
                return BadRequest(new ApiErrorResponse(ApiErrorCode.INVALID_OPERATION, null, "Not a taggable object type"));
            if (!Authorized.HasModifyRole(HttpContext.Items, selectedRequest.SockType))
                return StatusCode(403, new ApiNotAuthorizedResponse());

            tag = TagBiz.NormalizeTag(tag);
            if (string.IsNullOrWhiteSpace(tag))
                return BadRequest(new ApiErrorResponse(ApiErrorCode.VALIDATION_REQUIRED, null, "tag"));

            //Rehydrate id list if necessary
            if (selectedRequest.SelectedRowIds.Length == 0)
                selectedRequest.SelectedRowIds = await DataListSelectedProcessingOptions.RehydrateIdList(
                    selectedRequest,
                    ct,
                    UserRolesFromContext.Roles(HttpContext.Items),
                    log,
                    UserIdFromContext.Id(HttpContext.Items),
                    UserTranslationIdFromContext.Id(HttpContext.Items));

            var JobName = $"LT:BatchJob LT:Remove LT:Tag \"{tag}\" LT:{selectedRequest.SockType} ({selectedRequest.SelectedRowIds.LongLength}) LT:User {UserNameFromContext.Name(HttpContext.Items)}";
            JObject o = JObject.FromObject(new
            {
                idList = selectedRequest.SelectedRowIds,
                tag = tag
            });

            OpsJob j = new OpsJob();
            j.Name = JobName;
            j.SockType = selectedRequest.SockType;
            j.JobType = JobType.BatchCoreObjectOperation;
            j.SubType = JobSubType.TagRemove;
            j.Exclusive = false;
            j.JobInfo = o.ToString();
            await JobsBiz.AddJobAsync(j);
            await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserIdFromContext.Id(HttpContext.Items), 0, SockType.ServerJob, SockEvent.Created, JobName), ct);
            return Accepted(new { JobId = j.GId });
        }


        /// <summary>
        /// Batch remove tags to all objects of type specified
        /// </summary>
        /// <param name="sockType"></param>
        /// <param name="tag"></param>        
        /// <returns>Job Id</returns>
        [HttpPost("batch-remove-any/{sockType}/{tag}")]
        public async Task<IActionResult> BatchRemoveAny([FromRoute] SockType sockType, [FromRoute] string tag)
        {
            if (!serverState.IsOpen)
                return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
            if (!ModelState.IsValid)
                return BadRequest(new ApiErrorResponse(ModelState));
            if (!sockType.HasAttribute(typeof(CoreBizObjectAttribute)))
                return BadRequest(new ApiErrorResponse(ApiErrorCode.INVALID_OPERATION, null, "Not a taggable object type"));
            if (!Authorized.HasModifyRole(HttpContext.Items, sockType))
                return StatusCode(403, new ApiNotAuthorizedResponse());

            tag = TagBiz.NormalizeTag(tag);
            if (string.IsNullOrWhiteSpace(tag))
                return BadRequest(new ApiErrorResponse(ApiErrorCode.VALIDATION_REQUIRED, null, "tag"));

            var JobName = $"LT:BatchJob LT:Remove LT:Tag \"{tag}\" LT:{sockType.ToString()} (LT:GridRowFilterDropDownAllItem) LT:User {UserNameFromContext.Name(HttpContext.Items)}";
            JObject o = JObject.FromObject(new
            {
                tag = tag
            });

            OpsJob j = new OpsJob();
            j.Name = JobName;
            j.SockType = sockType;
            j.SubType = JobSubType.TagRemoveAny;
            j.JobType = JobType.BatchCoreObjectOperation;
            j.Exclusive = false;
            j.JobInfo = o.ToString();
            await JobsBiz.AddJobAsync(j);
            await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserIdFromContext.Id(HttpContext.Items), 0, SockType.ServerJob, SockEvent.Created, JobName), ct);
            return Accepted(new { JobId = j.GId });
        }


        /// <summary>
        /// Batch replace tags to list of object id's specified
        /// </summary>        
        /// <param name="fromTag"></param>
        /// <param name="toTag"></param>
        /// <param name="selectedRequest"></param>
        /// <returns>Job Id</returns>
        [HttpPost("batch-replace/{fromTag}")]
        public async Task<IActionResult> BatchReplace([FromRoute] string fromTag, [FromQuery] string toTag, [FromBody] DataListSelectedRequest selectedRequest)
        {
            if (!serverState.IsOpen)
                return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
            if (!ModelState.IsValid)
                return BadRequest(new ApiErrorResponse(ModelState));

            if (!selectedRequest.SockType.HasAttribute(typeof(CoreBizObjectAttribute)))
                return BadRequest(new ApiErrorResponse(ApiErrorCode.INVALID_OPERATION, null, "Not a taggable object type"));
            if (!Authorized.HasModifyRole(HttpContext.Items, selectedRequest.SockType))
                return StatusCode(403, new ApiNotAuthorizedResponse());
            fromTag = TagBiz.NormalizeTag(fromTag);
            if (string.IsNullOrWhiteSpace(fromTag))
                return BadRequest(new ApiErrorResponse(ApiErrorCode.VALIDATION_REQUIRED, null, "fromTag"));
            toTag = TagBiz.NormalizeTag(toTag);
            if (string.IsNullOrWhiteSpace(toTag))
                return BadRequest(new ApiErrorResponse(ApiErrorCode.VALIDATION_REQUIRED, null, "toTag"));

            //Rehydrate id list if necessary
            if (selectedRequest.SelectedRowIds.Length == 0)
                selectedRequest.SelectedRowIds = await DataListSelectedProcessingOptions.RehydrateIdList(
                    selectedRequest,
                    ct,
                    UserRolesFromContext.Roles(HttpContext.Items),
                    log,
                    UserIdFromContext.Id(HttpContext.Items),
                    UserTranslationIdFromContext.Id(HttpContext.Items));

            var JobName = $"LT:BatchJob LT:Replace LT:Tag \"{fromTag}\" -> LT:Tag \"{toTag}\" LT:{selectedRequest.SockType} ({selectedRequest.SelectedRowIds.LongLength}) LT:User {UserNameFromContext.Name(HttpContext.Items)}";
            JObject o = JObject.FromObject(new
            {
                idList = selectedRequest.SelectedRowIds,
                tag = fromTag,
                toTag = toTag
            });

            OpsJob j = new OpsJob();
            j.SockType = selectedRequest.SockType;
            j.Name = JobName;
            j.JobType = JobType.BatchCoreObjectOperation;
            j.SubType = JobSubType.TagReplace;
            j.Exclusive = false;
            j.JobInfo = o.ToString();
            await JobsBiz.AddJobAsync(j);
            await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserIdFromContext.Id(HttpContext.Items), 0, SockType.ServerJob, SockEvent.Created, JobName), ct);
            return Accepted(new { JobId = j.GId });
        }

        /// <summary>
        /// Batch replace tags to all objects of type specified
        /// </summary>
        /// <param name="sockType"></param>
        /// <param name="fromTag"></param> 
        /// <param name="toTag"></param>       
        /// <returns>Job Id</returns>
        [HttpPost("batch-replace-any/{sockType}/{fromTag}")]
        public async Task<IActionResult> BatchReplaceAny([FromRoute] SockType sockType, [FromRoute] string fromTag, [FromQuery] string toTag)
        {
            if (!serverState.IsOpen)
                return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
            if (!ModelState.IsValid)
                return BadRequest(new ApiErrorResponse(ModelState));
            if (!sockType.HasAttribute(typeof(CoreBizObjectAttribute)))
                return BadRequest(new ApiErrorResponse(ApiErrorCode.INVALID_OPERATION, null, "Not a taggable object type"));
            if (!Authorized.HasModifyRole(HttpContext.Items, sockType))
                return StatusCode(403, new ApiNotAuthorizedResponse());

            fromTag = TagBiz.NormalizeTag(fromTag);
            if (string.IsNullOrWhiteSpace(fromTag))
                return BadRequest(new ApiErrorResponse(ApiErrorCode.VALIDATION_REQUIRED, null, "fromTag"));

            toTag = TagBiz.NormalizeTag(toTag);
            if (string.IsNullOrWhiteSpace(toTag))
                return BadRequest(new ApiErrorResponse(ApiErrorCode.VALIDATION_REQUIRED, null, "toTag"));

            var JobName = $"LT:BatchJob LT:Replace LT:Tag \"{fromTag}\" -> LT:Tag \"{toTag}\" LT:{sockType.ToString()} (LT:GridRowFilterDropDownAllItem) LT:User {UserNameFromContext.Name(HttpContext.Items)}";
            JObject o = JObject.FromObject(new
            {
                tag = fromTag,
                toTag = toTag
            });

            OpsJob j = new OpsJob();
            j.Name = JobName;
            j.SockType = sockType;
            j.JobType = JobType.BatchCoreObjectOperation;
            j.SubType = JobSubType.TagReplaceAny;
            j.Exclusive = false;
            j.JobInfo = o.ToString();
            await JobsBiz.AddJobAsync(j);
            await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserIdFromContext.Id(HttpContext.Items), 0, SockType.ServerJob, SockEvent.Created, JobName), ct);
            return Accepted(new { JobId = j.GId });
        }


    }//eoc
}//ens