using System.Reflection;
using Extrudex.API.Filters;
using Extrudex.API.Hubs;
using Extrudex.API.Jobs;
using Extrudex.Domain.Interfaces;
using Extrudex.Infrastructure.Configuration;
using Extrudex.Infrastructure.Data;
using Extrudex.Infrastructure.Services;
using FluentValidation;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

// ── Database ───────────────────────────────────────────────
// Connection string resolution (highest priority first):
//   1. EXTRUDEX_DB_CONNECTION_STRING env var (Docker / production)
//   2. Individual env vars: EXTRUDEX_DB_HOST, EXTRUDEX_DB_PORT, etc.
//   3. appsettings.json ConnectionStrings:ExtrudexDb
//   4. Hardcoded default for local dev
var connectionString = Environment.GetEnvironmentVariable("EXTRUDEX_DB_CONNECTION_STRING")
    ?? BuildConnectionStringFromEnvVars()
    ?? builder.Configuration.GetConnectionString("ExtrudexDb")
    ?? "Host=localhost;Port=5432;Database=extrudex;Username=extrudex;Password=changeme";

builder.Services.AddDbContext<ExtrudexDbContext>(options =>
    options.UseNpgsql(connectionString));

// ── API Services ───────────────────────────────────────────
builder.Services.AddControllers(options =>
{
    options.Filters.AddService<FluentValidationFilter>();
});
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new()
    {
        Title = "Extrudex API",
        Version = "v1",
        Description = "Filament inventory and print tracking system"
    });

    // Include XML doc comments in Swagger output
    var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
    var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
    if (File.Exists(xmlPath))
    {
        c.IncludeXmlComments(xmlPath);
    }
});

// ── QR Code Generation ──────────────────────────────────────
builder.Services.AddSingleton<IQrCodeService, QrCodeService>();

// ── Cost Per Print Calculation ─────────────────────────────
builder.Services.AddScoped<ICostPerPrintService, CostPerPrintService>();

// ── Low Stock Detection ────────────────────────────────────
builder.Services.AddSingleton<ILowStockDetector, LowStockDetector>();

// ── Usage Logging ───────────────────────────────────────────
builder.Services.AddScoped<IUsageLogService, UsageLogService>();

// ── Filament Usage Service ──────────────────────────────────
builder.Services.AddScoped<IFilamentUsageService, FilamentUsageService>();

// ── Moonraker Usage Poller (Background Service) ─────────────
builder.Services.Configure<MoonrakerPollerOptions>(
    builder.Configuration.GetSection("MoonrakerPoller"));
builder.Services.AddHostedService<MoonrakerUsagePoller>();

// ── FluentValidation ──────────────────────────────────────
// Registers all validators from the API assembly into DI.
builder.Services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly());

// Register the FluentValidation action filter so validators run automatically
// on all API controller actions before the action executes.
builder.Services.AddScoped<FluentValidationFilter>();

// ── CORS (kiosk + remote browser) ─────────────────────────
// AllowAnyOrigin disallows credentials by spec; this is fine for
// REST API calls. SignalR WebSockets negotiate without credentials
// by default, so no special CORS policy is needed. If browser clients
// require credentials (cookies, auth headers), replace AllowAnyOrigin
// with .WithOrigins(...) and add .AllowCredentials().
builder.Services.AddCors(options =>
{
    options.AddDefaultPolicy(policy =>
    {
        policy.AllowAnyOrigin()
              .AllowAnyMethod()
              .AllowAnyHeader();
    });
});

// ── SignalR (real-time printer updates) ────────────────────
builder.Services.AddSignalR();

// ── Filament Usage Sync (Background Job) ──────────────────
builder.Services.Configure<FilamentUsageSyncOptions>(
    builder.Configuration.GetSection(FilamentUsageSyncOptions.SectionName));
builder.Services.AddHttpClient<IMoonrakerClient, MoonrakerClient>(client =>
{
    client.DefaultRequestHeaders.Add("User-Agent", "Extrudex/1.0");
});
builder.Services.AddScoped<IFilamentUsageSyncService, FilamentUsageSyncService>();
builder.Services.AddHostedService<FilamentUsageSyncJob>();

// ── Moonraker Printer Sync (Background Service) ──────────
builder.Services.Configure<MoonrakerPrinterSyncOptions>(
    builder.Configuration.GetSection(MoonrakerPrinterSyncOptions.SectionName));
builder.Services.AddScoped<IMoonrakerPrinterSyncService, MoonrakerPrinterSyncService>();
builder.Services.AddHostedService<MoonrakerPrinterSyncJob>();

// ── Health Checks ───────────────────────────────────────────
builder.Services.AddHealthChecks()
    .AddNpgSql(connectionString);

var app = builder.Build();

// ── Middleware ──────────────────────────────────────────────
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseCors();
app.UseAuthorization();
app.MapControllers();

// ── Hub Endpoints ───────────────────────────────────────────
app.MapHub<PrinterHub>("/hubs/printer");

// ── Health Check Endpoint ──────────────────────────────────
app.MapHealthChecks("/health");

app.Run();

// Helper: builds a connection string from individual env vars.
// Returns null if EXTRUDEX_DB_HOST is not set.
static string? BuildConnectionStringFromEnvVars()
{
    var host = Environment.GetEnvironmentVariable("EXTRUDEX_DB_HOST");
    if (string.IsNullOrEmpty(host)) return null;

    var port = Environment.GetEnvironmentVariable("EXTRUDEX_DB_PORT") ?? "5432";
    var database = Environment.GetEnvironmentVariable("EXTRUDEX_DB_NAME") ?? "extrudex";
    var username = Environment.GetEnvironmentVariable("EXTRUDEX_DB_USER") ?? "extrudex";
    var password = Environment.GetEnvironmentVariable("EXTRUDEX_DB_PASSWORD") ?? "changeme";

    return $"Host={host};Port={port};Database={database};Username={username};Password={password}";
}