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

        public PlantStatementOptionsController(MyDbContext context, IConfiguration configuration)
        {
            db = context;
            authHelper = new AuthorizationHelper(Main.IsAuthenticationEnabled);
        }

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

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

        [Permission("Plant Statement Options", PermissionType.View)]
        [Route("[action]")]
        public IQueryable<PlantStatementOptionsListItem> GetItemsInternal()
        {
            IQueryable<PlantStatementOptionsListItem>? itemsQueryable = null;

            itemsQueryable = (
                from q in db.PlantStatementOptions
                select new PlantStatementOptionsListItem
                {
                    PlantStatementOptionsId = q.Id,
                    PlantId = q.PlantId,
                    Plant = q.Plant.Name,
                    PayoutType = q.PayoutType == null ? null : q.PayoutType.Name,
                    PayoutTypeId = q.PayoutTypeId,
                    RequiredFields = q.RequiredFields
                }
            ).AsNoTracking();

            return itemsQueryable;
        }

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

            var plants = await (from q in db.Plants orderby q.Name select new IdName(q.Id, q.Name)).ToListAsync();
            var payoutTypes = await (from q in db.PayoutTypes orderby q.Id select new IdName(q.Id, q.Name)).ToListAsync();
            var fieldNames = new List<string>();
            var props = typeof(PlantStatement).GetProperties();
            foreach (var propInfo in props)
            {
                var isNullable = Nullable.GetUnderlyingType(propInfo.PropertyType) != null;
                if (isNullable)
                {
                    var fieldName = Util.String.FirstCharToUpper(propInfo.Name);
                    fieldName = GetRequiredFieldDisplayFromFieldName(fieldName);
                    fieldNames.Add(fieldName);
                }
            }

            var requiredFields = fieldNames;
            var result = new { hasModifyPermission, plants, payoutTypes, requiredFields };
            return Ok(result);
        }

        private static readonly Dictionary<string, string> requiredFieldsDisplayDic = new(StringComparer.OrdinalIgnoreCase)
        {
            { "Meter", "meterId" },
            { "Prices", "pricesId" },
            { "PayoutType", "payoutTypeId" },
            { "ProcessorOrInletPipe", "statementDescriptorId" }
        };

        public static string GetRequiredFieldNameFromDisplay(string displayName)
        {
            if (requiredFieldsDisplayDic.ContainsKey(displayName))
                return requiredFieldsDisplayDic[displayName];
            else
                return displayName;
        }

        public static string GetRequiredFieldDisplayFromFieldName(string fieldName)
        {
            string? displayName = requiredFieldsDisplayDic.FirstOrDefault(x => x.Value.ToLower() == fieldName.ToLower()).Key;
            if (displayName == null)
                return fieldName;
            else
                return displayName;
        }

        [Permission("Plant Statement Options", PermissionType.View)]
        [Route("[action]")]
        public async Task<IActionResult> GetDetail(int id)
        {
            var dbItem = await (
                from q in db.PlantStatementOptions
                where q.Id == id
                select new
                {
                    PlantStatementOptionsId = q.Id,
                    PlantId = q.PlantId,
                    PayoutTypeId = q.PayoutTypeId,
                    RequiredFields = q.RequiredFields
                }
            ).AsNoTracking().FirstAsync();

            var detail = new PlantStatementOptionsDetail
            {
                PlantstatementoptionsId = dbItem.PlantStatementOptionsId,
                PlantId = dbItem.PlantId,
                PayoutTypeId = dbItem.PayoutTypeId,
                RequiredFields = dbItem.RequiredFields?.Split(',').Where(x => !string.IsNullOrWhiteSpace(x)).ToList()
            };

            return Ok(detail);
        }

        [Permission("Plant Statement Options", PermissionType.Modify)]
        [Route("[action]")]
        public async Task<IActionResult> SaveDetail(PlantStatementOptionsDetail detail, Enums.SaveType saveType)
        {
            string? requiredFields = null;
            if (detail.RequiredFields != null && detail.RequiredFields.Count > 0)
                requiredFields = string.Join(",", detail.RequiredFields).Trim(',');

            int resultId = 0;
            await db.Database.CreateExecutionStrategy().Execute(async () =>
            {
                using var dbContextTransaction = await db.Database.BeginTransactionAsync();
                PlantStatementOption? dbItem = null;
                if (saveType != Enums.SaveType.New)
                {
                    dbItem = await (
                        from q in db.PlantStatementOptions
                        where q.Id == detail.PlantstatementoptionsId
                        select q
                    ).FirstOrDefaultAsync();
                }

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

                var d = detail;
                dbItem.PlantId = d.PlantId;
                dbItem.PayoutTypeId = d.PayoutTypeId;
                dbItem.RequiredFields = requiredFields;

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

                await dbContextTransaction.CommitAsync();
            });

            return Ok(resultId);
        }

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

            return Ok();
        }
    }
}
