using foodmarket.Api.Infrastructure.Tenancy;
using foodmarket.Application.Common.Tenancy;
using foodmarket.Infrastructure.Integrations.MoySklad;
using foodmarket.Infrastructure.Persistence;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace foodmarket.Api.Controllers.Admin;

// Временные эндпоинты для очистки данных после кривых импортов.
// Удалять только свой tenant — query-filter на DbSets это обеспечивает.
[ApiController]
[Authorize(Policy = "AdminAccess")]
[Route("api/admin/cleanup")]
public class AdminCleanupController : ControllerBase
{
    private readonly AppDbContext _db;
    private readonly IServiceScopeFactory _scopes;
    private readonly ImportJobRegistry _jobs;
    private readonly ITenantContext _tenant;

    public AdminCleanupController(
        AppDbContext db,
        IServiceScopeFactory scopes,
        ImportJobRegistry jobs,
        ITenantContext tenant)
    {
        _db = db;
        _scopes = scopes;
        _jobs = jobs;
        _tenant = tenant;
    }

    public record CleanupStats(
        int Counterparties,
        int Products,
        int ProductGroups,
        int ProductBarcodes,
        int ProductPrices,
        int Supplies,
        int RetailSales,
        int Stocks,
        int StockMovements);

    public record CleanupResult(string Scope, CleanupStats Deleted);

    [HttpGet("stats")]
    public async Task<ActionResult<CleanupStats>> GetStats(CancellationToken ct)
        => new CleanupStats(
            await _db.Counterparties.CountAsync(ct),
            await _db.Products.CountAsync(ct),
            await _db.ProductGroups.CountAsync(ct),
            await _db.ProductBarcodes.CountAsync(ct),
            await _db.ProductPrices.CountAsync(ct),
            await _db.Supplies.CountAsync(ct),
            await _db.RetailSales.CountAsync(ct),
            await _db.Stocks.CountAsync(ct),
            await _db.StockMovements.CountAsync(ct));

    /// <summary>Удалить всех контрагентов текущей организации. Чтобы не нарваться на FK,
    /// сначала обнуляем ссылки (Product.DefaultSupplier, RetailSale.Customer) и сносим
    /// поставки (они жёстко ссылаются на supplier).</summary>
    [HttpDelete("counterparties")]
    public async Task<ActionResult<CleanupResult>> WipeCounterparties(CancellationToken ct)
    {
        var before = await SnapshotAsync(ct);

        // 1. Обнуляем nullable-FK
        await _db.Products
            .Where(p => p.DefaultSupplierId != null)
            .ExecuteUpdateAsync(u => u.SetProperty(p => p.DefaultSupplierId, (Guid?)null), ct);
        await _db.RetailSales
            .Where(s => s.CustomerId != null)
            .ExecuteUpdateAsync(u => u.SetProperty(s => s.CustomerId, (Guid?)null), ct);

        // 2. Сносим поставки (NOT NULL supplier) + их stock movements/stocks
        await _db.StockMovements
            .Where(m => m.DocumentType == "supply" || m.DocumentType == "supply-reversal")
            .ExecuteDeleteAsync(ct);
        await _db.SupplyLines.ExecuteDeleteAsync(ct);
        await _db.Supplies.ExecuteDeleteAsync(ct);

        // 3. Контрагенты
        await _db.Counterparties.ExecuteDeleteAsync(ct);

        var after = await SnapshotAsync(ct);
        return new CleanupResult("counterparties", Diff(before, after));
    }

    // Async-версия полной очистки: возвращает jobId, реальные DELETE в фоне, прогресс в Stage+Deleted.
    [HttpPost("all/async")]
    public ActionResult<object> WipeAllAsync()
    {
        var orgId = _tenant.OrganizationId ?? throw new InvalidOperationException("No tenant.");
        var job = _jobs.Create("cleanup-all");
        job.Stage = "Подготовка…";
        _ = Task.Run(async () =>
        {
            try
            {
                using var tenantScope = HttpContextTenantContext.UseOverride(orgId);
                using var scope = _scopes.CreateScope();
                var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();

                var steps = new (string Stage, Func<Task<int>> Run)[]
                {
                    ("Движения склада", () => db.StockMovements.ExecuteDeleteAsync()),
                    ("Остатки", () => db.Stocks.ExecuteDeleteAsync()),
                    ("Строки поставок", () => db.SupplyLines.ExecuteDeleteAsync()),
                    ("Поставки", () => db.Supplies.ExecuteDeleteAsync()),
                    ("Строки продаж", () => db.RetailSaleLines.ExecuteDeleteAsync()),
                    ("Продажи", () => db.RetailSales.ExecuteDeleteAsync()),
                    ("Изображения товаров", () => db.ProductImages.ExecuteDeleteAsync()),
                    ("Цены товаров", () => db.ProductPrices.ExecuteDeleteAsync()),
                    ("Штрихкоды", () => db.ProductBarcodes.ExecuteDeleteAsync()),
                    ("Товары", () => db.Products.ExecuteDeleteAsync()),
                    ("Группы товаров", () => db.ProductGroups.ExecuteDeleteAsync()),
                    ("Контрагенты", () => db.Counterparties.ExecuteDeleteAsync()),
                };

                foreach (var (stage, run) in steps)
                {
                    job.Stage = $"Удаление: {stage}…";
                    job.Deleted += await run();
                }

                job.Stage = "Готово";
                job.Message = $"Удалено записей: {job.Deleted}.";
                job.Status = ImportJobStatus.Succeeded;
            }
            catch (Exception ex)
            {
                job.Status = ImportJobStatus.Failed;
                job.Message = ex.Message;
                job.Errors.Add(ex.ToString());
            }
            finally
            {
                job.FinishedAt = DateTime.UtcNow;
            }
        });
        return Ok(new { jobId = job.Id });
    }

    /// <summary>Полная очистка данных текущей организации — всё кроме настроек:
    /// остаются Organization, пользователи, справочники (Country, Currency, UnitOfMeasure,
    /// PriceType), Store и RetailPoint. Удаляются: Product*, ProductGroup, Counterparty,
    /// Supply*, RetailSale*, Stock, StockMovement.</summary>
    [HttpDelete("all")]
    public async Task<ActionResult<CleanupResult>> WipeAll(CancellationToken ct)
    {
        var before = await SnapshotAsync(ct);

        // Documents first — they reference products, counterparties, stores.
        await _db.StockMovements.ExecuteDeleteAsync(ct);
        await _db.Stocks.ExecuteDeleteAsync(ct);

        await _db.SupplyLines.ExecuteDeleteAsync(ct);
        await _db.Supplies.ExecuteDeleteAsync(ct);

        await _db.RetailSaleLines.ExecuteDeleteAsync(ct);
        await _db.RetailSales.ExecuteDeleteAsync(ct);

        // Product composites.
        await _db.ProductImages.ExecuteDeleteAsync(ct);
        await _db.ProductPrices.ExecuteDeleteAsync(ct);
        await _db.ProductBarcodes.ExecuteDeleteAsync(ct);

        // Products reference counterparty.DefaultSupplier — FK Restrict, but we're about
        // to delete products anyway, so order products → counterparties.
        await _db.Products.ExecuteDeleteAsync(ct);
        await _db.ProductGroups.ExecuteDeleteAsync(ct);
        await _db.Counterparties.ExecuteDeleteAsync(ct);

        var after = await SnapshotAsync(ct);
        return new CleanupResult("all", Diff(before, after));
    }

    private async Task<CleanupStats> SnapshotAsync(CancellationToken ct) => new(
        await _db.Counterparties.CountAsync(ct),
        await _db.Products.CountAsync(ct),
        await _db.ProductGroups.CountAsync(ct),
        await _db.ProductBarcodes.CountAsync(ct),
        await _db.ProductPrices.CountAsync(ct),
        await _db.Supplies.CountAsync(ct),
        await _db.RetailSales.CountAsync(ct),
        await _db.Stocks.CountAsync(ct),
        await _db.StockMovements.CountAsync(ct));

    private static CleanupStats Diff(CleanupStats a, CleanupStats b) => new(
        a.Counterparties - b.Counterparties,
        a.Products - b.Products,
        a.ProductGroups - b.ProductGroups,
        a.ProductBarcodes - b.ProductBarcodes,
        a.ProductPrices - b.ProductPrices,
        a.Supplies - b.Supplies,
        a.RetailSales - b.RetailSales,
        a.Stocks - b.Stocks,
        a.StockMovements - b.StockMovements);
}
