using InventoryApi.Models;
using InventoryApi.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Security.Claims;

namespace InventoryApi.Controllers;

[ApiController]
[Route("api/[controller]")]
public class InventoryController : ControllerBase
{
    private readonly InventoryStore _inventory;

    public InventoryController(InventoryStore inventory)
    {
        _inventory = inventory;
    }

    [HttpPost("internal/by-owner/{ownerType}")]
    public async Task<IActionResult> GetByOwnersInternal(string ownerType, [FromBody] InternalOwnerInventoryBatchRequest req)
    {
        var configuredKey = (HttpContext.RequestServices.GetRequiredService<IConfiguration>()["InternalApi:Key"]
            ?? HttpContext.RequestServices.GetRequiredService<IConfiguration>()["Jwt:Key"]
            ?? string.Empty).Trim();
        var requestKey = (Request.Headers["X-Internal-Api-Key"].FirstOrDefault() ?? string.Empty).Trim();
        if (string.IsNullOrWhiteSpace(configuredKey) || !string.Equals(configuredKey, requestKey, StringComparison.Ordinal))
            return Unauthorized();

        var normalizedOwnerType = ownerType.Trim().ToLowerInvariant();
        if (normalizedOwnerType is not ("character" or "location"))
            return BadRequest("Unsupported ownerType");

        var groupedItems = await _inventory.GetByOwnersAsync(normalizedOwnerType, req.OwnerIds);
        var ownerIds = req.OwnerIds
            .Where(id => !string.IsNullOrWhiteSpace(id))
            .Select(id => id.Trim())
            .Distinct(StringComparer.Ordinal)
            .ToList();

        var response = ownerIds.Select(ownerId => new OwnerInventorySummaryResponse
        {
            OwnerType = normalizedOwnerType,
            OwnerId = ownerId,
            Items = groupedItems.GetValueOrDefault(ownerId, []).Select(InventoryItemResponse.FromModel).ToList()
        }).ToList();

        return Ok(response);
    }

    [HttpGet("item-definitions")]
    [Authorize(Roles = "USER,SUPER")]
    public async Task<IActionResult> ListItemDefinitions()
    {
        var definitions = await _inventory.ListItemDefinitionsAsync();
        return Ok(definitions.Select(ItemDefinitionResponse.FromModel).ToList());
    }

    [HttpGet("item-definitions/{itemKey}")]
    [Authorize(Roles = "USER,SUPER")]
    public async Task<IActionResult> GetItemDefinition(string itemKey)
    {
        var definition = await _inventory.GetItemDefinitionAsync(itemKey);
        if (definition is null)
            return NotFound();

        return Ok(ItemDefinitionResponse.FromModel(definition));
    }

    [HttpPost("item-definitions")]
    [Authorize(Roles = "SUPER")]
    public async Task<IActionResult> CreateItemDefinition([FromBody] CreateItemDefinitionRequest req)
    {
        if (string.IsNullOrWhiteSpace(req.ItemKey))
            return BadRequest("itemKey required");
        if (string.IsNullOrWhiteSpace(req.DisplayName))
            return BadRequest("displayName required");
        if (req.MaxStackSize <= 0)
            return BadRequest("maxStackSize must be greater than 0");
        if (!req.Stackable && req.MaxStackSize != 1)
            return BadRequest("Non-stackable items must have maxStackSize of 1");

        var created = await _inventory.CreateItemDefinitionAsync(req);
        if (created is null)
            return Conflict("Item definition already exists");

        return Ok(ItemDefinitionResponse.FromModel(created));
    }

    [HttpPut("item-definitions/{itemKey}")]
    [Authorize(Roles = "SUPER")]
    public async Task<IActionResult> UpdateItemDefinition(string itemKey, [FromBody] UpdateItemDefinitionRequest req)
    {
        if (string.IsNullOrWhiteSpace(req.DisplayName))
            return BadRequest("displayName required");
        if (req.MaxStackSize <= 0)
            return BadRequest("maxStackSize must be greater than 0");
        if (!req.Stackable && req.MaxStackSize != 1)
            return BadRequest("Non-stackable items must have maxStackSize of 1");

        var updated = await _inventory.UpdateItemDefinitionAsync(itemKey, req);
        if (updated is null)
            return NotFound();

        return Ok(ItemDefinitionResponse.FromModel(updated));
    }

    [HttpGet("by-owner/{ownerType}/{ownerId}")]
    [Authorize(Roles = "USER,SUPER")]
    public async Task<IActionResult> GetByOwner(string ownerType, string ownerId)
    {
        var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
        if (string.IsNullOrWhiteSpace(userId))
            return Unauthorized();

        var access = await _inventory.ResolveOwnerAsync(ownerType, ownerId, userId, User.IsInRole("SUPER"));
        if (!access.IsSupported)
            return BadRequest("Unsupported ownerType");
        if (!access.Exists)
            return NotFound();
        if (!access.IsAuthorized)
            return Forbid();

        var items = await _inventory.GetByOwnerAsync(access.OwnerType, access.OwnerId);
        return Ok(new InventoryOwnerResponse
        {
            OwnerType = access.OwnerType,
            OwnerId = access.OwnerId,
            Items = items.Select(InventoryItemResponse.FromModel).ToList()
        });
    }

    [HttpPost("by-owner/{ownerType}/{ownerId}/grant")]
    [Authorize(Roles = "USER,SUPER")]
    public async Task<IActionResult> Grant(string ownerType, string ownerId, [FromBody] GrantInventoryItemRequest req)
    {
        if (string.IsNullOrWhiteSpace(req.ItemKey))
            return BadRequest("itemKey required");
        if (req.Quantity <= 0)
            return BadRequest("quantity must be greater than 0");

        var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
        if (string.IsNullOrWhiteSpace(userId))
            return Unauthorized();

        var access = await _inventory.ResolveOwnerAsync(ownerType, ownerId, userId, User.IsInRole("SUPER"));
        if (!access.IsSupported)
            return BadRequest("Unsupported ownerType");
        if (!access.Exists)
            return NotFound();
        if (!access.IsAuthorized)
            return Forbid();

        var definition = await _inventory.GetItemDefinitionAsync(req.ItemKey);
        if (definition is null)
            return BadRequest("Unknown itemKey");

        var grant = await _inventory.GrantAsync(access, req, definition);
        return Ok(new InventoryOwnerResponse
        {
            OwnerType = access.OwnerType,
            OwnerId = access.OwnerId,
            RequestedQuantity = grant.RequestedQuantity,
            GrantedQuantity = grant.GrantedQuantity,
            OverflowQuantity = grant.OverflowQuantity,
            Items = grant.Items.Select(InventoryItemResponse.FromModel).ToList()
        });
    }

    [HttpPost("by-owner/{ownerType}/{ownerId}/move")]
    [Authorize(Roles = "USER,SUPER")]
    public async Task<IActionResult> Move(string ownerType, string ownerId, [FromBody] MoveInventoryItemRequest req)
    {
        if (string.IsNullOrWhiteSpace(req.ItemId))
            return BadRequest("itemId required");
        if (req.ToSlot < 0)
            return BadRequest("toSlot must be >= 0");
        if (req.Quantity is <= 0)
            return BadRequest("quantity must be greater than 0");

        var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
        if (string.IsNullOrWhiteSpace(userId))
            return Unauthorized();

        var access = await _inventory.ResolveOwnerAsync(ownerType, ownerId, userId, User.IsInRole("SUPER"));
        if (!access.IsSupported)
            return BadRequest("Unsupported ownerType");
        if (!access.Exists)
            return NotFound();
        if (!access.IsAuthorized)
            return Forbid();

        var result = await _inventory.MoveAsync(access, req);
        return result.Status switch
        {
            InventoryMutationStatus.ItemNotFound => NotFound(),
            InventoryMutationStatus.Invalid => BadRequest("Invalid move"),
            InventoryMutationStatus.Conflict => Conflict("Target slot is not available"),
            _ => Ok(new InventoryOwnerResponse
            {
                OwnerType = access.OwnerType,
                OwnerId = access.OwnerId,
                Items = result.Items.Select(InventoryItemResponse.FromModel).ToList()
            })
        };
    }

    [HttpPost("transfer")]
    [Authorize(Roles = "USER,SUPER")]
    public async Task<IActionResult> Transfer([FromBody] TransferInventoryItemRequest req)
    {
        if (string.IsNullOrWhiteSpace(req.ItemId))
            return BadRequest("itemId required");
        if (string.IsNullOrWhiteSpace(req.FromOwnerType) || string.IsNullOrWhiteSpace(req.FromOwnerId))
            return BadRequest("from owner required");
        if (string.IsNullOrWhiteSpace(req.ToOwnerType) || string.IsNullOrWhiteSpace(req.ToOwnerId))
            return BadRequest("to owner required");
        if (req.ToSlot is < 0)
            return BadRequest("toSlot must be >= 0");
        if (req.Quantity is <= 0)
            return BadRequest("quantity must be greater than 0");

        var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
        if (string.IsNullOrWhiteSpace(userId))
            return Unauthorized();

        var fromAccess = await _inventory.ResolveOwnerAsync(req.FromOwnerType, req.FromOwnerId, userId, User.IsInRole("SUPER"));
        if (!fromAccess.IsSupported)
            return BadRequest("Unsupported fromOwnerType");
        if (!fromAccess.Exists)
            return NotFound("Source owner not found");
        if (!fromAccess.IsAuthorized)
            return Forbid();

        var toAccess = await _inventory.ResolveOwnerAsync(req.ToOwnerType, req.ToOwnerId, userId, User.IsInRole("SUPER"));
        if (!toAccess.IsSupported)
            return BadRequest("Unsupported toOwnerType");
        if (!toAccess.Exists)
            return NotFound("Target owner not found");
        if (!toAccess.IsAuthorized)
            return Forbid();

        var result = await _inventory.TransferAsync(fromAccess, toAccess, req);
        return result.Status switch
        {
            InventoryMutationStatus.ItemNotFound => NotFound(),
            InventoryMutationStatus.Invalid => BadRequest("Invalid transfer"),
            InventoryMutationStatus.Conflict => Conflict("Target slot is not available"),
            _ => Ok(new TransferInventoryResponse
            {
                MovedItemId = req.ItemId,
                FromOwnerType = fromAccess.OwnerType,
                FromOwnerId = fromAccess.OwnerId,
                ToOwnerType = toAccess.OwnerType,
                ToOwnerId = toAccess.OwnerId,
                FromItems = result.Items.Select(InventoryItemResponse.FromModel).Where(x => x.OwnerType == fromAccess.OwnerType && x.OwnerId == fromAccess.OwnerId).ToList(),
                ToItems = result.Items.Select(InventoryItemResponse.FromModel).Where(x => x.OwnerType == toAccess.OwnerType && x.OwnerId == toAccess.OwnerId).ToList()
            })
        };
    }

    [HttpPost("items/{itemId}/consume")]
    [Authorize(Roles = "USER,SUPER")]
    public async Task<IActionResult> Consume(string itemId, [FromBody] ConsumeInventoryItemRequest req)
    {
        if (req.Quantity <= 0)
            return BadRequest("quantity must be greater than 0");

        var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
        if (string.IsNullOrWhiteSpace(userId))
            return Unauthorized();

        var result = await _inventory.ConsumeAsync(itemId, req.Quantity, userId, User.IsInRole("SUPER"));
        return result.Status switch
        {
            InventoryMutationStatus.ItemNotFound => NotFound(),
            InventoryMutationStatus.Invalid => BadRequest("Invalid consume request"),
            InventoryMutationStatus.Conflict => Conflict(),
            _ => Ok(new InventoryOwnerResponse
            {
                OwnerType = result.OwnerType,
                OwnerId = result.OwnerId,
                Items = result.Items.Select(InventoryItemResponse.FromModel).ToList()
            })
        };
    }

    [HttpPost("items/{itemId}/equip")]
    [Authorize(Roles = "USER,SUPER")]
    public async Task<IActionResult> Equip(string itemId, [FromBody] EquipInventoryItemRequest req)
    {
        if (string.IsNullOrWhiteSpace(req.OwnerId))
            return BadRequest("ownerId required");
        if (string.IsNullOrWhiteSpace(req.EquipmentSlot))
            return BadRequest("equipmentSlot required");

        var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
        if (string.IsNullOrWhiteSpace(userId))
            return Unauthorized();

        var result = await _inventory.EquipAsync(itemId, req.OwnerId, req.EquipmentSlot, userId, User.IsInRole("SUPER"));
        return result.Status switch
        {
            InventoryMutationStatus.ItemNotFound => NotFound(),
            InventoryMutationStatus.Invalid => BadRequest("Invalid equip request"),
            InventoryMutationStatus.Conflict => Conflict("Equipment slot is not available"),
            _ => Ok(new InventoryOwnerResponse
            {
                OwnerType = result.OwnerType,
                OwnerId = result.OwnerId,
                Items = result.Items.Select(InventoryItemResponse.FromModel).ToList()
            })
        };
    }

    [HttpPost("items/{itemId}/unequip")]
    [Authorize(Roles = "USER,SUPER")]
    public async Task<IActionResult> Unequip(string itemId, [FromBody] UnequipInventoryItemRequest req)
    {
        if (string.IsNullOrWhiteSpace(req.OwnerId))
            return BadRequest("ownerId required");
        if (req.PreferredSlot is < 0)
            return BadRequest("preferredSlot must be >= 0");

        var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
        if (string.IsNullOrWhiteSpace(userId))
            return Unauthorized();

        var result = await _inventory.UnequipAsync(itemId, req.OwnerId, req.PreferredSlot, userId, User.IsInRole("SUPER"));
        return result.Status switch
        {
            InventoryMutationStatus.ItemNotFound => NotFound(),
            InventoryMutationStatus.Invalid => BadRequest("Invalid unequip request"),
            InventoryMutationStatus.Conflict => Conflict("Inventory slot is not available"),
            _ => Ok(new InventoryOwnerResponse
            {
                OwnerType = result.OwnerType,
                OwnerId = result.OwnerId,
                Items = result.Items.Select(InventoryItemResponse.FromModel).ToList()
            })
        };
    }
}
