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

        public ProducerFeeController(MyDbContext context, IWebHostEnvironment env)
        {
            db = context;
            authHelper = new AuthorizationHelper(Main.IsAuthenticationEnabled);
        }

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

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

        [Permission("Producer Fees", PermissionType.View)]
        [Route("[action]")]
        public IQueryable<ProducerFeeListItem> GetItemsInternal(DateTime? asOfDate)
        {
            var asOfDateOnly = DateOnly.FromDateTime(asOfDate ?? DateTime.Today);

            IQueryable<ProducerFeeListItem>? itemsQueryable = null;

            if (asOfDate == null)
            {
                itemsQueryable = (
                    from q in db.ProducerFees
                    select new ProducerFeeListItem
                    {
                        Id = q.Id,
                        ProducerName = q.Producer.Name,
                        EffectiveDate = q.EffectiveDate,
                        BaseFee = q.BaseFee,
                        IncFee = q.IncFee,
                        FeeTypeId = q.FeeTypeId,
                        FeeTypeName = q.FeeType.Name,
                        ContractDate = q.ContractDate
                    }
                ).AsNoTracking();
            }
            else
            {
                var maxEffectives = (
                    from q in db.ProducerFees
                    where q.EffectiveDate <= asOfDateOnly
                    group q by q.ProducerId into g
                    select new { ProducerId = g.Key, MaxEffectiveDate = g.Max(x => x.EffectiveDate) }
                ).AsNoTracking();

                itemsQueryable = (
                    from q in db.ProducerFees
                    join max in maxEffectives on new { q.ProducerId, MaxEffectiveDate = q.EffectiveDate } equals new { max.ProducerId, max.MaxEffectiveDate }
                    select new ProducerFeeListItem
                    {
                        Id = q.Id,
                        ProducerName = q.Producer.Name,
                        EffectiveDate = max.MaxEffectiveDate,
                        BaseFee = q.BaseFee,
                        IncFee = q.IncFee,
                        FeeTypeId = q.FeeTypeId,
                        FeeTypeName = q.FeeType.Name,
                        ContractDate = q.ContractDate
                    }
                ).AsNoTracking();
            }

            return itemsQueryable;
        }

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

            var producers = (await DataHelper.GetProducersAsync(false));
            var feeTypes = await (from q in db.ProducerFeeTypes orderby q.Id select new IdName(q.Id, q.Name)).ToListAsync();

            var result = new { hasModifyPermission, producers, feeTypes };
            return Ok(result);
        }

        [Permission("Producer Fees", PermissionType.View)]
        [Route("[action]")]
        public async Task<IActionResult> GetDetail(int id)
        {
            var detail = await (
                from q in db.ProducerFees
                where q.Id == id
                select new ProducerFeeDetail
                {
                    Id = q.Id,
                    ProducerId = q.ProducerId,
                    BaseFee = q.BaseFee,
                    IncFee = q.IncFee,
                    FeeTypeId = q.FeeTypeId,
                    EffectiveDate = q.EffectiveDate,
                    ContractDate = q.ContractDate
                }
            ).AsNoTracking().FirstAsync();

            return Ok(detail);
        }

        [Permission("Producer Fees", PermissionType.Modify)]
        [Route("[action]")]
        public async Task<IActionResult> SaveDetail(ProducerFeeDetail detail, Enums.SaveType SaveType)
        {
            DateOnly noEndDateOnly = new(9999, 12, 31);
            int resultId = 0;

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

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

                var d = detail;

                dbItem.ProducerId = d.ProducerId;
                dbItem.BaseFee = d.BaseFee;
                dbItem.IncFee = d.IncFee;
                dbItem.FeeTypeId = d.FeeTypeId;
                dbItem.EffectiveDate = d.EffectiveDate ?? noEndDateOnly;
                dbItem.ContractDate = d.ContractDate == null ? noEndDateOnly : d.ContractDate.Value;

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

                await dbContextTransaction.CommitAsync();
            });

            return Ok(resultId);
        }

        [Permission("Producer Fees", PermissionType.Modify)]
        [Route("[action]/{id}")]
        public IActionResult DeleteDetail(int id)
        {
            ProducerFee dbItem = db.ProducerFees.Where(x => x.Id == id).First();
            db.ProducerFees.Remove(dbItem);
            db.SaveChanges();

            return Ok();
        }
    }
}
