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

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

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

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

        [Permission("Point", PermissionType.View)]
        [Route("[action]")]
        public IQueryable<PointListItem> GetItemsInternal()
        {
            IQueryable<PointListItem>? itemsQueryable = null;

            itemsQueryable = (
                from pt in db.VwPointOverviewInfos
                select new PointListItem
                {
                    PointId = pt.PointId!.Value,
                    PointName = pt.PointName ?? "",
                    IsActive = pt.IsActive!.Value ? "yes" : "no",
                    PipeName = pt.PipeName == null ? "" : pt.PipeName ?? "",
                    Products = pt.Commodities ?? ""
                }
            ).AsNoTracking();

            return itemsQueryable;
        }

        [Permission("Point", PermissionType.View)]
        [Route("[action]")]
        public async Task<IActionResult> GetRequiredData()
        {
            var hasModifyPermission = await authHelper.IsAuthorizedAsync(User, "Point", PermissionType.Modify);

            var pipelines = (await DataHelper.GetPipelinesAsync(false)).Select(x => new IdName(x.PipeId, x.PipeName));
            var products = await DataHelper.GetProductsAsync();

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

        }

        [Permission("Point", PermissionType.View)]
        [Route("[action]")]
        public async Task<IActionResult> GetDetail(int id)
        {
            var detailTemp = await (
                from pt in db.Points
                where pt.Id == id
                select new
                {
                    PointId = pt.Id,
                    PointName = pt.Name ?? "",
                    pt.IsActive,
                    PipelineId = pt.Pipeline == null ? 0 : pt.Pipeline.Id,
                    Description = pt.PointDescription ?? "",
                    pt.ProductIds
                }
            ).AsNoTracking().FirstAsync();

            var detail = new PointDetail()
            {
                PointId = detailTemp.PointId,
                PointName = detailTemp.PointName,
                IsActive = detailTemp.IsActive,
                PipelineId = detailTemp.PipelineId,
                Description = detailTemp.Description,
                ProductIds = detailTemp.ProductIds == null ? new List<int>() : detailTemp.ProductIds.Split(",", StringSplitOptions.None).Select(x => int.Parse(x)).ToList()
            };

            return Ok(detail);
        }

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

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

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

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

                var d = detail;
                dbItem.ClientId = 4;
                dbItem.Name = d.PointName;
                dbItem.PipelineId = d.PipelineId;
                dbItem.IsActive = d.IsActive;
                dbItem.PointDescription = d.Description;
                dbItem.ProductIds = String.Join(",", d.ProductIds.Select(x => x.ToString()).ToList());

                var pipeProductIds = db.Pipelines.Where(x => x.Id == d.PipelineId).Select(x => x.ProductIds).First();
                if (!string.IsNullOrWhiteSpace(pipeProductIds))
                {
                    var pipeProductIdsList = pipeProductIds.Split(",", StringSplitOptions.None).Select(x => int.Parse(x)).ToList();
                    var pointProductIdsList = d.ProductIds;
                    var pointProductIdsListNotInPipe = pointProductIdsList.Where(x => !pipeProductIdsList.Contains(x)).ToList();
                    if (pointProductIdsListNotInPipe.Count > 0)
                    {
                        var productName = db.Products.Where(x => x.Id == pointProductIdsListNotInPipe.First()).Select(x => x.Name).First();
                        throw new Exception($"Product {{{productName}}} is not available for this pipeline.  Please remove this product or add it on the pipeline screen.");
                    }
                    else
                    {
                        await db.SaveChangesAsync();
                        resultId = dbItem.Id;
                        await dbContextTransaction.CommitAsync();
                    }
                }
                else
                    throw new Exception("Pipeline does not have any products.");
            });

            return Ok(resultId);
        }

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

            return Ok();
        }
    }
}
