using MassTransit;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using OpenWarehouse.warehouse.api.Common.ItemTagManager;
using OpenWarehouse.warehouse.api.Common.Kafka;
using OpenWarehouse.warehouse.api.Common.Kafka.TagTree;
using OpenWarehouse.warehouse.api.Common.Producer;
using OpenWarehouse.warehouse.api.Common.TaskManager;
using OpenWarehouse.warehouse.api.Model.Item;
using ActionResult = Microsoft.AspNetCore.Mvc.ActionResult;
using ItemCategoryTag = OpenWarehouse.warehouse.api.Data.ItemCategoryTag;

namespace OpenWarehouse.warehouse.api.Controller;

[ApiController]
[Route("[controller]/[action]")]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class Item(IItemManager itemManager, ILogger<Item> logger,
   ITopicProducer<TagTreeKafkaMessage> publishTagTreeEndpoint, ITaskManager taskManager,
   IItemTagManager itemTagManager, IProducerManager producerManager) : ControllerBase
{
    [HttpGet("{id}")]
    public async Task<ActionResult<ItemModel>> GetItemById([FromRoute] string id)
    {
        logger.LogInformation("GetItemById called for item id {id}", id);
        var item = await itemManager.FindItemByIdAsync(id);
        logger.LogDebug("Item retrieved: {@Item}", item);

        if (item == null)
        {
            logger.LogWarning("Item with ID:{id} not found", id);
            return NotFound();
        }
        var result = new ItemModel()
        {
            Id = item.Id.ToString(),
            Name = item.Name,
            Claims = item.ItemClaims?.ToDictionary(
                q => q.ClaimType ?? $"Unknown type, {Guid.NewGuid()} ",
                q => new ClaimValueModel()
                {
                    Issuer = q.ClaimIssuer,
                    Value = q.ClaimValue,
                }),
            ProducerId = item.ProducerId.ToString(),
            ProducerName = item.Producer?.Name,
        };
        logger.LogDebug("Returning item model: {@Result}", result);
        return Ok(result);
    }
    
    [HttpPost]
    public async Task<ActionResult<CreateItemRespond>> CreateItem([FromBody]CreateItemRequest request)
    {
        CreateItemRespond respond = new CreateItemRespond();
        
        bool ready = true;
        if (request is { ProducerName: null, ItemName: null}) return BadRequest();

        var producer = await producerManager.FindProducerByName(request.ProducerName ?? "");

        if (producer == null)
        {
            ready = false;
            respond.Errors["Producer"] = $"Producer {request.ProducerName} is not found";
        }
        
        var listOfTags = new List<ItemCategoryTag>();

        foreach (var itemTag in request.Tags)
        {
            var tag = await itemTagManager.GetTagByName(itemTag);


            if (tag == null)
            {
                ready = false;
                respond.Errors[$"Tag:{itemTag}"] = "Not found";
                continue;
            }
            
            listOfTags.Add(tag);
        }

        if (!ready)
        {
            return BadRequest(respond);
        }

        var item = new Data.Item()
        {
            Name = request.ItemName,
            ItemCategoryTags = listOfTags,
            Producer = producer,
        };

        var res = await itemManager.CreateAsync(item);
        
        return res.IsSuccess ? Ok(item) : BadRequest(respond);
    }

    [HttpPost]
    public async Task<ActionResult> CreateTagTree([FromBody] CreateTagTreeRequest request)
    {
        Guid id = Guid.NewGuid();
        logger.LogInformation("Request with id:{id} to create tag tree received", id);
        if (request.ItemCategoryTagsList == null || request.ItemCategoryTagsList.Count == 0)
        {
            logger.LogWarning("Request with id:{id} cannot be accepted ", id);
            return BadRequest("List cannot be empty!");
        }
        logger.LogInformation("Request with id:{id} to create item tag tree accepted", id);

        var actor = (HttpContext.User.Identity as CaseSensitiveClaimsIdentity)? 
            .Claims.Where(q => q.Type == ClaimTypes.NameIdentifier)
            .Select(q => q.Value).FirstOrDefault();
        logger.LogDebug("Actor identified: {Actor}", actor);

        await taskManager.CreateTask(id, actor);
        logger.LogDebug("Task created with ID {TaskId}", id);

        var kafkaMess = new TagTreeKafkaMessage()
        {
            Guid = id,
            ItemCategoryTagsList = request.ItemCategoryTagsList,
            Action = Actions.Create
        };
        logger.LogDebug("Publishing Kafka message: {@KafkaMessage}", kafkaMess);
        
        _ = publishTagTreeEndpoint.Produce(kafkaMess);
        
        return Accepted($"/status/{id}");
    }

    [HttpPost]
    public async Task<ActionResult> DeleteTagList([FromBody] CreateTagTreeRequest request)
    {
        Guid id = Guid.NewGuid();
        logger.LogInformation("Request with id:{id} to delete tag list received", id);
        
        if (request.ItemCategoryTagsList == null || request.ItemCategoryTagsList.Count == 0)
        {
            logger.LogWarning("Request with id:{id} cannot be accepted ", id);
            return BadRequest("List cannot be empty!");
        }
        
        logger.LogInformation("Request with id:{id} to delete tag list accepted", id);
        
        var actor = (HttpContext.User.Identity as CaseSensitiveClaimsIdentity)? 
            .Claims.Where(q => q.Type == ClaimTypes.NameIdentifier)
            .Select(q => q.Value).FirstOrDefault();
        logger.LogDebug("Actor identified: {Actor}", actor);
        
        await taskManager.CreateTask(id, actor);
        logger.LogDebug("Task created with ID {TaskId}", id);
        
        var kafkaMess = new TagTreeKafkaMessage()
        {
            Guid = id,
            ItemCategoryTagsList = request.ItemCategoryTagsList,
            Action = Actions.Delete
        };
        logger.LogDebug("Publishing Kafka message: {@KafkaMessage}", kafkaMess);
        
        _ = publishTagTreeEndpoint.Produce(kafkaMess);
        
        return Accepted($"/status/{id}");
    }

    [HttpGet("{root}")]
    public async Task<ActionResult<TagTreeModel>> GetTagTree([FromRoute]string root)
    {
        logger.LogInformation("GetTagTree called for root {root}", root);

        var result = await itemTagManager.GetTagTree(root);
        logger.LogDebug("Tag tree retrieved for root {Root}: {@Result}", root, result);

        if (result == null)
        {
            logger.LogWarning("No tag tree found for root {Root}", root);
            return NotFound();
        }
        
        var returnResult = GetChild(result);
        logger.LogDebug("Returning tag tree model: {@ReturnResult}", returnResult);

        return Ok(returnResult);
    }

    [HttpPost]
    public async Task<ActionResult<int>> UpdateItemClaims([FromBody]UpdateItemClaimRequest request)
    {
        
        if (request.ItemId == null || request.ListOfUpdatedClaims.Count == 0) return BadRequest();
        int result = 0;
        
        var item = await itemManager.FindItemByIdAsync(request.ItemId);
        if (item == null) return NotFound();

        foreach (var claim in request.ListOfUpdatedClaims)
        {
            if (claim.ClaimType == null) continue;

            if (claim.ClaimValue == null)
            {
                var resultLocal = await itemManager.DeleteClaim(item, claim.ClaimType);
                
                if (resultLocal.IsSuccess)
                {
                    result++;
                }
                else
                {
                    logger.LogWarning("Failed to delete claim: {ClaimType}", claim.ClaimType);
                }
            }
            else
            {
                var resultLocal = await itemManager.UpdateClaim(item, new Claim(claim.ClaimType, claim.ClaimValue, claim.ClaimIssuer));
                
                if (resultLocal.IsSuccess)
                {
                    result++;
                }
                else
                {
                    logger.LogWarning("Failed to update claim: {ClaimType}", claim.ClaimType);
                }
            }
        }
        
        return result> 0 ? Ok(result) : BadRequest();
    }
    
    private TagTreeModel GetChild(ItemCategoryTag model)
    {
        logger.LogDebug("Building child model for tag {TagName}", model.Name);
        var result = new TagTreeModel(){Name = model.Name};
        if (model.ListOfChildrenTag != null)
            foreach (var element in model.ListOfChildrenTag)
            {
                logger.LogDebug("Processing child tag {ChildTagName}", element.Name);
                result.Child.Add(GetChild(element));
            }

        return result;
    }
}
