using Serilog;
using Serilog.Core;
using Serilog.Events;
using Microsoft.Extensions.Configuration;
using EvaPosSCOSrv;
using EvaPosSrvAplicacionImp;
using EvaPosSrvRespImp;
using gatewaySCO.POSBC;
using GatewayCHEC.Servidor;

namespace GatewaySCO
{

    public class TaskIdEnricher : ILogEventEnricher
    {
        public const string TaskIdPropertyName = "TaskId";

        public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
        {
            var taskId = Task.CurrentId?.ToString() ?? "no-task";
            logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty(TaskIdPropertyName, taskId));
        }
    }

    /// <summary>
    /// EvaPos-API : servidor api, sockets y rest.
    /// Usa Serilog para bitácora.
    /// </summary> 
    public class ProgramGatewaySCO
    {
        static void Main(string[] args)
        {
            Console.WriteLine("*** Gateway SCO - Servidor: procesa peticiones de Toshiba CHEC ***");
            ProgramGatewaySCO program = new();
            LeeActivaConfiguracion(args);
        }

        public static void LeeActivaConfiguracion(string[] args)
        {
            // TODO - opción de incluir la activación en la cadena de configuración.

            // Instancia objeto de configuración, usa proveedor json y variables ambiente.
            IConfigurationRoot configBuilder = new ConfigurationBuilder()
                .AddJsonFile("appsettings.json")
                .AddEnvironmentVariables()
                .Build();

            // TODO Sacar la ruta de logs del objeto de configuración adecuadamente.
            string? rutaLogs = configBuilder["Serilog:WriteTo:1:Args:path"];
            Console.WriteLine($"Archivo de logs en {rutaLogs}");

            // Instancia Serilog según parámetros en archivo de configuración.
            Log.Logger = new LoggerConfiguration()
                .ReadFrom.Configuration(configBuilder)
                .CreateLogger();

            // Lee del archivo de configuración la configuración general.
            Config config = configBuilder.GetRequiredSection("GatewayConfig").Get<Config>()
                ?? throw new ApplicationException("Archivo de configuración sin sección 'GatewayConfig'.");
            Log.Information(config.ToString());
            Entorno<Config>.Instancia.set(config);

            // // Lee del archivo de configuración sección POS Gk si aplica.
            // // TODO - valor de parámetro POS de archivo de configuración en ENUM?
            // if (config.POS == "gk")
            // {
            //     ConfigGk configGk = configBuilder.GetRequiredSection("DataGK").Get<ConfigGk>()
            //         ?? throw new ApplicationException("Archivo de configuración sin sección 'DataGK'.");
            //     Log.Debug(configGk.ToString());
            //     // Inicializa el entorno Gk.
            //     Entorno<EntornoGK>.Instancia.set(new EntornoGK());
            //     Entorno<EntornoGK>.Instancia.get().Language = config.Language;
            //     Entorno<EntornoGK>.Instancia.get().ConfigGk = configGk;
            //     Log.Information($"GK {Entorno<EntornoGK>.Instancia.get().UrlBase}");
            // }
            // // Configuración para pruebas de emulación del POSBC.
            // if (config.POS == "ECO_POSBC")
            // {
            //     ConfigPOSBC configPOSBC = configBuilder.GetRequiredSection("POSBC").Get<ConfigPOSBC>()
            //         ?? throw new ApplicationException("Archivo de configuración sin sección 'POSBC'.");
            //     Log.Information(configPOSBC.ToString());
            //     Entorno<EntornoPOSBC>.Instancia.set(new EntornoPOSBC());
            //     Entorno<EntornoPOSBC>.Instancia.get().IpPOSBC = configPOSBC.IpPOSBC;
            //     Entorno<EntornoPOSBC>.Instancia.get().PortPOSBC = configPOSBC.PortPOSBC;
            //     // Se activa lógica de cliente POSBC
            //     // La conexión con el POSBC se almacena en el entorno para uso
            //     // posterior en la emisión y recepción de mensajes.
            //     Entorno<EntornoPOSBC>.Instancia.get().ClientePOSBC = new ClienteServidorPOSBC(configPOSBC.IpPOSBC, configPOSBC.PortPOSBC);
            //     Entorno<EntornoPOSBC>.Instancia.get().ClientePOSBC.AbreConexion();
            //     if (Entorno<EntornoPOSBC>.Instancia.get().ClientePOSBC.ConexionActiva == false)
            //     {
            //         throw new ApplicationException("Error en conexión al POSBC.");
            //     }
            // }
            // Activa servidor Gateway. 
            // En este punto, el programa se bloquea esperando conexión y mensajes desde CHEC.

            switch (config.POS)
            {
                case "gk":
                    {
                        // Procesa sección DataGK del archivo de configuración.
                        ConfigGk configGk = configBuilder.GetRequiredSection("DataGK").Get<ConfigGk>()
                            ?? throw new ApplicationException("Archivo de configuración sin sección 'DataGK'.");
                        Log.Debug(configGk.ToString());
                        // Inicializa el entorno Gk.
                        Entorno<EntornoGK>.Instancia.set(new EntornoGK());
                        Entorno<EntornoGK>.Instancia.get().Language = config.Language;
                        Entorno<EntornoGK>.Instancia.get().ConfigGk = configGk;
                        Log.Information($"GK {Entorno<EntornoGK>.Instancia.get().UrlBase}");
                        break;
                    }
                case "ECO_POSBC":
                    {
                        // Procesa sección POSBC del archivo de configuración.
                        ConfigPOSBC configPOSBC = configBuilder.GetRequiredSection("POSBC").Get<ConfigPOSBC>()
                            ?? throw new ApplicationException("Archivo de configuración sin sección 'POSBC'.");
                        Log.Information(configPOSBC.ToString());
                        Entorno<EntornoPOSBC>.Instancia.set(new EntornoPOSBC());
                        Entorno<EntornoPOSBC>.Instancia.get().IpPOSBC = configPOSBC.IpPOSBC;
                        Entorno<EntornoPOSBC>.Instancia.get().PortPOSBC = configPOSBC.PortPOSBC;
                        // Activa cliente de conexión a POSBC. La conexión con el POSBC 
                        // se almacena en el entorno para uso posterior en la emisión y recepción de mensajes.
                        //Entorno<EntornoPOSBC>.Instancia.get().ClientePOSBC = new ClienteServidorPOSBC(configPOSBC.IpPOSBC, configPOSBC.PortPOSBC);
                        //Entorno<EntornoPOSBC>.Instancia.get().ClientePOSBC.AbreConexion();
                        // if (Entorno<EntornoPOSBC>.Instancia.get().ClientePOSBC.ConexionActiva == false)
                        // {
                        //     throw new ApplicationException("Error en conexión al POSBC.");
                        // }
                        break;
                    }
                default:
                    {
                        throw new ApplicationException($"Configuración POS desconocida: {config.POS}");
                    }
            }

            //ActivaServidor(config.IpGateway, config.PortGateway, config.POS);

            // Activa servidor Gateway.
            try
            {
                string ipPOSBC = Entorno<EntornoPOSBC>.Instancia.get().IpPOSBC;
                int ptoPOSBC = Entorno<EntornoPOSBC>.Instancia.get().PortPOSBC;
                ServidorGatewayCHEC gateway = new(config.IpGateway, config.PortGateway, ipPOSBC, ptoPOSBC);
                gateway.Activa();
            }
            catch (Exception e)
            {
                Log.Error("Excepción {e}", e.ToString);
                Environment.Exit(1);
            }
        }

        // Servidor sockets: acepta peticiones de SCO CHEC.
        public static void ActivaServidor(string ip, int pto, string tipoPOS)
        {
            try
            {
                // TODO - aunque hace uso de una "invocación fluida" no parece ser el tipo de api que se beneficia de ese esquema.
                ServidorSocket servidor = new ServidorSocket()
                    .ConIp(ip)
                    .EnPuerto(pto)
                    .AgregaDispensadorAdaptadores(IniciaDirectorioAdaptadores.Cargar().DirectorioAdaptadores)
                    .AgregaDirectorioCmds(DirectorioCmdsFactory.CreaDirectorio(tipoPOS))
                    .AgregaProcesadorAplicacion(new Aplicacion())
                    .Activa();
            }
            catch (Exception e)
            {
                Log.Error($"Excepción: {e}");
                Environment.Exit(1);
            }
        }
    }
}
