using Fast.Web.Models;

namespace Fast.Web.Logic;

public class SetCrudeTneMeterPayload
{
    public required ActualsCrudeListItem ClickedItem { get; set; }
    public required IEnumerable<ActualsCrudeListItem> TneMeterItems { get; set; }
}

public class ActualsCrudeTneMeterService()
{
    public async Task<IEnumerable<ActualsCrudeListItem>> SetTneMetersAsync(SetCrudeTneMeterPayload payload, int appUserId)
    {
        using var db = Main.CreateContext();

        var mostRecentItem = await GetMostRecentItemAsync(db, payload.ClickedItem);
        var tneMeterId = mostRecentItem?.TneMeterId;
        var setTneMeter = tneMeterId != null;

        var tneMeterItems = payload.TneMeterItems;

        if (setTneMeter)
            await AddTneMetersAsync(tneMeterItems, appUserId, tneMeterId);
        else
            await RemoveTneMetersAsync(tneMeterItems, appUserId);

        return tneMeterItems;
    }

    public async Task<IEnumerable<ActualsCrudeListItem>> ToggleTneMetersAsync(SetCrudeTneMeterPayload payload, int appUserId)
    {
        var setTneMeter = payload.ClickedItem.TneMeterId == payload.ClickedItem.DeliveryMeterId;

        var tneMeterItems = payload.TneMeterItems;

        if (setTneMeter)
            await AddTneMetersAsync(tneMeterItems, appUserId);
        else
            await RemoveTneMetersAsync(tneMeterItems, appUserId);

        return tneMeterItems;
    }

    private static async Task<CrudeActual?> GetMostRecentItemAsync(MyDbContext db, ActualsCrudeListItem item)
    {
        return await (
            from x in db.CrudeActuals
            where x.ActualTypeId == item.ActualTypeId
                && x.SupplyNomId == item.SupplyNomId
                && x.MarketNomId == item.MarketNomId
                && x.LastTransferId == item.LastTransferId
            orderby x.SaveDate descending
            select x
        ).FirstOrDefaultAsync();
    }

    private static async Task AddTneMetersAsync(IEnumerable<ActualsCrudeListItem> items, int appUserId, int? tneMeterId = null)
    {
        using var db = Main.CreateContext();

        foreach (var item in items)
            await AddTneMeterAsync(db, appUserId, item, tneMeterId);

        await db.SaveChangesAsync();

        // set tne meter names
        var meterNameDict = await db.Meters.Select(x => new { x.Id, x.Name }).Distinct().ToDictionaryAsync(x => x.Id, x => x.Name);

        foreach (var item in items)
            item.TneMeterName = meterNameDict[item.TneMeterId!.Value];
    }

    private static async Task AddTneMeterAsync(
        MyDbContext db,
        int appUserId,
        ActualsCrudeListItem item,
        int? tneMeterId = null)
    {
        tneMeterId ??= item.ReceiptMeterId;

        var mostRecentItem = await GetMostRecentItemAsync(db, item);

        if (mostRecentItem == null)
        {
            // create a new item for today
            var dbItem = new CrudeActual
            {
                ActualTypeId = item.ActualTypeId,
                SupplyNomId = item.SupplyNomId,
                MarketNomId = item.MarketNomId,
                LastTransferId = item.LastTransferId,
                SaveDate = DateTime.Now.ToDateOnly(),
                SavedBy = appUserId,
                TneMeterId = tneMeterId,
                IsLinked = item.IsLinked
            };
            db.CrudeActuals.Add(dbItem);
        }
        else
        {
            // modify the most recent item
            mostRecentItem.SavedBy = appUserId;
            mostRecentItem.TneMeterId = tneMeterId;
        }

        item.TneMeterId = tneMeterId;
    }

    private static async Task RemoveTneMetersAsync(IEnumerable<ActualsCrudeListItem> items, int appUserId)
    {
        using var db = Main.CreateContext();

        foreach (var item in items)
        {
            var mostRecentItem = await GetMostRecentItemAsync(db, item);

            if (mostRecentItem != null)
            {
                var isRowEmpty = mostRecentItem.Volume == null
                    && mostRecentItem.Price == null
                    && mostRecentItem.Adder == null
                    && mostRecentItem.TransportRate == null;

                if (isRowEmpty)
                {
                    db.CrudeActuals.Remove(mostRecentItem);
                }
                else
                {
                    mostRecentItem.SavedBy = appUserId;
                    mostRecentItem.TneMeterId = null;
                }
            }

            item.TneMeterId = null;
            item.TneMeterName = null;
        }

        await db.SaveChangesAsync();
    }
}
