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; } [HttpGet("by-owner/{ownerType}/{ownerId}")] [Authorize(Roles = "USER,SUPER")] public async Task 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 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 items = await _inventory.GrantAsync(access, req); return Ok(new InventoryOwnerResponse { OwnerType = access.OwnerType, OwnerId = access.OwnerId, Items = items.Select(InventoryItemResponse.FromModel).ToList() }); } [HttpPost("by-owner/{ownerType}/{ownerId}/move")] [Authorize(Roles = "USER,SUPER")] public async Task 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 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 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 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 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() }) }; } }