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

namespace foodmarket.Api.Controllers.Catalog;

/// <summary>Phase5c: единицы измерения — глобальный справочник (управляется
/// SuperAdmin'ом). Орга подключает нужные через junction org_units_of_measure.
/// Этот контроллер: read-only список + toggle включения/выключения.
/// CRUD — на /api/super-admin/units-of-measure.</summary>
[ApiController]
[Authorize]
[Route("api/catalog/units-of-measure")]
public class UnitsOfMeasureController : ControllerBase
{
    private readonly AppDbContext _db;
    private readonly ITenantContext _tenant;

    public UnitsOfMeasureController(AppDbContext db, ITenantContext tenant)
    {
        _db = db;
        _tenant = tenant;
    }

    /// <summary>Список единиц для текущей орги: только включённые active
    /// globals. Для SuperAdmin без override — все active globals (чтобы UI
    /// мог показывать справочник в платформенном контексте).</summary>
    [HttpGet]
    public async Task<ActionResult<PagedResult<UnitOfMeasureDto>>> List(
        [FromQuery] PagedRequest req, CancellationToken ct)
    {
        var orgId = _tenant.OrganizationId;
        var isSuperAdminPlatform = _tenant.IsSuperAdmin && !_tenant.IsTenantOverride;

        // Globals — read через IgnoreQueryFilters: фильтр иначе пропустит
        // нашу же null-OrganizationId-запись только потому, что мы её специально
        // ищем; явная фильтрация по OrganizationId IS NULL понятнее.
        var q = _db.UnitsOfMeasure
            .IgnoreQueryFilters()
            .AsNoTracking()
            .Where(u => u.OrganizationId == null && u.IsActive);

        if (!isSuperAdminPlatform && orgId.HasValue)
        {
            // Org Admin / Storekeeper: только включённые в его орге.
            q = q.Where(u => _db.OrgUnitsOfMeasure
                .IgnoreQueryFilters()
                .Any(j => j.OrganizationId == orgId.Value && j.UnitOfMeasureId == u.Id));
        }

        if (!string.IsNullOrWhiteSpace(req.Search))
        {
            var s = req.Search.Trim().ToLower();
            q = q.Where(u => u.Name.ToLower().Contains(s) || u.Code.ToLower().Contains(s));
        }
        var total = await q.CountAsync(ct);
        q = (req.Sort, req.Desc) switch
        {
            ("code", false) => q.OrderBy(u => u.Code),
            ("code", true) => q.OrderByDescending(u => u.Code),
            ("name", true) => q.OrderByDescending(u => u.Name),
            _ => q.OrderBy(u => u.Name),
        };
        var items = await q
            .Skip(req.Skip).Take(req.Take)
            .Select(u => new UnitOfMeasureDto(
                u.Id, u.Code, u.Name, u.OrganizationId, u.IsActive, true))
            .ToListAsync(ct);
        return new PagedResult<UnitOfMeasureDto> { Items = items, Total = total, Page = req.Page, PageSize = req.Take };
    }

    [HttpGet("{id:guid}")]
    public async Task<ActionResult<UnitOfMeasureDto>> Get(Guid id, CancellationToken ct)
    {
        var u = await _db.UnitsOfMeasure
            .IgnoreQueryFilters()
            .AsNoTracking()
            .FirstOrDefaultAsync(x => x.Id == id && x.OrganizationId == null, ct);
        if (u is null) return NotFound();

        var orgId = _tenant.OrganizationId;
        var enabled = !orgId.HasValue || await _db.OrgUnitsOfMeasure
            .IgnoreQueryFilters()
            .AnyAsync(j => j.OrganizationId == orgId.Value && j.UnitOfMeasureId == id, ct);
        return new UnitOfMeasureDto(u.Id, u.Code, u.Name, u.OrganizationId, u.IsActive, enabled);
    }

    /// <summary>Включить global для текущей орги. Идемпотентно: повторный
    /// вызов отдаёт 204 и не плодит дубликатов junction.</summary>
    [HttpPost("{id:guid}/enable"), Authorize(Roles = "Admin,SuperAdmin")]
    public async Task<IActionResult> Enable(Guid id, CancellationToken ct)
    {
        var orgId = _tenant.OrganizationId;
        if (!orgId.HasValue) return BadRequest(new { error = "Tenant context required." });
        var unit = await _db.UnitsOfMeasure
            .IgnoreQueryFilters()
            .FirstOrDefaultAsync(u => u.Id == id && u.OrganizationId == null && u.IsActive, ct);
        if (unit is null) return NotFound();

        var existing = await _db.OrgUnitsOfMeasure
            .IgnoreQueryFilters()
            .AnyAsync(j => j.OrganizationId == orgId.Value && j.UnitOfMeasureId == id, ct);
        if (!existing)
        {
            _db.OrgUnitsOfMeasure.Add(new OrgUnitOfMeasure { OrganizationId = orgId.Value, UnitOfMeasureId = id });
            await _db.SaveChangesAsync(ct);
        }
        return NoContent();
    }

    /// <summary>Отключить global для текущей орги. Если на эту единицу
    /// ссылаются продукты орги — 409 со списком названий, чтобы админ
    /// перепривязал их сначала.</summary>
    [HttpDelete("{id:guid}/enable"), Authorize(Roles = "Admin,SuperAdmin")]
    public async Task<IActionResult> Disable(Guid id, CancellationToken ct)
    {
        var orgId = _tenant.OrganizationId;
        if (!orgId.HasValue) return BadRequest(new { error = "Tenant context required." });

        var productNames = await _db.Products
            .Where(p => p.UnitOfMeasureId == id)
            .OrderBy(p => p.Name)
            .Select(p => p.Name)
            .Take(10)
            .ToListAsync(ct);
        if (productNames.Count > 0)
        {
            return Conflict(new
            {
                error = "Единица используется в товарах. Перепривяжите товары на другую единицу прежде чем отключать.",
                products = productNames,
            });
        }

        var link = await _db.OrgUnitsOfMeasure
            .IgnoreQueryFilters()
            .FirstOrDefaultAsync(j => j.OrganizationId == orgId.Value && j.UnitOfMeasureId == id, ct);
        if (link is not null)
        {
            _db.OrgUnitsOfMeasure.Remove(link);
            await _db.SaveChangesAsync(ct);
        }
        return NoContent();
    }
}
