using System.Text; using Mercados.Infrastructure; using Mercados.Infrastructure.DataFetchers; using Mercados.Infrastructure.Persistence; using Mercados.Infrastructure.Persistence.Repositories; using Mercados.Worker; using Polly; using Polly.Extensions.Http; // Carga las variables de entorno desde el archivo .env en la raíz de la solución. DotNetEnv.Env.Load(); Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); // --- Configuración del Host --- // Esto prepara el host del servicio, permitiendo la inyección de dependencias, // la configuración desde appsettings.json y el logging. IHost host = Host.CreateDefaultBuilder(args) .ConfigureServices((hostContext, services) => { // Obtenemos la configuración desde el host builder para usarla aquí. IConfiguration configuration = hostContext.Configuration; // --- 1. Registro de Servicios de Infraestructura --- // Registramos la fábrica de conexiones a la BD. Es un Singleton porque // solo necesita ser creada una vez para leer la cadena de conexión. services.AddSingleton(); // Registramos los repositorios. Se crean "por petición" (Scoped). // En un worker, "Scoped" significa que se creará una instancia por cada // ejecución del servicio, lo cual es seguro y eficiente. services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); // --- 2. Registro de los Data Fetchers --- // Registramos CADA uno de nuestros fetchers. El contenedor de DI sabrá // que todos implementan la interfaz IDataFetcher. services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); // El cliente HTTP es fundamental para hacer llamadas a APIs externas. // Le damos un nombre al cliente de Finnhub para cumplir con los requisitos de su constructor. //services.AddHttpClient("Finnhub"); // Configuramos CADA cliente HTTP que nuestros fetchers usan. // IHttpClientFactory nos permite nombrar y configurar clientes de forma independiente. // Cliente para el scraper del MercadoAgro, con una política de reintentos services.AddHttpClient("MercadoAgroFetcher") .AddPolicyHandler(GetRetryPolicy()); // Cliente para la API de BCR, con la misma política de reintentos services.AddHttpClient("BcrDataFetcher") .AddPolicyHandler(GetRetryPolicy()); // Cliente para Finnhub, con la misma política de reintentos services.AddHttpClient("FinnhubDataFetcher") .AddPolicyHandler(GetRetryPolicy()); // Cliente para YahooFinance (aunque es menos probable que falle, es buena práctica incluirlo) // La librería YahooFinanceApi usa su propio HttpClient, así que esta configuración // no le afectará directamente. La resiliencia para YahooFinance la manejaremos de otra forma si es necesario. // Por ahora, lo dejamos así y nos enfocamos en los que usan IHttpClientFactory. // --- 3. Registro del Worker Principal --- // Finalmente, registramos nuestro servicio de fondo (el worker en sí). services.AddHostedService(); }) .Build(); // Esta función define nuestra política de reintentos. static IAsyncPolicy GetRetryPolicy() { // Polly.Extensions.Http nos da este método conveniente. return HttpPolicyExtensions // Maneja errores de red transitorios O códigos de estado de servidor que indican un problema temporal. .HandleTransientHttpError() // También maneja el error 408 Request Timeout .OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.RequestTimeout) // Política de reintento con espera exponencial: 3 reintentos, esperando 2^intento segundos. .WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), onRetry: (outcome, timespan, retryAttempt, context) => { // Registramos un log cada vez que se realiza un reintento. // Esta es una forma de hacerlo sin tener acceso directo al ILogger aquí. Console.WriteLine($"[Polly] Reintentando petición... Intento {retryAttempt}. Esperando {timespan.TotalSeconds}s. Causa: {outcome.Exception?.Message ?? outcome.Result.ReasonPhrase}"); }); } await host.RunAsync();