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>Локальное хранилище изображений товаров: multipart upload →
/// /app/uploads/products/{productId}/{guid}.{ext}; в БД лежит относительный путь
/// типа "/uploads/products/{id}/{file}", раздаётся nginx'ом как статика.</summary>
[ApiController]
[Authorize]
[Route("api/catalog/products/{productId:guid}/images")]
public class ProductImagesController : ControllerBase
{
    private readonly AppDbContext _db;
    private readonly ITenantContext _tenant;
    private readonly IWebHostEnvironment _env;

    public ProductImagesController(AppDbContext db, ITenantContext tenant, IWebHostEnvironment env)
    {
        _db = db;
        _tenant = tenant;
        _env = env;
    }

    private static readonly HashSet<string> AllowedExt = new(StringComparer.OrdinalIgnoreCase)
        { ".jpg", ".jpeg", ".png", ".webp", ".gif" };

    private const long MaxBytes = 10 * 1024 * 1024;

    private string UploadRoot => Path.Combine(_env.ContentRootPath, "uploads", "products");

    public record ImageDto(Guid Id, string Url, bool IsMain, int SortOrder);

    [HttpGet]
    public async Task<ActionResult<IReadOnlyList<ImageDto>>> List(Guid productId, CancellationToken ct)
    {
        var product = await _db.Products.FirstOrDefaultAsync(p => p.Id == productId, ct);
        if (product is null) return NotFound();

        var images = await _db.ProductImages
            .Where(i => i.ProductId == productId)
            .OrderBy(i => i.SortOrder)
            .Select(i => new ImageDto(i.Id, i.Url, i.IsMain, i.SortOrder))
            .ToListAsync(ct);
        return images;
    }

    [HttpPost, Authorize(Roles = "Admin,Storekeeper")]
    [RequestSizeLimit(MaxBytes)]
    public async Task<ActionResult<ImageDto>> Upload(Guid productId, IFormFile file, CancellationToken ct)
    {
        if (file is null || file.Length == 0) return BadRequest(new { error = "No file." });
        if (file.Length > MaxBytes) return BadRequest(new { error = $"File too large (max {MaxBytes / 1024 / 1024} MB)." });
        var ext = Path.GetExtension(file.FileName).ToLowerInvariant();
        if (!AllowedExt.Contains(ext)) return BadRequest(new { error = "Only JPG/PNG/WEBP/GIF are allowed." });

        var product = await _db.Products.FirstOrDefaultAsync(p => p.Id == productId, ct);
        if (product is null) return NotFound();

        var orgId = _tenant.OrganizationId ?? throw new InvalidOperationException("No tenant.");
        var dir = Path.Combine(UploadRoot, productId.ToString());
        Directory.CreateDirectory(dir);

        var fileName = $"{Guid.NewGuid():N}{ext}";
        var fullPath = Path.Combine(dir, fileName);
        using (var stream = System.IO.File.Create(fullPath))
        {
            await file.CopyToAsync(stream, ct);
        }

        var relativeUrl = $"/uploads/products/{productId}/{fileName}";
        var sortOrder = await _db.ProductImages.Where(i => i.ProductId == productId).CountAsync(ct);
        var isMain = sortOrder == 0;   // первое загруженное — основное
        var entity = new ProductImage
        {
            OrganizationId = orgId,
            ProductId = productId,
            Url = relativeUrl,
            IsMain = isMain,
            SortOrder = sortOrder,
        };
        _db.ProductImages.Add(entity);
        if (isMain) product.ImageUrl = relativeUrl;
        await _db.SaveChangesAsync(ct);

        return new ImageDto(entity.Id, entity.Url, entity.IsMain, entity.SortOrder);
    }

    [HttpDelete("{imageId:guid}"), Authorize(Roles = "Admin,Storekeeper")]
    public async Task<IActionResult> Delete(Guid productId, Guid imageId, CancellationToken ct)
    {
        var image = await _db.ProductImages.FirstOrDefaultAsync(i => i.Id == imageId && i.ProductId == productId, ct);
        if (image is null) return NotFound();

        // Удаляем файл с диска (не фейлим если отсутствует).
        var fileName = Path.GetFileName(image.Url);
        var fullPath = Path.Combine(UploadRoot, productId.ToString(), fileName);
        try { if (System.IO.File.Exists(fullPath)) System.IO.File.Delete(fullPath); }
        catch { /* ignore */ }

        _db.ProductImages.Remove(image);

        // Если удалили основное — назначаем основным оставшуюся первую.
        if (image.IsMain)
        {
            var next = await _db.ProductImages
                .Where(i => i.ProductId == productId && i.Id != imageId)
                .OrderBy(i => i.SortOrder)
                .FirstOrDefaultAsync(ct);
            if (next is not null)
            {
                next.IsMain = true;
                var product = await _db.Products.FirstOrDefaultAsync(p => p.Id == productId, ct);
                if (product is not null) product.ImageUrl = next.Url;
            }
            else
            {
                var product = await _db.Products.FirstOrDefaultAsync(p => p.Id == productId, ct);
                if (product is not null) product.ImageUrl = null;
            }
        }

        await _db.SaveChangesAsync(ct);
        return NoContent();
    }

    [HttpPost("{imageId:guid}/main"), Authorize(Roles = "Admin,Storekeeper")]
    public async Task<IActionResult> SetMain(Guid productId, Guid imageId, CancellationToken ct)
    {
        var image = await _db.ProductImages.FirstOrDefaultAsync(i => i.Id == imageId && i.ProductId == productId, ct);
        if (image is null) return NotFound();

        await _db.ProductImages.Where(i => i.ProductId == productId).ExecuteUpdateAsync(
            s => s.SetProperty(i => i.IsMain, false), ct);
        image.IsMain = true;

        var product = await _db.Products.FirstOrDefaultAsync(p => p.Id == productId, ct);
        if (product is not null) product.ImageUrl = image.Url;

        await _db.SaveChangesAsync(ct);
        return NoContent();
    }
}
