using foodmarket.Application.Common;
using foodmarket.Infrastructure.Persistence;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace foodmarket.Api.Controllers.Inventory;

[ApiController]
[Authorize]
[Route("api/inventory")]
public class StockController : ControllerBase
{
    private readonly AppDbContext _db;
    public StockController(AppDbContext db) => _db = db;

    public record StockRow(
        Guid ProductId, string ProductName, string? Article, string UnitSymbol,
        Guid StoreId, string StoreName,
        decimal Quantity, decimal ReservedQuantity, decimal Available);

    [HttpGet("stock")]
    public async Task<ActionResult<PagedResult<StockRow>>> GetStock(
        [FromQuery] Guid? storeId,
        [FromQuery] Guid? productId,
        [FromQuery] string? search,
        [FromQuery] bool includeZero = false,
        [FromQuery] int page = 1,
        [FromQuery] int pageSize = 50,
        [FromQuery] string? sort = null,
        [FromQuery] string? order = null,
        CancellationToken ct = default)
    {
        var q = from s in _db.Stocks
                join p in _db.Products on s.ProductId equals p.Id
                join u in _db.UnitsOfMeasure on p.UnitOfMeasureId equals u.Id
                join st in _db.Stores on s.StoreId equals st.Id
                select new { s, p, u, st };

        if (storeId.HasValue) q = q.Where(x => x.s.StoreId == storeId.Value);
        if (productId.HasValue) q = q.Where(x => x.s.ProductId == productId.Value);
        if (!includeZero) q = q.Where(x => x.s.Quantity != 0);
        if (!string.IsNullOrWhiteSpace(search))
        {
            var term = $"%{search.Trim()}%";
            q = q.Where(x => EF.Functions.ILike(x.p.Name, term)
                          || (x.p.Article != null && EF.Functions.ILike(x.p.Article, term)));
        }

        var total = await q.CountAsync(ct);
        var desc = string.Equals(order, "desc", StringComparison.OrdinalIgnoreCase);
        q = (sort, desc) switch
        {
            ("article", false) => q.OrderBy(x => x.p.Article).ThenBy(x => x.p.Name),
            ("article", true) => q.OrderByDescending(x => x.p.Article).ThenBy(x => x.p.Name),
            ("unit", false) => q.OrderBy(x => x.u.Name).ThenBy(x => x.p.Name),
            ("unit", true) => q.OrderByDescending(x => x.u.Name).ThenBy(x => x.p.Name),
            ("store", false) => q.OrderBy(x => x.st.Name).ThenBy(x => x.p.Name),
            ("store", true) => q.OrderByDescending(x => x.st.Name).ThenBy(x => x.p.Name),
            ("quantity", false) => q.OrderBy(x => x.s.Quantity).ThenBy(x => x.p.Name),
            ("quantity", true) => q.OrderByDescending(x => x.s.Quantity).ThenBy(x => x.p.Name),
            ("reserved", false) => q.OrderBy(x => x.s.ReservedQuantity).ThenBy(x => x.p.Name),
            ("reserved", true) => q.OrderByDescending(x => x.s.ReservedQuantity).ThenBy(x => x.p.Name),
            ("available", false) => q.OrderBy(x => x.s.Quantity - x.s.ReservedQuantity).ThenBy(x => x.p.Name),
            ("available", true) => q.OrderByDescending(x => x.s.Quantity - x.s.ReservedQuantity).ThenBy(x => x.p.Name),
            ("name", true) => q.OrderByDescending(x => x.p.Name),
            _ => q.OrderBy(x => x.p.Name),
        };
        var items = await q
            .Skip((page - 1) * pageSize).Take(pageSize)
            .Select(x => new StockRow(
                x.p.Id, x.p.Name, x.p.Article, x.u.Name,
                x.st.Id, x.st.Name,
                x.s.Quantity, x.s.ReservedQuantity, x.s.Quantity - x.s.ReservedQuantity))
            .ToListAsync(ct);

        return new PagedResult<StockRow> { Items = items, Total = total, Page = page, PageSize = pageSize };
    }

    public record MovementRow(
        Guid Id, DateTime OccurredAt,
        Guid ProductId, string ProductName, string? Article,
        Guid StoreId, string StoreName,
        decimal Quantity, decimal? UnitCost,
        string Type, string DocumentType, Guid? DocumentId, string? DocumentNumber,
        string? Notes);

    [HttpGet("movements")]
    public async Task<ActionResult<PagedResult<MovementRow>>> GetMovements(
        [FromQuery] Guid? storeId,
        [FromQuery] Guid? productId,
        [FromQuery] DateTime? from,
        [FromQuery] DateTime? to,
        [FromQuery] int page = 1,
        [FromQuery] int pageSize = 50,
        [FromQuery] string? sort = null,
        [FromQuery] string? order = null,
        CancellationToken ct = default)
    {
        var q = from m in _db.StockMovements
                join p in _db.Products on m.ProductId equals p.Id
                join st in _db.Stores on m.StoreId equals st.Id
                select new { m, p, st };

        if (storeId.HasValue) q = q.Where(x => x.m.StoreId == storeId.Value);
        if (productId.HasValue) q = q.Where(x => x.m.ProductId == productId.Value);
        if (from.HasValue) q = q.Where(x => x.m.OccurredAt >= from.Value);
        if (to.HasValue) q = q.Where(x => x.m.OccurredAt < to.Value);

        var total = await q.CountAsync(ct);
        var desc = string.Equals(order, "desc", StringComparison.OrdinalIgnoreCase);
        q = (sort, desc) switch
        {
            ("occurredAt", false) => q.OrderBy(x => x.m.OccurredAt),
            ("occurredAt", true) => q.OrderByDescending(x => x.m.OccurredAt),
            ("product", false) => q.OrderBy(x => x.p.Name),
            ("product", true) => q.OrderByDescending(x => x.p.Name),
            ("store", false) => q.OrderBy(x => x.st.Name).ThenByDescending(x => x.m.OccurredAt),
            ("store", true) => q.OrderByDescending(x => x.st.Name).ThenByDescending(x => x.m.OccurredAt),
            ("quantity", false) => q.OrderBy(x => x.m.Quantity).ThenByDescending(x => x.m.OccurredAt),
            ("quantity", true) => q.OrderByDescending(x => x.m.Quantity).ThenByDescending(x => x.m.OccurredAt),
            ("type", false) => q.OrderBy(x => x.m.Type).ThenByDescending(x => x.m.OccurredAt),
            ("type", true) => q.OrderByDescending(x => x.m.Type).ThenByDescending(x => x.m.OccurredAt),
            ("documentType", false) => q.OrderBy(x => x.m.DocumentType).ThenByDescending(x => x.m.OccurredAt),
            ("documentType", true) => q.OrderByDescending(x => x.m.DocumentType).ThenByDescending(x => x.m.OccurredAt),
            _ => q.OrderByDescending(x => x.m.OccurredAt),
        };
        var items = await q
            .Skip((page - 1) * pageSize).Take(pageSize)
            .Select(x => new MovementRow(
                x.m.Id, x.m.OccurredAt,
                x.p.Id, x.p.Name, x.p.Article,
                x.st.Id, x.st.Name,
                x.m.Quantity, x.m.UnitCost,
                x.m.Type.ToString(), x.m.DocumentType, x.m.DocumentId, x.m.DocumentNumber,
                x.m.Notes))
            .ToListAsync(ct);

        return new PagedResult<MovementRow> { Items = items, Total = total, Page = page, PageSize = pageSize };
    }
}
