using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Logging;
using Sockeye.Api.ControllerHelpers;
using Sockeye.Biz;
using Sockeye.Models;
using System.ComponentModel.DataAnnotations;
using Microsoft.Extensions.Hosting;
using Sockeye.Util;
using System.Linq;
using System.IO;


namespace Sockeye.Api.Controllers
{

    /// <summary>
    /// Server state controller
    /// </summary>
    [ApiController]
    [ApiVersion("8.0")]
    [Route("api/v{version:apiVersion}/server-state")]
    [Produces("application/json")]
    [Authorize]
    public class ServerStateController : ControllerBase
    {
        private readonly AyContext ct;
        private readonly ILogger<ServerStateController> log;
        private readonly ApiServerState serverState;
        private readonly IHostApplicationLifetime _appLifetime;


        public ServerStateController(ILogger<ServerStateController> logger, ApiServerState apiServerState, AyContext dbcontext, IHostApplicationLifetime appLifetime)
        {
            ct = dbcontext;
            log = logger;
            serverState = apiServerState;
            _appLifetime = appLifetime;
        }


        /// <summary>
        /// Get server state 
        /// </summary>
        /// <returns>Current server state (Closed, MigrateMode, OpsOnly, Open)</returns>
        [HttpGet]
        public ActionResult Get()
        {
            //any logged in user can get the state
            return Ok(ApiOkResponse.Response(new ServerStateModel() { ServerState = serverState.GetState().ToString(), Reason = serverState.Reason }));
        }


        /// <summary>
        /// Set server state
        /// Valid parameters:
        /// One of "OpsOnly", "MigrateMode" or "Open"
        /// </summary>
        /// <param name="state">{"serverState":"Open"}</param>
        /// <returns>New server state</returns>
        [HttpPost]
        public async Task<IActionResult> PostServerState([FromBody] ServerStateModel state)
        {
            if (serverState.IsClosed)//no state change allowed when closed
                return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));

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

            if (!ModelState.IsValid)
                return BadRequest(new ApiErrorResponse(ModelState));

            ApiServerState.ServerState desiredState;
            if (!Enum.TryParse<ApiServerState.ServerState>(state.ServerState, true, out desiredState))
                return BadRequest(new ApiErrorResponse(ApiErrorCode.VALIDATION_INVALID_VALUE, null, "Invalid state -  must be one of \"OpsOnly\", \"MigrateMode\" or \"Open\""));

            //don't allow a server to be set to closed, that's for internal ops only
            if (desiredState == ApiServerState.ServerState.Closed)
                return BadRequest(new ApiErrorResponse(ApiErrorCode.VALIDATION_INVALID_VALUE, null, "Invalid state -  must be one of \"OpsOnly\", \"MigrateMode\" or \"Open\""));

            var TransId = UserTranslationIdFromContext.Id(HttpContext.Items);

            log.LogInformation($"ServerState change request by user {UserNameFromContext.Name(HttpContext.Items)} from current state of \"{serverState.GetState().ToString()}\" to \"{desiredState.ToString()} {state.Reason}\"");

          

            //Add a message if user didn't enter one so other users know why they can't login
            if (string.IsNullOrWhiteSpace(state.Reason))
            {
                switch (desiredState)
                {
                    case ApiServerState.ServerState.Open:
                        break;                  
                    case ApiServerState.ServerState.OpsOnly:
                        state.Reason += await TranslationBiz.GetTranslationStaticAsync("ServerStateLoginRestricted", TransId, ct) + ":\n" + await TranslationBiz.GetTranslationStaticAsync("ServerStateOps", TransId, ct);
                        break;
                    case ApiServerState.ServerState.Closed:
                        state.Reason += await TranslationBiz.GetTranslationStaticAsync("ServerStateLoginRestricted", TransId, ct) + ":\n" + await TranslationBiz.GetTranslationStaticAsync("ErrorAPI2000", TransId, ct);
                        break;
                }
            }
            else
            {
                if (desiredState != ApiServerState.ServerState.OpsOnly)
                    state.Reason = await TranslationBiz.GetTranslationStaticAsync("ServerStateLoginRestricted", TransId, ct) + ":\n" + state.Reason;
            }

            serverState.SetState(desiredState, state.Reason);

            //Log
            await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserIdFromContext.Id(HttpContext.Items), 0, SockType.ServerState, SockEvent.ServerStateChange, $"{state.ServerState}-{state.Reason}"), ct);

            return Ok(ApiOkResponse.Response(new ServerStateModel() { ServerState = serverState.GetState().ToString(), Reason = serverState.Reason }));

        }


        /// <summary>
        /// Parameter object
        /// </summary>
        public class ServerStateModel
        {
            /// <summary>
            /// "OpsOnly", "MigrateMode"  or "Open"
            /// </summary>
            /// <returns></returns>
            [Required]
            public string ServerState { get; set; }

            /// <summary>
            /// Reason for server state
            /// </summary>
            /// <returns></returns>
            public string Reason { get; set; }
        }



        /// <summary>
        /// Controlled server shut down
        /// </summary>
        /// <returns>Accepted</returns>
        [HttpPost("shutdown")]
        public ActionResult PostShutdown()
        {
            if (serverState.IsClosed)//no state change allowed when closed
                return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));

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

            log.LogInformation($"### Server shut down requested by user {UserNameFromContext.Name(HttpContext.Items)}, triggering shut down event now...");

            _appLifetime.StopApplication();//Note: this should also trigger graceful shutdown of JOBS as well
            return Accepted();

        }




        /// <summary>
        /// Get server configuration 
        /// </summary>
        /// <returns>Active server configuration</returns>
        [HttpGet("active-configuration")]
        public ActionResult GetActiveConfiguration()
        {
            if (!Authorized.HasReadFullRole(HttpContext.Items, SockType.ServerState))
                return StatusCode(403, new ApiNotAuthorizedResponse());


            return Ok(ApiOkResponse.Response(
                new
                {
                    SOCKEYE_DEFAULT_TRANSLATION = ServerBootConfig.SOCKEYE_DEFAULT_TRANSLATION,
                    SOCKEYE_USE_URLS = ServerBootConfig.SOCKEYE_USE_URLS,
                    SOCKEYE_DB_CONNECTION = DbUtil.PasswordRedactedConnectionString(ServerBootConfig.SOCKEYE_DB_CONNECTION),
                    SOCKEYE_REPORT_RENDERING_TIMEOUT = ServerBootConfig.SOCKEYE_REPORT_RENDERING_TIMEOUT,
                    SOCKEYE_ATTACHMENT_FILES_PATH = ServerBootConfig.SOCKEYE_ATTACHMENT_FILES_PATH,
                    SOCKEYE_BACKUP_FILES_PATH = ServerBootConfig.SOCKEYE_BACKUP_FILES_PATH,
                    SOCKEYE_TEMP_FILES_PATH = ServerBootConfig.SOCKEYE_TEMP_FILES_PATH,
                    SOCKEYE_BACKUP_PG_DUMP_PATH = ServerBootConfig.SOCKEYE_BACKUP_PG_DUMP_PATH,
                    SOCKEYE_LOG_PATH = ServerBootConfig.SOCKEYE_LOG_PATH,
                    SOCKEYE_LOG_LEVEL = ServerBootConfig.SOCKEYE_LOG_LEVEL,
                    SOCKEYE_LOG_ENABLE_LOGGER_DIAGNOSTIC_LOG = ServerBootConfig.SOCKEYE_LOG_ENABLE_LOGGER_DIAGNOSTIC_LOG,
                    serverinfo = ServerBootConfig.BOOT_DIAGNOSTIC_INFO,
                    dbserverinfo = ServerBootConfig.DBSERVER_DIAGNOSTIC_INFO
                }));
        }

        // /// <summary>
        // /// Download all logs and server configuration setting in one zip package for technical support purposes
        // /// </summary>
        // /// <param name="t">download token</param>
        // /// <returns>A single zip file</returns>
        // [AllowAnonymous]
        // [HttpGet("tech-support-info")]
        // public async Task<IActionResult> DownloadSupportInfo([FromQuery] string t)
        // {
        //     //NOTE: this route deliberately open even when server closed as a troubleshooting measure

        //     if (!ModelState.IsValid)
        //         return BadRequest(new ApiErrorResponse(ModelState));

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

        //     if (!Authorized.HasReadFullRole(user.Roles, SockType.LogFile) || !Authorized.HasReadFullRole(user.Roles, SockType.ServerState))
        //         return StatusCode(403, new ApiNotAuthorizedResponse());


        //     System.Text.StringBuilder sbRet = new System.Text.StringBuilder();
        //     sbRet.AppendLine("#########################################################");
        //     sbRet.AppendLine("SERVER CONFIGURATION");
        //     sbRet.AppendLine($"SOCKEYE_USE_URLS: {ServerBootConfig.SOCKEYE_USE_URLS}");
        //     sbRet.AppendLine($"SOCKEYE_DB_CONNECTION: {DbUtil.PasswordRedactedConnectionString(ServerBootConfig.SOCKEYE_DB_CONNECTION)}");
        //     sbRet.AppendLine($"SOCKEYE_REPORT_RENDERING_TIMEOUT: {ServerBootConfig.SOCKEYE_REPORT_RENDERING_TIMEOUT}");
        //     sbRet.AppendLine($"SOCKEYE_DEFAULT_TRANSLATION: {ServerBootConfig.SOCKEYE_DEFAULT_TRANSLATION}");
        //     sbRet.AppendLine($"SOCKEYE_ATTACHMENT_FILES_PATH: {ServerBootConfig.SOCKEYE_ATTACHMENT_FILES_PATH}");
        //     sbRet.AppendLine($"SOCKEYE_BACKUP_FILES_PATH: {ServerBootConfig.SOCKEYE_BACKUP_FILES_PATH}");
        //     sbRet.AppendLine($"SOCKEYE_TEMP_FILES_PATH: {ServerBootConfig.SOCKEYE_TEMP_FILES_PATH}");
        //     sbRet.AppendLine($"SOCKEYE_BACKUP_PG_DUMP_PATH: {ServerBootConfig.SOCKEYE_BACKUP_PG_DUMP_PATH}");
        //     sbRet.AppendLine($"SOCKEYE_LOG_PATH: {ServerBootConfig.SOCKEYE_LOG_PATH}");
        //     sbRet.AppendLine($"SOCKEYE_LOG_LEVEL: {ServerBootConfig.SOCKEYE_LOG_LEVEL}");
        //     sbRet.AppendLine($"SOCKEYE_LOG_ENABLE_LOGGER_DIAGNOSTIC_LOG: {ServerBootConfig.SOCKEYE_LOG_ENABLE_LOGGER_DIAGNOSTIC_LOG}");

        //     sbRet.AppendLine("#########################################################");
        //     sbRet.AppendLine("SERVER DIAGNOSTICS");
        //     foreach (var di in ServerBootConfig.BOOT_DIAGNOSTIC_INFO)
        //     {
        //         sbRet.AppendLine($"{di.Key}: {di.Value}");
        //     }

        //     sbRet.AppendLine("#########################################################");
        //     sbRet.AppendLine("DB SERVER DIAGNOSTICS");
        //     foreach (var di in ServerBootConfig.DBSERVER_DIAGNOSTIC_INFO)
        //     {
        //         sbRet.AppendLine($"{di.Key}: {di.Value}");
        //     }




        //     //System.IO.Compression.ZipFile.CreateFromDirectory(ServerBootConfig.SOCKEYE_LOG_PATH, AttachmentsBackupFile);


        //     var files = System.IO.Directory.GetFiles(ServerBootConfig.SOCKEYE_LOG_PATH, "*.txt").OrderBy(z => new FileInfo(z).LastWriteTime).Select(z => System.IO.Path.GetFileName(z)).ToList();

        //     //Directory.GetFiles(@“C:\RPA\Vector Reports”,“IW28*”).OrderByAscending(d => new FileInfo(d).GetLastWriteTime)

        //     sbRet.AppendLine("#########################################################");
        //     sbRet.AppendLine($"SERVER LOGS");

        //     foreach (string logfilename in files)
        //     {
        //         var logFilePath = System.IO.Path.Combine(ServerBootConfig.SOCKEYE_LOG_PATH, logfilename);
        //         FileStreamOptions fso = new FileStreamOptions();
        //         fso.Access = FileAccess.Read;
        //         fso.Mode = FileMode.Open;
        //         fso.Share = FileShare.ReadWrite;

        //         using (StreamReader sr = new StreamReader(logFilePath, fso))
        //         {
        //             sbRet.AppendLine(sr.ReadToEnd());
        //         }

        //     }
        //     sbRet.AppendLine("#########################################################");
        //     return Content(sbRet.ToString());


        // }


        /// <summary>
        /// Fetch all logs and server configuration setting for technical support purposes
        /// </summary>
        /// <returns>text result</returns>        
        [HttpGet("tech-support-info")]
        public ActionResult GetTechSupportInfo()
        {

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

            if (!ModelState.IsValid)
                return BadRequest(new ApiErrorResponse(ModelState));


            System.Text.StringBuilder sbRet = new System.Text.StringBuilder();
            sbRet.AppendLine("#########################################################");
            sbRet.AppendLine("SERVER CONFIGURATION");
            sbRet.AppendLine($"SOCKEYE_USE_URLS: {ServerBootConfig.SOCKEYE_USE_URLS}");
            sbRet.AppendLine($"SOCKEYE_DB_CONNECTION: {DbUtil.PasswordRedactedConnectionString(ServerBootConfig.SOCKEYE_DB_CONNECTION)}");
            sbRet.AppendLine($"SOCKEYE_REPORT_RENDERING_TIMEOUT: {ServerBootConfig.SOCKEYE_REPORT_RENDERING_TIMEOUT}");
            sbRet.AppendLine($"SOCKEYE_DEFAULT_TRANSLATION: {ServerBootConfig.SOCKEYE_DEFAULT_TRANSLATION}");
            sbRet.AppendLine($"SOCKEYE_ATTACHMENT_FILES_PATH: {ServerBootConfig.SOCKEYE_ATTACHMENT_FILES_PATH}");
            sbRet.AppendLine($"SOCKEYE_BACKUP_FILES_PATH: {ServerBootConfig.SOCKEYE_BACKUP_FILES_PATH}");
            sbRet.AppendLine($"SOCKEYE_TEMP_FILES_PATH: {ServerBootConfig.SOCKEYE_TEMP_FILES_PATH}");
            sbRet.AppendLine($"SOCKEYE_BACKUP_PG_DUMP_PATH: {ServerBootConfig.SOCKEYE_BACKUP_PG_DUMP_PATH}");
            sbRet.AppendLine($"SOCKEYE_LOG_PATH: {ServerBootConfig.SOCKEYE_LOG_PATH}");
            sbRet.AppendLine($"SOCKEYE_LOG_LEVEL: {ServerBootConfig.SOCKEYE_LOG_LEVEL}");
            sbRet.AppendLine($"SOCKEYE_LOG_ENABLE_LOGGER_DIAGNOSTIC_LOG: {ServerBootConfig.SOCKEYE_LOG_ENABLE_LOGGER_DIAGNOSTIC_LOG}");

            sbRet.AppendLine("#########################################################");
            sbRet.AppendLine("SERVER DIAGNOSTICS");
            sbRet.AppendLine($"Version: {SockeyeVersion.FullNameAndVersion}");
            sbRet.AppendLine($"Schema level: {AySchema.currentSchema}");
            sbRet.AppendLine($"Server current time: {DateUtil.ServerDateTimeString(System.DateTime.UtcNow)}");
           

            foreach (var di in ServerBootConfig.BOOT_DIAGNOSTIC_INFO)
            {
                sbRet.AppendLine($"{di.Key}: {di.Value}");
            }

            sbRet.AppendLine("#########################################################");
            sbRet.AppendLine("DB SERVER DIAGNOSTICS");
            foreach (var di in ServerBootConfig.DBSERVER_DIAGNOSTIC_INFO)
            {
                sbRet.AppendLine($"{di.Key}: {di.Value}");
            }


            var files = System.IO.Directory.GetFiles(ServerBootConfig.SOCKEYE_LOG_PATH, "*.txt").OrderBy(z => new FileInfo(z).LastWriteTime).Select(z => System.IO.Path.GetFileName(z)).ToList();


            sbRet.AppendLine("#########################################################");
            sbRet.AppendLine($"ALL SERVER LOGS");

            foreach (string logfilename in files)
            {
                var logFilePath = System.IO.Path.Combine(ServerBootConfig.SOCKEYE_LOG_PATH, logfilename);
                FileStreamOptions fso = new FileStreamOptions();
                fso.Access = FileAccess.Read;
                fso.Mode = FileMode.Open;
                fso.Share = FileShare.ReadWrite;

                using (StreamReader sr = new StreamReader(logFilePath, fso))
                {
                    sbRet.AppendLine(sr.ReadToEnd());
                }

            }
            sbRet.AppendLine("#########################################################");

            return Ok(ApiOkResponse.Response(sbRet.ToString()));


        }

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

    }
}