using ERPCore.Domain.Entities;
using ERPCore.Domain.Enums;
using Microsoft.EntityFrameworkCore;

namespace ERPCore.Infra.Persistence;

/// <summary>
/// Idempotent startup seeding of configurable reference data (docs/10 §B.8.3).
/// Reason codes are seeded at runtime (not via <c>HasData</c>) so the identity
/// sequence advances normally and later admin <c>POST /reason-codes</c> calls
/// cannot collide with seeded ids.
/// </summary>
public static class DataSeeder
{
    /// <summary>
    /// Item type names the frontend builder has always assumed exist (they were hardcoded
    /// while it ran on mock data). Seeded so the dropdown is not empty on a fresh database;
    /// users add their own (e.g. Material) from the admin screen.
    /// </summary>
    private static readonly string[] StandardItemTypes = ["Color", "Size"];

    private static readonly (string Code, string Description, ReasonContext Context)[] StandardReasonCodes =
    [
        ("DMG", "Damage", ReasonContext.Adjustment),
        ("THEFT", "Theft/Loss", ReasonContext.Adjustment),
        ("VAR", "Count Variance", ReasonContext.Adjustment),
        ("EXP", "Expiry Write-off", ReasonContext.Adjustment),
        ("SYS", "System Correction", ReasonContext.Adjustment),
        ("DEF", "Defective", ReasonContext.Return),
        ("WRONG", "Wrong Item", ReasonContext.Return),
        ("OVER", "Over-supply", ReasonContext.Return),
        ("QREJ", "Quality Reject", ReasonContext.Return),
    ];

    public static async Task SeedAsync(ErpDbContext db, CancellationToken ct = default)
    {
        var dirty = await SeedReasonCodesAsync(db, ct);
        dirty |= await SeedItemTypesAsync(db, ct);
        dirty |= await SeedProductConfigAsync(db, ct);

        if (dirty) await db.SaveChangesAsync(ct);
    }

    private static async Task<bool> SeedReasonCodesAsync(ErpDbContext db, CancellationToken ct)
    {
        var existing = await db.ReasonCodes
            .Select(r => new { r.Context, r.Code })
            .ToListAsync(ct);
        var have = existing.Select(x => (x.Context, x.Code)).ToHashSet();

        var toAdd = StandardReasonCodes
            .Where(r => !have.Contains((r.Context, r.Code)))
            .Select(r => new ReasonCode { Code = r.Code, Description = r.Description, Context = r.Context })
            .ToList();

        if (toAdd.Count == 0) return false;

        db.ReasonCodes.AddRange(toAdd);
        return true;
    }

    private static async Task<bool> SeedItemTypesAsync(ErpDbContext db, CancellationToken ct)
    {
        var have = await db.ItemTypes.Select(t => t.Name).ToListAsync(ct);

        var toAdd = StandardItemTypes
            .Where(name => !have.Contains(name, StringComparer.OrdinalIgnoreCase))
            .Select(name => new ItemType { Name = name, Status = EntityStatus.Active, CreatedAt = DateTime.UtcNow })
            .ToList();

        if (toAdd.Count == 0) return false;

        db.ItemTypes.AddRange(toAdd);
        return true;
    }

    /// <summary>
    /// Ensures the singleton product-config row exists (FR-MD-11). Migration #2 inserts it,
    /// so this only fires for a database built some other way — but without it every Item
    /// write would 404 on the missing config, so it is worth the one query at startup.
    /// New deployments start with all features on.
    /// </summary>
    private static async Task<bool> SeedProductConfigAsync(ErpDbContext db, CancellationToken ct)
    {
        if (await db.ProductConfig.AnyAsync(c => c.ConfigId == ProductConfig.SingletonId, ct)) return false;

        db.ProductConfig.Add(new ProductConfig
        {
            ConfigId = ProductConfig.SingletonId,
            SubcategoriesEnabled = true,
            BrandsEnabled = true,
            ItemTypesEnabled = true
        });
        return true;
    }
}
