using Fast.Web.Logic;
using Fast.Web.Models;

namespace Fast.Web.Controllers;

[Authorize]
[ApiController]
[Route("api/[controller]")]
public class SosDealController : ODataController
{
    private readonly MyDbContext db;

    public SosDealController(MyDbContext db)
    {
        this.db = db;
    }

    [Permission("SOS Gas Nomination", PermissionType.Modify)]
    [Route("[action]")]
    public async Task<IActionResult> GetTransferPaths(int pipeId)
    {
        var paths = await SosDatabaseHelper.GetTransferPathsAsync(pipeId);
        return Ok(paths);
    }

    [Permission("SOS Gas Nomination", PermissionType.Modify)]
    [Route("[action]")]
    public async Task<IActionResult> GetTransferDeals(int pathId, int meterId, DateOnly nomDate)
    {
        var result = new SosTransferPathDeals();

        var productId = (int)Enums.Product.NaturalGas;

        var route1 = await (
            from q in db.GasPathRoutes
                .Include(x => x.Path)
                .Include(x => x.MeterMap)
                .ThenInclude(x => x!.Meter1)
                .ThenInclude(x => x.MeterProducts)
                .ThenInclude(x => x.SourceZone)
                .Include(x => x.MeterMap)
                .ThenInclude(x => x!.Meter2)
                .ThenInclude(x => x.MeterProducts)
                .ThenInclude(x => x.SourceZone)
            where q.PathId == pathId && q.OrderId == 1
            select q
        ).AsNoTracking().FirstOrDefaultAsync();

        if (route1 == null)
            return Ok(result);

        int SupplyCounterpartyID = route1.Path.SourceCounterpartyId.GetValueOrDefault();

        Meter? beginMeter = route1.IsMeter1First.GetValueOrDefault() ? route1.MeterMap?.Meter1 : route1.MeterMap?.Meter2;

        if (beginMeter == null)
            return Ok(result);

        var meterProduct = beginMeter.MeterProducts
            .Where(x => x.ProductId == productId && x.EffectiveDate <= nomDate).OrderByDescending(x => x.EffectiveDate).FirstOrDefault();
        int? supplyPipelineID = meterProduct?.SourceZone?.PipelineId;

        if (supplyPipelineID == null)
            return Ok(result);

        var pointIds = new List<int>();
        var mp = await db.MeterProducts.AsNoTracking()
            .Include(x => x.MeterProductSourcePoints)
            .Where(x => x.ProductId == productId && x.MeterId == meterId && x.EffectiveDate <= nomDate)
            .OrderByDescending(x => x.EffectiveDate).FirstOrDefaultAsync();
        if (mp != null)
            pointIds = mp.MeterProductSourcePoints.Select(x => x.PointId).Distinct().ToList();

        var supplyDealsAndPoints = await db.PointSourceDeliveries
            .Where(psd => psd.Deal.BuyButton == 1 && psd.Deal.StartDate <= nomDate && psd.Deal.EndDate >= nomDate &&
                psd.Deal.PipelineSourceDeliveryId == supplyPipelineID && psd.Deal.CounterpartyId == SupplyCounterpartyID &&
                pointIds.Contains(psd.PointId))
            .Select(psd => new { psd.Deal.Id, psd.Deal.TicketNum, psd.PointId, psd.Point.Name })
            .AsNoTracking().ToListAsync();

        var supplyDeals = supplyDealsAndPoints.Select(m => new { m.Id, m.TicketNum }).GroupBy(m => new { m.Id, m.TicketNum });

        var supplyPoints = supplyDealsAndPoints.GroupBy(m => new { m.PointId, m.Name })
            .Select(g => new SosTransferPathPoint
            {
                PointId = g.Key.PointId,
                PointName = g.Key.Name ?? "",
                PointDealIds = g.Select(x => x.Id).ToList(),
                PointMeterIds = new List<int> { meterId }
            });

        result.PathDeals = supplyDeals.Select(x => new SosTransferPathDeal() { DealId = x.Key.Id, DealNum = x.Key.TicketNum ?? "" }).ToList();
        result.PathPoints = supplyPoints.ToList();
        result.PathMeters = await db.Meters
            .Where(m => m.Id == meterId)
            .Select(m => new SosTransferPathMeter() { MeterId = m.Id, MeterName = m.Name }).AsNoTracking().ToListAsync();

        return Ok(result);
    }

    [Permission("SOS Gas Nomination", PermissionType.Modify)]
    [Route("[action]")]
    public async Task<IActionResult> SaveSupplyDeal(SosDeal supplyItem)
    {
        await SaveDeal(supplyItem, true);
        return Ok();
    }

    [Permission("SOS Gas Nomination", PermissionType.Modify)]
    [Route("[action]")]
    public async Task<IActionResult> SaveMarketDeal(SosDeal marketItem)
    {
        await SaveDeal(marketItem, false);
        return Ok();
    }

    [Permission("SOS Gas Nomination", PermissionType.Modify)]
    [Route("[action]")]
    public async Task<IActionResult> SaveTransfer(SosTransferAdd transferAddItem)
    {
        int userId = Util.GetAppUserId(User);
        var transferAdder = new SosTransferAdder(db, transferAddItem, userId);
        await transferAdder.SaveTransferAsync();

        return Ok();
    }

    private static string GetUpdateNotes(DateTime created, string createdName, DateTime modified, string modifiedName)
    {
        var createdStr = created.ToString(@"MMM d yyyy \a\t h:mm tt");
        var modifiedStr = modified.ToString(@"MMM d yyyy \a\t h:mm tt");
        string updateNotes = $"Created {createdStr}\r\nBy {createdName}\r\n\r\nModified {modifiedStr}\r\nBy {modifiedName}";
        return updateNotes;
    }

    private async Task SaveDeal(SosDeal dealItem, bool isBuy)
    {
        var pipeId = await db.Points.Where(x => x.Id == dealItem.PointId).Select(x => x.PipelineId).FirstOrDefaultAsync();
        var userId = Util.GetAppUserId(User);
        var newDeal = new Deal();
        newDeal.TicketNum = await Util.GetNewDealNumAsync((int)Enums.TransactionType.PhysicalGas, db);
        newDeal.CounterpartyId = dealItem.CounterpartyId;
        newDeal.PointId = dealItem.PointId;
        newDeal.PointSourceDeliveries.Add(new PointSourceDelivery() { PointId = dealItem.PointId });
        newDeal.StartDate = dealItem.StartDate;
        newDeal.EndDate = dealItem.EndDate;
        newDeal.BuyButton = isBuy ? 1 : -1;
        newDeal.Volume = dealItem.Volume;
        newDeal.IsVariableVolume = false;
        newDeal.TradingDate = DateOnly.FromDateTime(DateTime.Today);
        newDeal.TraderId = userId;
        newDeal.InternalEntityId = 315;
        newDeal.PipelineId = pipeId;
        newDeal.PipelineSourceDeliveryId = pipeId;
        newDeal.Created = DateTime.UtcNow;
        newDeal.CreatedBy = userId;
        newDeal.Modified = DateTime.UtcNow;
        newDeal.ModifiedBy = userId;
        var createdName = (from q in db.AppUsers where q.Id == newDeal.CreatedBy select q.DisplayName).First();
        var modifiedName = (from q in db.AppUsers where q.Id == newDeal.ModifiedBy select q.DisplayName).First();
        newDeal.UpdateNotes = GetUpdateNotes(newDeal.Created.GetValueOrDefault(), createdName, newDeal.Modified.Value, modifiedName);
        newDeal.TransactionTypeId = (int)Enums.TransactionType.PhysicalGas;
        newDeal.ProductId = (int)Enums.Product.NaturalGas;
        newDeal.DeliveryModeId = null;
        newDeal.DealPurposeId = (int)Enums.DealPurpose.General;
        newDeal.RegionId = 1;
        newDeal.StrategyId = 1;
        newDeal.PortfolioId = 1;
        newDeal.BookId = 1;
        newDeal.PhysicalDealTypeId = dealItem.DealType.ToLower() == "baseload" ? 2 : 3;
        newDeal.DealStatusId = 17; //entered from SOS
        newDeal.NettingContractNumber = await (
            from q in db.Contracts
            where q.ProductId == (int)Enums.Product.NaturalGas &&
                (!q.TerminationEffectiveDate.HasValue || q.TerminationEffectiveDate.Value > DateTime.Today.ToUniversalTime()) &&
                q.Counterparty.Id == newDeal.CounterpartyId &&
                q.InternalEntity.Id == newDeal.InternalEntityId
            orderby q.EffectiveDate descending, q.ContractNum
            select q.ContractNum
        ).FirstOrDefaultAsync();
        db.Deals.Add(newDeal);
        await db.SaveChangesAsync();
    }
}
