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

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

    [HttpGet]
    public async Task<ActionResult<PagedResult<CountryDto>>> List([FromQuery] PagedRequest req, CancellationToken ct)
    {
        var q = _db.Countries.Include(c => c.DefaultCurrency).AsNoTracking().AsQueryable();
        if (!string.IsNullOrWhiteSpace(req.Search))
        {
            var s = req.Search.Trim().ToLower();
            q = q.Where(c => c.Name.ToLower().Contains(s) || c.Code.ToLower().Contains(s));
        }
        var total = await q.CountAsync(ct);
        q = (req.Sort, req.Desc) switch
        {
            ("code", false) => q.OrderBy(c => c.Code),
            ("code", true) => q.OrderByDescending(c => c.Code),
            ("currency", false) => q.OrderBy(c => c.DefaultCurrency != null ? c.DefaultCurrency.Code : null).ThenBy(c => c.Name),
            ("currency", true) => q.OrderByDescending(c => c.DefaultCurrency != null ? c.DefaultCurrency.Code : null).ThenBy(c => c.Name),
            ("vatRate", false) => q.OrderBy(c => c.VatRate).ThenBy(c => c.Name),
            ("vatRate", true) => q.OrderByDescending(c => c.VatRate).ThenBy(c => c.Name),
            ("name", true) => q.OrderByDescending(c => c.Name),
            _ => q.OrderBy(c => c.Name),
        };
        var items = await q
            .Skip(req.Skip).Take(req.Take)
            .Select(c => new CountryDto(
                c.Id, c.Code, c.Name,
                c.DefaultCurrencyId,
                c.DefaultCurrency != null ? c.DefaultCurrency.Code : null,
                c.DefaultCurrency != null ? c.DefaultCurrency.Symbol : null,
                c.VatRate))
            .ToListAsync(ct);
        return new PagedResult<CountryDto> { Items = items, Total = total, Page = req.Page, PageSize = req.Take };
    }

    [HttpGet("{id:guid}")]
    public async Task<ActionResult<CountryDto>> Get(Guid id, CancellationToken ct)
    {
        var c = await _db.Countries.Include(x => x.DefaultCurrency).AsNoTracking().FirstOrDefaultAsync(x => x.Id == id, ct);
        return c is null ? NotFound() : Project(c);
    }

    [HttpPost, Authorize(Roles = "SuperAdmin")]
    public async Task<ActionResult<CountryDto>> Create([FromBody] CountryInput input, CancellationToken ct)
    {
        var e = new Country
        {
            Code = input.Code.Trim().ToUpper(),
            Name = input.Name,
            DefaultCurrencyId = input.DefaultCurrencyId,
            VatRate = input.VatRate,
        };
        _db.Countries.Add(e);
        await _db.SaveChangesAsync(ct);
        await _db.Entry(e).Reference(x => x.DefaultCurrency).LoadAsync(ct);
        return CreatedAtAction(nameof(Get), new { id = e.Id }, Project(e));
    }

    [HttpPut("{id:guid}"), Authorize(Roles = "SuperAdmin")]
    public async Task<IActionResult> Update(Guid id, [FromBody] CountryInput input, CancellationToken ct)
    {
        var e = await _db.Countries.FirstOrDefaultAsync(x => x.Id == id, ct);
        if (e is null) return NotFound();
        e.Code = input.Code.Trim().ToUpper();
        e.Name = input.Name;
        e.DefaultCurrencyId = input.DefaultCurrencyId;
        e.VatRate = input.VatRate;
        await _db.SaveChangesAsync(ct);
        return NoContent();
    }

    [HttpDelete("{id:guid}"), Authorize(Roles = "SuperAdmin")]
    public async Task<IActionResult> Delete(Guid id, CancellationToken ct)
    {
        var e = await _db.Countries.FirstOrDefaultAsync(x => x.Id == id, ct);
        if (e is null) return NotFound();
        _db.Countries.Remove(e);
        await _db.SaveChangesAsync(ct);
        return NoContent();
    }

    private static CountryDto Project(Country c) => new(
        c.Id, c.Code, c.Name,
        c.DefaultCurrencyId, c.DefaultCurrency?.Code, c.DefaultCurrency?.Symbol, c.VatRate);
}
