using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Logging;
using Sockeye.Models;
using Sockeye.Api.ControllerHelpers;
using Sockeye.Biz;
using Sockeye.Util;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.IO;
using System;

namespace Sockeye.Api.Controllers
{
    [ApiController]
    [ApiVersion("8.0")]
    [Route("api/v{version:apiVersion}/export")]
    [Produces("application/json")]
    [Authorize]
    public class ExportController : ControllerBase
    {
        private readonly AyContext ct;
        private readonly ILogger<ExportController> log;
        private readonly ApiServerState serverState;

        /// <summary>
        /// ctor
        /// </summary>
        /// <param name="dbcontext"></param>
        /// <param name="logger"></param>
        /// <param name="apiServerState"></param>  
        public ExportController(AyContext dbcontext, ILogger<ExportController> logger, ApiServerState apiServerState)
        {
            ct = dbcontext;
            log = logger;
            serverState = apiServerState;
        }


        /// <summary>
        /// Export to file
        /// </summary>
        /// <param name="selectedRequest"></param>
        /// <returns>downloadable export file name</returns>
        [HttpPost("render")]
        public async Task<IActionResult> RenderExport([FromBody] DataListSelectedRequest selectedRequest)
        {

            if (!serverState.IsOpen)
                return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
            if (!ModelState.IsValid)
                return BadRequest(new ApiErrorResponse(ModelState));

            if (selectedRequest == null)
                return BadRequest(new ApiErrorResponse(ApiErrorCode.VALIDATION_REQUIRED, null, "DataListSelectedRequest is required"));

            if (!Authorized.HasReadFullRole(HttpContext.Items, selectedRequest.SockType))
                return StatusCode(403, new ApiNotAuthorizedResponse());


            var UserId = UserIdFromContext.Id(HttpContext.Items);
            var UserRoles = UserRolesFromContext.Roles(HttpContext.Items);
            var UserTranslationId = UserTranslationIdFromContext.Id(HttpContext.Items);

            //Rehydrate id list if necessary
            if (selectedRequest.SelectedRowIds.Length == 0)
                selectedRequest.SelectedRowIds = await DataListSelectedProcessingOptions.RehydrateIdList(
                    selectedRequest,
                    ct,
                    UserRoles,
                    log,
                    UserId,
                    UserTranslationId);


            log.LogDebug($"Instantiating biz object handler for {selectedRequest.SockType}");
            var biz = BizObjectFactory.GetBizObject(selectedRequest.SockType, ct, UserId, UserRoles, UserTranslationId);
            log.LogDebug($"Fetching data for {selectedRequest.SelectedRowIds.Length} {selectedRequest.SockType} items");
            string baseFileName = FileUtil.StringToSafeFileName($"{selectedRequest.SockType.ToString().ToLowerInvariant()}-{FileUtil.GetSafeDateFileName()}");
            string outputSourceFileName = baseFileName + ".json";
            string outputSourceFullPath = System.IO.Path.Combine(FileUtil.TemporaryFilesFolder, outputSourceFileName);

            log.LogDebug($"Calling render export data to file {outputSourceFullPath}");
            try
            {
                using (StreamWriter file = System.IO.File.CreateText(outputSourceFullPath))
                using (JsonTextWriter writer = new JsonTextWriter(file))
                {
                    var dat = await ((IExportAbleObject)biz).GetExportData(selectedRequest, Guid.Empty);//todo: jobify
                    dat.WriteTo(writer);
                }

                log.LogDebug($"Completed, returning results");
                return Ok(ApiOkResponse.Response(outputSourceFileName));
            }
            catch (ReportRenderTimeOutException)
            {
                log.LogInformation($"RenderExport timeout data list key: {selectedRequest.DataListKey}, record count:{selectedRequest.SelectedRowIds.LongLength}, user:{UserNameFromContext.Name(HttpContext.Items)} ");
                return BadRequest(new ApiErrorResponse(ApiErrorCode.INVALID_OPERATION, null, "timeout - select fewer records"));
            }
        }

        public static IList<dynamic> ToDynamicList(JArray data)
        {
            var dynamicData = new List<dynamic>();
            var expConverter = new Newtonsoft.Json.Converters.ExpandoObjectConverter();

            foreach (var dataItem in data)
            {
                dynamic obj = JsonConvert.DeserializeObject<System.Dynamic.ExpandoObject>(dataItem.ToString(), expConverter);
                dynamicData.Add(obj);
            }
            return dynamicData;
        }

        /// <summary>
        /// Download a rendered Export
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="t">download token</param>
        /// <returns></returns>
        [HttpGet("download/{fileName}")]
        [AllowAnonymous]
        public async Task<IActionResult> DownloadAsync([FromRoute] string fileName, [FromQuery] string t)
        {
            if (!serverState.IsOpen)
                return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));

            if (await UserBiz.ValidateDownloadTokenAndReturnUserAsync(t, ct) == null)
            {
                await Task.Delay(ServerBootConfig.FAILED_AUTH_DELAY);//DOS protection
                return StatusCode(401, new ApiErrorResponse(ApiErrorCode.AUTHENTICATION_FAILED));
            }

            if (!FileUtil.TemporaryFileExists(fileName))
            {
                await Task.Delay(ServerBootConfig.FAILED_AUTH_DELAY);//fishing protection
                return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
            }

            var FilePath = FileUtil.GetFullPathForTemporaryFile(fileName);

            //including the file name triggers save automatically "attachment" rather than viewing it "inline"
            return PhysicalFile(FilePath, "application/json", fileName);
        }



        //-----------------------------------------





    }//eoc
}//eons