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



namespace AyaNova.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)));
        }




        /////////////////////////////////////////////////////////////
        //BULK OPS
        //
        //

        /// <summary>
        /// Bulk add tags to list of object id's specified
        /// </summary>
        /// <param name="ayaType"></param>
        /// <param name="tag"></param>
        /// <param name="idList"></param>
        /// <returns>Job Id</returns>
        [HttpPost("bulk-add/{ayaType}/{tag}")]
        public async Task<IActionResult> BulkAdd([FromRoute] AyaType ayaType, [FromRoute] string tag, [FromBody] List<long> idList)
        {
            if (!serverState.IsOpen)
                return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
            if (!ModelState.IsValid)
                return BadRequest(new ApiErrorResponse(ModelState));
            if (!ayaType.HasAttribute(typeof(CoreBizObjectAttribute)))
                return BadRequest(new ApiErrorResponse(ApiErrorCode.INVALID_OPERATION, null, "Not a taggable object type"));
            if (!Authorized.HasModifyRole(HttpContext.Items, ayaType))
                return StatusCode(403, new ApiNotAuthorizedResponse());
            if (idList.Count == 0)
                return BadRequest(new ApiErrorResponse(ApiErrorCode.VALIDATION_REQUIRED, null, "List of ids are required"));
            tag = TagBiz.NormalizeTag(tag);
            if (string.IsNullOrWhiteSpace(tag))
                return BadRequest(new ApiErrorResponse(ApiErrorCode.VALIDATION_REQUIRED, null, "tag required"));

            var JobName = $"Bulk operation: Add tag \"{tag}\" on {ayaType} ({idList.Count} specified)";
            JObject o = JObject.FromObject(new
            {
                idList = idList,
                tag = tag
            });

            OpsJob j = new OpsJob();
            j.Name = JobName;
            j.ObjectType = ayaType;
            j.JobType = JobType.BulkCoreBizObjectOperation;
            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, AyaType.ServerJob, AyaEvent.Created, JobName), ct);
            return Accepted(new { JobId = j.GId });
        }

        /// <summary>
        /// Bulk add tags to all objects of type specified
        /// </summary>
        /// <param name="ayaType"></param>
        /// <param name="tag"></param>        
        /// <returns>Job Id</returns>
        [HttpPost("bulk-add-any/{ayaType}/{tag}")]
        public async Task<IActionResult> BulkAddAny([FromRoute] AyaType ayaType, [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 (!ayaType.HasAttribute(typeof(CoreBizObjectAttribute)))
                return BadRequest(new ApiErrorResponse(ApiErrorCode.INVALID_OPERATION, null, "Not a taggable object type"));
            if (!Authorized.HasModifyRole(HttpContext.Items, ayaType))
                return StatusCode(403, new ApiNotAuthorizedResponse());

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

            var JobName = $"Bulk operation: Add tag \"{tag}\" on any {ayaType}";
            JObject o = JObject.FromObject(new
            {
                tag = tag
            });

            OpsJob j = new OpsJob();
            j.Name = JobName;
            j.ObjectType = ayaType;
            j.JobType = JobType.BulkCoreBizObjectOperation;
            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, AyaType.ServerJob, AyaEvent.Created, JobName), ct);
            return Accepted(new { JobId = j.GId });
        }

        /// <summary>
        /// Bulk remove tags to list of object id's specified
        /// </summary>
        /// <param name="ayaType"></param>
        /// <param name="tag"></param>
        /// <param name="idList"></param>
        /// <returns>Job Id</returns>
        [HttpPost("bulk-remove/{ayaType}/{tag}")]
        public async Task<IActionResult> BulkRemove([FromRoute] AyaType ayaType, [FromRoute] string tag, [FromBody] List<long> idList)
        {
            if (!serverState.IsOpen)
                return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
            if (!ModelState.IsValid)
                return BadRequest(new ApiErrorResponse(ModelState));
            if (!ayaType.HasAttribute(typeof(CoreBizObjectAttribute)))
                return BadRequest(new ApiErrorResponse(ApiErrorCode.INVALID_OPERATION, null, "Not a taggable object type"));
            if (!Authorized.HasModifyRole(HttpContext.Items, ayaType))
                return StatusCode(403, new ApiNotAuthorizedResponse());
            if (idList.Count == 0)
                return BadRequest(new ApiErrorResponse(ApiErrorCode.VALIDATION_REQUIRED, null, "List of ids"));
            tag = TagBiz.NormalizeTag(tag);
            if (string.IsNullOrWhiteSpace(tag))
                return BadRequest(new ApiErrorResponse(ApiErrorCode.VALIDATION_REQUIRED, null, "tag"));

            var JobName = $"Bulk operation: Remove tag \"{tag}\" from {ayaType} ({idList.Count} specified)";
            JObject o = JObject.FromObject(new
            {
                idList = idList,
                tag = tag
            });

            OpsJob j = new OpsJob();
            j.Name = JobName;
            j.ObjectType = ayaType;
            j.JobType = JobType.BulkCoreBizObjectOperation;
            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, AyaType.ServerJob, AyaEvent.Created, JobName), ct);
            return Accepted(new { JobId = j.GId });
        }


        /// <summary>
        /// Bulk remove tags to all objects of type specified
        /// </summary>
        /// <param name="ayaType"></param>
        /// <param name="tag"></param>        
        /// <returns>Job Id</returns>
        [HttpPost("bulk-remove-any/{ayaType}/{tag}")]
        public async Task<IActionResult> BulkRemoveAny([FromRoute] AyaType ayaType, [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 (!ayaType.HasAttribute(typeof(CoreBizObjectAttribute)))
                return BadRequest(new ApiErrorResponse(ApiErrorCode.INVALID_OPERATION, null, "Not a taggable object type"));
            if (!Authorized.HasModifyRole(HttpContext.Items, ayaType))
                return StatusCode(403, new ApiNotAuthorizedResponse());

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

            var JobName = $"Bulk operation: Remove tag \"{tag}\" from any {ayaType}";
            JObject o = JObject.FromObject(new
            {
                tag = tag
            });

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


        /// <summary>
        /// Bulk replace tags to list of object id's specified
        /// </summary>
        /// <param name="ayaType"></param>
        /// <param name="fromTag"></param>
        /// <param name="toTag"></param>
        /// <param name="idList"></param>
        /// <returns>Job Id</returns>
        [HttpPost("bulk-replace/{ayaType}/{fromTag}")]
        public async Task<IActionResult> BulkReplace([FromRoute] AyaType ayaType, [FromRoute] string fromTag, [FromQuery] string toTag, [FromBody] List<long> idList)
        {
            if (!serverState.IsOpen)
                return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
            if (!ModelState.IsValid)
                return BadRequest(new ApiErrorResponse(ModelState));
            if (!ayaType.HasAttribute(typeof(CoreBizObjectAttribute)))
                return BadRequest(new ApiErrorResponse(ApiErrorCode.INVALID_OPERATION, null, "Not a taggable object type"));
            if (!Authorized.HasModifyRole(HttpContext.Items, ayaType))
                return StatusCode(403, new ApiNotAuthorizedResponse());
            if (idList.Count == 0)
                return BadRequest(new ApiErrorResponse(ApiErrorCode.VALIDATION_REQUIRED, null, "List of ids"));
            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 = $"Bulk operation: Replace tag \"{fromTag}\" with tag \"{toTag}\" on {ayaType} ({idList.Count} specified)";
            JObject o = JObject.FromObject(new
            {
                idList = idList,
                tag = fromTag,
                toTag = toTag
            });

            OpsJob j = new OpsJob();
            j.ObjectType = ayaType;
            j.Name = JobName;
            j.JobType = JobType.BulkCoreBizObjectOperation;
            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, AyaType.ServerJob, AyaEvent.Created, JobName), ct);
            return Accepted(new { JobId = j.GId });
        }

        /// <summary>
        /// Bulk replace tags to all objects of type specified
        /// </summary>
        /// <param name="ayaType"></param>
        /// <param name="fromTag"></param> 
        /// <param name="toTag"></param>       
        /// <returns>Job Id</returns>
        [HttpPost("bulk-replace-any/{ayaType}/{fromTag}")]
        public async Task<IActionResult> BulkReplaceAny([FromRoute] AyaType ayaType, [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 (!ayaType.HasAttribute(typeof(CoreBizObjectAttribute)))
                return BadRequest(new ApiErrorResponse(ApiErrorCode.INVALID_OPERATION, null, "Not a taggable object type"));
            if (!Authorized.HasModifyRole(HttpContext.Items, ayaType))
                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 = $"Bulk operation: Replace tag \"{fromTag}\" with tag \"{toTag}\" on any {ayaType}";
            JObject o = JObject.FromObject(new
            {
                tag = fromTag,
                toTag = toTag
            });

            OpsJob j = new OpsJob();
            j.Name = JobName;
            j.ObjectType = ayaType;
            j.JobType = JobType.BulkCoreBizObjectOperation;
            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, AyaType.ServerJob, AyaEvent.Created, JobName), ct);
            return Accepted(new { JobId = j.GId });
        }


    }//eoc
}//ens