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

namespace foodmarket.Api.Controllers.Catalog;

[ApiController]
[Authorize]
[Route("api/catalog/product-groups")]
public class ProductGroupsController : ControllerBase
{
    private readonly AppDbContext _db;

    public ProductGroupsController(AppDbContext db) => _db = db;

    [HttpGet]
    public async Task<ActionResult<PagedResult<ProductGroupDto>>> List(
        [FromQuery] PagedRequest req,
        [FromQuery] Guid? parentId,
        CancellationToken ct)
    {
        var q = _db.ProductGroups.AsNoTracking().AsQueryable();
        if (parentId is not null)
        {
            q = parentId == Guid.Empty ? q.Where(g => g.ParentId == null) : q.Where(g => g.ParentId == parentId);
        }
        if (!string.IsNullOrWhiteSpace(req.Search))
        {
            var s = req.Search.Trim().ToLower();
            q = q.Where(g => g.Name.ToLower().Contains(s) || g.Path.ToLower().Contains(s));
        }
        var total = await q.CountAsync(ct);
        q = (req.Sort, req.Desc) switch
        {
            ("name", false) => q.OrderBy(g => g.Name),
            ("name", true) => q.OrderByDescending(g => g.Name),
            ("path", false) => q.OrderBy(g => g.Path),
            ("path", true) => q.OrderByDescending(g => g.Path),
            _ => q.OrderBy(g => g.Path).ThenBy(g => g.SortOrder).ThenBy(g => g.Name),
        };
        var items = await q
            .Skip(req.Skip).Take(req.Take)
            .Select(g => new ProductGroupDto(g.Id, g.Name, g.ParentId, g.Path, g.SortOrder, g.MarkupPercent, g.OrganizationId))
            .ToListAsync(ct);
        return new PagedResult<ProductGroupDto> { Items = items, Total = total, Page = req.Page, PageSize = req.Take };
    }

    [HttpGet("{id:guid}")]
    public async Task<ActionResult<ProductGroupDto>> Get(Guid id, CancellationToken ct)
    {
        var g = await _db.ProductGroups.AsNoTracking().FirstOrDefaultAsync(x => x.Id == id, ct);
        return g is null ? NotFound() : new ProductGroupDto(g.Id, g.Name, g.ParentId, g.Path, g.SortOrder, g.MarkupPercent, g.OrganizationId);
    }

    [HttpPost, Authorize(Roles = "Admin")]
    public async Task<ActionResult<ProductGroupDto>> Create([FromBody] ProductGroupInput input, CancellationToken ct)
    {
        var path = await BuildPathAsync(input.ParentId, input.Name, ct);
        var e = new ProductGroup
        {
            Name = input.Name, ParentId = input.ParentId, Path = path,
            SortOrder = input.SortOrder,
            MarkupPercent = input.MarkupPercent,
        };
        _db.ProductGroups.Add(e);
        await _db.SaveChangesAsync(ct);
        return CreatedAtAction(nameof(Get), new { id = e.Id },
            new ProductGroupDto(e.Id, e.Name, e.ParentId, e.Path, e.SortOrder, e.MarkupPercent, e.OrganizationId));
    }

    [HttpPut("{id:guid}"), Authorize(Roles = "Admin,SuperAdmin")]
    public async Task<IActionResult> Update(Guid id, [FromBody] ProductGroupInput input, CancellationToken ct)
    {
        var e = await _db.ProductGroups.FirstOrDefaultAsync(x => x.Id == id, ct);
        if (e is null) return NotFound();
        // Системную (эталонную) запись правит только SuperAdmin без override.
        if (e.OrganizationId is null && !(User.IsInRole("SuperAdmin")))
            return Forbid();
        if (input.ParentId == id) return BadRequest(new { error = "ParentId cannot be self" });

        e.Name = input.Name;
        e.ParentId = input.ParentId;
        e.Path = await BuildPathAsync(input.ParentId, input.Name, ct);
        e.SortOrder = input.SortOrder;
        e.MarkupPercent = input.MarkupPercent;
        await _db.SaveChangesAsync(ct);
        return NoContent();
    }

    [HttpDelete("{id:guid}"), Authorize(Roles = "Admin,SuperAdmin")]
    public async Task<IActionResult> Delete(Guid id, CancellationToken ct)
    {
        var owner = await _db.ProductGroups.Where(x => x.Id == id).Select(x => x.OrganizationId).FirstOrDefaultAsync(ct);
        if (owner is null && !User.IsInRole("SuperAdmin")) return Forbid();
        var hasChildren = await _db.ProductGroups.AnyAsync(g => g.ParentId == id, ct);
        if (hasChildren) return BadRequest(new { error = "Нельзя удалить группу с подгруппами" });
        var hasProducts = await _db.Products.AnyAsync(p => p.ProductGroupId == id, ct);
        if (hasProducts) return BadRequest(new { error = "Нельзя удалить группу с товарами" });

        var e = await _db.ProductGroups.FirstOrDefaultAsync(x => x.Id == id, ct);
        if (e is null) return NotFound();
        _db.ProductGroups.Remove(e);
        await _db.SaveChangesAsync(ct);
        return NoContent();
    }

    private async Task<string> BuildPathAsync(Guid? parentId, string name, CancellationToken ct)
    {
        if (parentId is null) return name;
        var parent = await _db.ProductGroups.AsNoTracking().FirstOrDefaultAsync(g => g.Id == parentId, ct);
        return parent is null ? name : $"{parent.Path}/{name}";
    }
}
