namespace Fast.Web.Controllers
{
    [Authorize]
    [ApiController]
    [Route("api/[controller]")]
    public class TransferMeterController : ODataController
    {
        private readonly MyDbContext db;
        private readonly AuthorizationHelper authHelper;

        public TransferMeterController(MyDbContext context)
        {
            db = context;
            authHelper = new AuthorizationHelper(Main.IsAuthenticationEnabled);
        }

        [Permission("Transfer Meter Map", PermissionType.View)]
        [Route("/odata/GetTransferMeterItems")]
        public IActionResult GetItems(ODataQueryOptions<TransferMeterListItem> queryOptions, bool isExport)
        {
            queryOptions = Util.GetQueryOptionsWithConvertedDates(queryOptions);
            var itemsQueryable = GetItemsInternal();
            var items = (queryOptions.ApplyTo(itemsQueryable) as IEnumerable<TransferMeterListItem>)?.ToList();

            if (isExport)
                return File(Util.Excel.GetExportFileStream(items), "application/octet-stream");
            else
                return Ok(items);
        }

        [Permission("Transfer Meter Map", PermissionType.View)]
        [Route("[action]")]
        public IQueryable<TransferMeterListItem> GetItemsInternal()
        {
            IQueryable<TransferMeterListItem>? itemsQueryable = null;

            itemsQueryable = (
                from map in db.TransferMeters
                join m1 in db.Meters on map.Meter1Id equals m1.Id
                join m2 in db.Meters on map.Meter2Id equals m2.Id
                join mp1 in DataHelper.RecentMeterProductsQueryable(db)
                    on new { MeterId = m1.Id, map.ProductId } equals new { mp1.MeterId, mp1.ProductId }
                join mp2 in DataHelper.RecentMeterProductsQueryable(db)
                    on new { MeterId = m2.Id, map.ProductId } equals new { mp2.MeterId, mp2.ProductId }
                join z1 in db.Zones on mp1.SourceZoneId equals z1.Id
                join z2 in db.Zones on mp2.SourceZoneId equals z2.Id
                join p1 in db.Pipelines on z1.PipelineId equals p1.Id
                join p2 in db.Pipelines on z2.PipelineId equals p2.Id
                orderby p1.Name, z1.Name, m1.Name
                select new TransferMeterListItem
                {
                    TransferMeterId = map.Id,
                    Meter1Name = m1.Name,
                    Meter2Name = m2.Name,
                    Zone1Name = z1.Name,
                    Zone2Name = z2.Name,
                    Pipe1Name = p1.Name ?? "",
                    Pipe2Name = p2.Name ?? "",
                    Product = map.Product.Name
                }
            ).AsNoTracking();

            return itemsQueryable;
        }

        [Permission("Transfer Meter Map", PermissionType.View)]
        [Route("[action]")]
        public async Task<IActionResult> GetRequiredData()
        {
            var hasModifyPermission = await authHelper.IsAuthorizedAsync(User, "Transfer Meter Map", PermissionType.Modify);
            var pipelines = (await GetPipelineDetails());
            var meters = await DataHelper.GetMetersByProductAsync(Enums.ProductCategory.All);
            var zones = await DataHelper.GetZonesAsync();
            var products = await DataHelper.GetProductsAsync();

            var result = new { hasModifyPermission, pipelines, meters, zones, products };
            return Ok(result);
        }

        private static async Task<List<PipelineDetail>> GetPipelineDetails()
        {
            var db = Main.CreateContext();

            var detailTemp = await (
                from q in db.Pipelines
                select new
                {
                    PipelineId = q.Id,
                    FullName = q.Name ?? "",
                    ShortName = q.PipeShort ?? "",
                    PipelineDescription = q.PipelineDescription ?? "",
                    ProductIds = q.ProductIds
                }
            ).AsNoTracking().ToListAsync();

            var detail = await Task.Run(() =>
            {
                return (
                    from x in detailTemp
                    let isInactive = x.FullName.ToLower().Contains("inactive")
                    orderby isInactive, x.FullName
                    select new PipelineDetail()
                    {
                        PipelineId = x.PipelineId,
                        FullName = x.FullName,
                        ShortName = x.ShortName,
                        PipelineDescription = x.PipelineDescription,
                        ProductIds = x.ProductIds == null ? new List<int>() : x.ProductIds.Split(",", StringSplitOptions.None).Select(int.Parse).ToList()
                    }
                ).ToList();
            });

            return detail;
        }

        [Permission("Transfer Meter Map", PermissionType.View)]
        [Route("[action]")]
        public async Task<IActionResult> GetDetail(int id)
        {
            var detail = await (
                from map in db.TransferMeters
                join m1 in db.Meters on map.Meter1Id equals m1.Id
                join m2 in db.Meters on map.Meter2Id equals m2.Id
                join mp1 in DataHelper.RecentMeterProductsQueryable(db)
                    on new { MeterId = m1.Id, map.ProductId } equals new { mp1.MeterId, mp1.ProductId }
                join mp2 in DataHelper.RecentMeterProductsQueryable(db)
                    on new { MeterId = m2.Id, map.ProductId } equals new { mp2.MeterId, mp2.ProductId }
                join z1 in db.Zones on mp1.SourceZoneId equals z1.Id
                join z2 in db.Zones on mp2.SourceZoneId equals z2.Id
                join p1 in db.Pipelines on z1.PipelineId equals p1.Id
                join p2 in db.Pipelines on z2.PipelineId equals p2.Id
                orderby p1.Name, z1.Name, m1.Name
                where map.Id == id
                select new TransferMeterDetail
                {
                    TransferMeterId = map.Id,
                    ProductId = map.ProductId,
                    Meter1Id = m1.Id,
                    Meter1Name = m1.Name,
                    Meter2Id = m2.Id,
                    Meter2Name = m2.Name,
                    Zone1Id = z1.Id,
                    Zone1Name = z1.Name,
                    Zone2Id = z2.Id,
                    Zone2Name = z2.Name,
                    Pipe1Id = p1.Id,
                    Pipe1Name = p1.Name ?? "",
                    Pipe2Id = p2.Id,
                    Pipe2Name = p2.Name ?? ""
                }
            ).AsNoTracking().FirstAsync();

            return Ok(detail);
        }

        public enum SaveType
        {
            New = 1,
            Normal = 2
        }

        [Permission("Transfer Meter Map", PermissionType.Modify)]
        [Route("[action]")]
        public async Task<IActionResult> SaveDetail(TransferMeterDetail detail, SaveType saveType)
        {
            int resultId = 0;

            await db.Database.CreateExecutionStrategy().Execute(async () =>
            {
                using var dbContextTransaction = await db.Database.BeginTransactionAsync();
                TransferMeter? dbItem = null;
                if (saveType != SaveType.New)
                {
                    dbItem = await (
                        from q in db.TransferMeters
                        where q.Id == detail.TransferMeterId
                        select q
                    ).FirstOrDefaultAsync();
                }

                if (dbItem == null) //if the item does not exist then add it
                {
                    dbItem = new TransferMeter();
                    db.TransferMeters.Add(dbItem);
                }
                else
                {
                    //remove existing items so that they get completely re-inserted
                }

                var d = detail;
                dbItem.ProductId = d.ProductId;
                dbItem.Meter1Id = d.Meter1Id;
                dbItem.Meter2Id = d.Meter2Id;

                await db.SaveChangesAsync();
                resultId = dbItem.Id;

                await dbContextTransaction.CommitAsync();
            });

            return Ok(resultId);
        }

        [Permission("Transfer Meter Map", PermissionType.Modify)]
        [Route("[action]/{id}")]
        public async Task<IActionResult> DeleteDetail(int id)
        {
            TransferMeter dbItem = await db.TransferMeters.Where(x => x.Id == id).FirstAsync();
            db.TransferMeters.Remove(dbItem);
            await db.SaveChangesAsync();

            return Ok();
        }
    }
}
