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


namespace AyaNova.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, 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" 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, AyaType.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\" 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\" or \"Open\""));

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

            serverState.SetState(desiredState, state.Reason);

            //Log
            await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserIdFromContext.Id(HttpContext.Items), 0, AyaType.ServerState, AyaEvent.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" 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, AyaType.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();
            return Accepted();

        }




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


            return Ok(ApiOkResponse.Response(
                new
                {
                    AYANOVA_DEFAULT_TRANSLATION = ServerBootConfig.AYANOVA_DEFAULT_TRANSLATION,
                    AYANOVA_USE_URLS = ServerBootConfig.AYANOVA_USE_URLS,
                    AYANOVA_DB_CONNECTION = ServerBootConfig.AYANOVA_DB_CONNECTION,
                    AYANOVA_FOLDER_USER_FILES = ServerBootConfig.AYANOVA_FOLDER_USER_FILES,
                    AYANOVA_FOLDER_BACKUP_FILES = ServerBootConfig.AYANOVA_FOLDER_BACKUP_FILES,
                    AYANOVA_FOLDER_TEMPORARY_SERVER_FILES = ServerBootConfig.AYANOVA_FOLDER_TEMPORARY_SERVER_FILES,
                    AYANOVA_BACKUP_PG_DUMP_PATH = ServerBootConfig.AYANOVA_BACKUP_PG_DUMP_PATH,
                    AYANOVA_LOG_PATH = ServerBootConfig.AYANOVA_LOG_PATH,
                    AYANOVA_LOG_LEVEL = ServerBootConfig.AYANOVA_LOG_LEVEL,
                    AYANOVA_LOG_ENABLE_LOGGER_DIAGNOSTIC_LOG = ServerBootConfig.AYANOVA_LOG_ENABLE_LOGGER_DIAGNOSTIC_LOG
                }));
        }


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

    }
}