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/price-types")]
public class PriceTypesController : ControllerBase
{
    private readonly AppDbContext _db;

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

    [HttpGet]
    public async Task<ActionResult<PagedResult<PriceTypeDto>>> List([FromQuery] PagedRequest req, CancellationToken ct)
    {
        var q = _db.PriceTypes.AsNoTracking().AsQueryable();
        if (!string.IsNullOrWhiteSpace(req.Search))
        {
            var s = req.Search.Trim().ToLower();
            q = q.Where(p => p.Name.ToLower().Contains(s));
        }
        var total = await q.CountAsync(ct);
        q = (req.Sort, req.Desc) switch
        {
            ("name", false) => q.OrderBy(p => p.Name),
            ("name", true) => q.OrderByDescending(p => p.Name),
            ("isRequired", false) => q.OrderBy(p => p.IsRequired).ThenBy(p => p.Name),
            ("isRequired", true) => q.OrderByDescending(p => p.IsRequired).ThenBy(p => p.Name),
            _ => q.OrderByDescending(p => p.IsSystem).ThenBy(p => p.SortOrder).ThenBy(p => p.Name),
        };
        var items = await q
            .Skip(req.Skip).Take(req.Take)
            .Select(p => new PriceTypeDto(p.Id, p.Name, p.IsRequired, p.IsSystem, p.IsRetail, p.SortOrder))
            .ToListAsync(ct);
        return new PagedResult<PriceTypeDto> { Items = items, Total = total, Page = req.Page, PageSize = req.Take };
    }

    [HttpGet("{id:guid}")]
    public async Task<ActionResult<PriceTypeDto>> Get(Guid id, CancellationToken ct)
    {
        var p = await _db.PriceTypes.AsNoTracking().FirstOrDefaultAsync(x => x.Id == id, ct);
        return p is null ? NotFound() : new PriceTypeDto(p.Id, p.Name, p.IsRequired, p.IsSystem, p.IsRetail, p.SortOrder);
    }

    [HttpPost, Authorize(Roles = "Admin")]
    public async Task<ActionResult<PriceTypeDto>> Create([FromBody] PriceTypeInput input, CancellationToken ct)
    {
        if (input.IsRetail)
        {
            // Уникальность IsRetail: не более одной записи в организации.
            await _db.PriceTypes.Where(p => p.IsRetail)
                .ExecuteUpdateAsync(s => s.SetProperty(p => p.IsRetail, false), ct);
        }
        var e = new PriceType
        {
            Name = input.Name,
            IsRequired = input.IsRequired,
            IsSystem = false,
            IsRetail = input.IsRetail,
            SortOrder = input.SortOrder,
        };
        _db.PriceTypes.Add(e);
        await _db.SaveChangesAsync(ct);
        return CreatedAtAction(nameof(Get), new { id = e.Id },
            new PriceTypeDto(e.Id, e.Name, e.IsRequired, e.IsSystem, e.IsRetail, e.SortOrder));
    }

    [HttpPut("{id:guid}"), Authorize(Roles = "Admin")]
    public async Task<IActionResult> Update(Guid id, [FromBody] PriceTypeInput input, CancellationToken ct)
    {
        var e = await _db.PriceTypes.FirstOrDefaultAsync(x => x.Id == id, ct);
        if (e is null) return NotFound();
        if (input.IsRetail && !e.IsRetail)
        {
            // Снимаем IsRetail с прежней записи (если была) — гарантия уникальности.
            await _db.PriceTypes.Where(p => p.IsRetail && p.Id != id)
                .ExecuteUpdateAsync(s => s.SetProperty(p => p.IsRetail, false), ct);
        }
        e.Name = input.Name;
        e.IsRetail = input.IsRetail;
        e.SortOrder = input.SortOrder;
        // У системной записи IsRequired всегда true и не меняется.
        e.IsRequired = e.IsSystem ? true : input.IsRequired;
        await _db.SaveChangesAsync(ct);
        return NoContent();
    }

    [HttpDelete("{id:guid}"), Authorize(Roles = "Admin")]
    public async Task<IActionResult> Delete(Guid id, CancellationToken ct)
    {
        var e = await _db.PriceTypes.FirstOrDefaultAsync(x => x.Id == id, ct);
        if (e is null) return NotFound();
        if (e.IsSystem) return BadRequest(new { error = "Системная запись не может быть удалена." });
        var wasRetail = e.IsRetail;
        _db.PriceTypes.Remove(e);
        await _db.SaveChangesAsync(ct);

        // Если удалённая запись была единственной IsRetail — фоллбэк на системную,
        // чтобы у организации всегда оставался один IsRetail-кандидат для POS.
        if (wasRetail)
        {
            var stillRetail = await _db.PriceTypes.AnyAsync(p => p.IsRetail, ct);
            if (!stillRetail)
            {
                var sys = await _db.PriceTypes.FirstOrDefaultAsync(p => p.IsSystem, ct);
                if (sys is not null && !sys.IsRetail)
                {
                    sys.IsRetail = true;
                    await _db.SaveChangesAsync(ct);
                }
            }
        }
        return NoContent();
    }
}
