using AspNet.Security.OAuth.GitHub;
using Hangfire;
using Hangfire.Mongo;
using Hangfire.Mongo.Migration.Strategies;
using Hangfire.Mongo.Migration.Strategies.Backup;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.Google;
using Microsoft.AspNetCore.Authentication.MicrosoftAccount;
using MongoDB.Driver;
using Nalu.Jobs;
using Nalu.Web.Data;
using Nalu.Web.Data.Repositories;
using Nalu.Web.Endpoints;
using Stripe;
using Nalu.Web.Enrichers;
using Nalu.Web.Infrastructure;
using Nalu.Web.Mcp;
using Nalu.Web.PostProcessors;
using Nalu.Web.Services;
using Nalu.Web.Services.LlmRouter;
using Scalar.AspNetCore;

var builder = WebApplication.CreateBuilder(args);

// ── Razor Pages (site) ───────────────────────────────────────────────────────
builder.Services.AddRazorPages();

// ── OpenAPI / Scalar ──────────────────────────────────────────────────────────
builder.Services.AddOpenApi("v1", options =>
{
    options.AddDocumentTransformer((doc, _, _) =>
    {
        doc.Info.Title = "NALU AI API";
        doc.Info.Version = "v1";
        doc.Info.Description = "Natural Language Understanding — extrai intenções reais de diálogos agente/usuário.";
        return Task.CompletedTask;
    });
});

// ── MongoDB ───────────────────────────────────────────────────────────────────
builder.Services.AddSingleton<IMongoClient>(sp =>
{
    var connStr = builder.Configuration.GetConnectionString("MongoDB") ?? string.Empty;
    if (string.IsNullOrWhiteSpace(connStr)) return null!;

    var settings = MongoClientSettings.FromConnectionString(
        connStr + (connStr.Contains('?') ? "&" : "?") +
        "maxPoolSize=200&minPoolSize=10&maxIdleTimeMS=30000");

    return new MongoClient(settings);
});

builder.Services.AddSingleton<MongoDbContext>();

// ── Hangfire ──────────────────────────────────────────────────────────────────
var hangfireConnStr = builder.Configuration.GetConnectionString("MongoDB") ?? string.Empty;
if (!string.IsNullOrWhiteSpace(hangfireConnStr))
{
    builder.Services.AddHangfire(cfg => cfg
        .SetDataCompatibilityLevel(CompatibilityLevel.Version_180)
        .UseSimpleAssemblyNameTypeSerializer()
        .UseRecommendedSerializerSettings()
        .UseMongoStorage(hangfireConnStr, new MongoStorageOptions
        {
            Prefix          = "hangfire_",
            CheckConnection = false,
            MigrationOptions = new MongoMigrationOptions
            {
                MigrationStrategy = new MigrateMongoMigrationStrategy(),
                BackupStrategy    = new CollectionMongoBackupStrategy()
            }
        }));

    builder.Services.AddHangfireServer(opts => opts.WorkerCount = 1);
}

// Jobs
builder.Services.AddSingleton<INameImporterJob, NaluNameImporterJob>();

// ── Repositories ──────────────────────────────────────────────────────────────
builder.Services.AddSingleton<ApiKeyRepository>();
builder.Services.AddSingleton<UserRepository>();
builder.Services.AddSingleton<UsageRepository>();
builder.Services.AddSingleton<SubscriptionRepository>();
builder.Services.AddSingleton<WebhookEventRepository>();

// ── Authentication ───────────────────────────────────────────────────────────
// Site = Cookie (Razor Pages). API = Bearer API key (/v1/*). DO NOT mix.
builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
    options.LoginPath = "/login";
    options.LogoutPath = "/auth/logout";
    options.Cookie.HttpOnly = true;
    options.Cookie.SameSite = SameSiteMode.Lax;
    options.ExpireTimeSpan = TimeSpan.FromDays(30);
    options.SlidingExpiration = true;
})
// Temporary cookie for external OAuth handshake
.AddCookie("ExternalCookie", options =>
{
    options.Cookie.Name = "nalu.external";
    options.ExpireTimeSpan = TimeSpan.FromMinutes(10);
})
// API key scheme — used explicitly by /v1/* endpoints
.AddScheme<AuthenticationSchemeOptions, NaluAuthHandler>(ApiKeyAuthScheme.Name, null)
.AddGoogle(GoogleDefaults.AuthenticationScheme, options =>
{
    options.ClientId = builder.Configuration["OAuth:Google:ClientId"] ?? string.Empty;
    options.ClientSecret = builder.Configuration["OAuth:Google:ClientSecret"] ?? string.Empty;
    options.CallbackPath = "/signin-google";
    options.SignInScheme = "ExternalCookie";
    options.SaveTokens = false;
})
.AddMicrosoftAccount(MicrosoftAccountDefaults.AuthenticationScheme, options =>
{
    options.ClientId = builder.Configuration["OAuth:Microsoft:ClientId"] ?? string.Empty;
    options.ClientSecret = builder.Configuration["OAuth:Microsoft:ClientSecret"] ?? string.Empty;
    options.CallbackPath = "/signin-microsoft";
    options.SignInScheme = "ExternalCookie";
    options.SaveTokens = false;
})
.AddGitHub(GitHubAuthenticationDefaults.AuthenticationScheme, options =>
{
    options.ClientId = builder.Configuration["OAuth:GitHub:ClientId"] ?? string.Empty;
    options.ClientSecret = builder.Configuration["OAuth:GitHub:ClientSecret"] ?? string.Empty;
    options.CallbackPath = "/signin-github";
    options.SignInScheme = "ExternalCookie";
    options.Scope.Add("user:email");
    options.SaveTokens = false;
});

builder.Services.AddAuthorization(options =>
{
    // API endpoints — must present a valid Bearer API key
    options.AddPolicy("ApiKey", policy =>
        policy.AddAuthenticationSchemes(ApiKeyAuthScheme.Name)
              .RequireAuthenticatedUser());
});

// ── Session (required for OAuth state) ───────────────────────────────────────
builder.Services.AddDistributedMemoryCache();
builder.Services.AddSession(options =>
{
    options.Cookie.HttpOnly = true;
    options.Cookie.IsEssential = true;
    options.IdleTimeout = TimeSpan.FromMinutes(10);
});

// ── Caching ──────────────────────────────────────────────────────────────────
builder.Services.AddMemoryCache();

// ── HTTP clients ─────────────────────────────────────────────────────────────
builder.Services.AddHttpClient<GroqClient>(client =>
{
    var baseUrl = builder.Configuration["Groq:BaseUrl"] ?? "https://api.groq.com/openai/v1";
    client.BaseAddress = new Uri(baseUrl.TrimEnd('/') + '/');
    client.DefaultRequestHeaders.Add(
        "Authorization",
        $"Bearer {builder.Configuration["Groq:ApiKey"]}");
    client.Timeout = TimeSpan.FromSeconds(30);
});

// ── LLM Router (Groq → OpenRouter → Google AI) ───────────────────────────────
builder.Services.AddHttpClient<GroqProvider>(client =>
{
    var baseUrl = builder.Configuration["Groq:BaseUrl"] ?? "https://api.groq.com/openai/v1";
    client.BaseAddress = new Uri(baseUrl.TrimEnd('/') + '/');
    client.DefaultRequestHeaders.Add("Authorization", $"Bearer {builder.Configuration["Groq:ApiKey"]}");
    client.Timeout = TimeSpan.FromSeconds(30);
});

builder.Services.AddHttpClient<OpenRouterProvider>(client =>
{
    var baseUrl = builder.Configuration["OpenRouter:BaseUrl"] ?? "https://openrouter.ai/api/v1";
    client.BaseAddress = new Uri(baseUrl.TrimEnd('/') + '/');
    client.DefaultRequestHeaders.Add("Authorization", $"Bearer {builder.Configuration["OpenRouter:ApiKey"]}");
    client.DefaultRequestHeaders.Add("HTTP-Referer", "https://naluai.dev");
    client.DefaultRequestHeaders.Add("X-Title", "NALU AI");
    client.Timeout = TimeSpan.FromSeconds(30);
});

builder.Services.AddHttpClient<GoogleAiProvider>(client =>
{
    var baseUrl = builder.Configuration["GoogleAi:BaseUrl"] ?? "https://generativelanguage.googleapis.com/v1beta/openai/";
    client.BaseAddress = new Uri(baseUrl.TrimEnd('/') + '/');
    client.DefaultRequestHeaders.Add("Authorization", $"Bearer {builder.Configuration["GoogleAi:ApiKey"]}");
    client.Timeout = TimeSpan.FromSeconds(30);
});

builder.Services.AddSingleton<ILlmRouter>(sp =>
{
    var providers = new List<ILlmProvider>
    {
        sp.GetRequiredService<GroqProvider>(),
        sp.GetRequiredService<OpenRouterProvider>(),
        sp.GetRequiredService<GoogleAiProvider>()
    };
    return new LlmRouter(providers, sp.GetRequiredService<ILogger<LlmRouter>>());
});

// ViaCEP enricher — no fixed base address (calls multiple URLs)
builder.Services.AddHttpClient<ViaCepEnricher>(client =>
{
    client.Timeout = TimeSpan.FromSeconds(10);
    client.DefaultRequestHeaders.Add("User-Agent", "nalu-ai/1.0");
});
builder.Services.AddTransient<IEnricher>(sp => sp.GetRequiredService<ViaCepEnricher>());

// ── Post-processors ───────────────────────────────────────────────────────────
builder.Services.AddSingleton<IPostProcessor, CapitalizeProperName>();
builder.Services.AddSingleton<IPostProcessor, RemoveTitles>();
builder.Services.AddSingleton<IPostProcessor, ValidateCpfDigit>();
builder.Services.AddSingleton<IPostProcessor, FormatPhone>();
builder.Services.AddSingleton<IPostProcessor, FormatCep>();
builder.Services.AddSingleton<IPostProcessor, CorrectEmailTypos>();
builder.Services.AddSingleton<IPostProcessor, NormalizePostalCode>();
builder.Services.AddSingleton<IPostProcessor, ParseDate>();
builder.Services.AddSingleton<IPostProcessor, CalculateAge>();
builder.Services.AddSingleton<IPostProcessor, ValidateCnpjDigit>();
builder.Services.AddSingleton<IPostProcessor, FormatPlate>();
builder.Services.AddSingleton<IPostProcessor, SelectHandoffSuggestion>();
builder.Services.AddSingleton<IPostProcessor, SelectCancelSuggestion>();

// ── Domain services ──────────────────────────────────────────────────────────
builder.Services.AddScoped<UserService>();
builder.Services.AddScoped<SmartSuggestionService>();
builder.Services.AddSingleton<SuspiciousRateLimiter>();
builder.Services.AddHttpContextAccessor();
builder.Services.AddSingleton<ValidatorLoader>();
builder.Services.AddSingleton<NameLookupService>();
builder.Services.AddSingleton<DeterministicLayer>();
builder.Services.AddSingleton<PostProcessorRegistry>();
builder.Services.AddSingleton<SuggestionBuilder>();
builder.Services.AddSingleton<CacheService>();
builder.Services.AddSingleton<AuthService>();
builder.Services.AddSingleton<RateLimitService>();
builder.Services.AddSingleton<CreditService>();
// EnrichmentService is Scoped because ViaCepEnricher is Transient (HttpClient lifecycle)
builder.Services.AddScoped<EnrichmentService>();
builder.Services.AddScoped<LlmExtractionService>();
builder.Services.AddScoped<ExtractionPipeline>();
builder.Services.AddScoped<ReplyService>();

// ── MCP server ────────────────────────────────────────────────────────────────
builder.Services.AddSingleton<McpServer>();

// ── App ───────────────────────────────────────────────────────────────────────
// ── Stripe ────────────────────────────────────────────────────────────────────
StripeConfiguration.ApiKey = builder.Configuration["Stripe:SecretKey"];

var app = builder.Build();

// Initialize MongoDB indexes on startup
var mongo = app.Services.GetRequiredService<MongoDbContext>();
await mongo.InitializeAsync();

// ── Hangfire dashboard + recurring jobs ───────────────────────────────────────
if (!string.IsNullOrWhiteSpace(hangfireConnStr))
{
    app.UseHangfireDashboard("/hangfire", new DashboardOptions
    {
        Authorization = [new HangfireDashboardAuth()]
    });

    var nameCron = app.Configuration["Hangfire:NameImporterCron"] ?? "0 3 1 * *";
    RecurringJob.AddOrUpdate<INameImporterJob>(
        "nalu-name-importer",
        job => job.ExecuteAsync(false),
        nameCron,
        new RecurringJobOptions { TimeZone = TimeZoneInfo.Utc });
}

app.UseStaticFiles();
app.UseSession();
app.UseAuthentication();
app.UseAuthorization();

app.MapOpenApi();
app.MapScalarApiReference(options =>
{
    options.Title = "NALU AI";
    options.DefaultHttpClient = new(ScalarTarget.Http, ScalarClient.HttpClient);
});

app.MapRazorPages();
app.MapExtractEndpoints();
app.MapValidatorsEndpoints();
app.MapPlaygroundEndpoints();
app.MapStripeEndpoints();

// ── Health check ──────────────────────────────────────────────────────────────
app.MapGet("/health", () => Results.Ok(new { status = "ok", ts = DateTime.UtcNow }))
    .WithTags("Health")
    .WithSummary("Health check")
    .AllowAnonymous();

// ── MCP endpoint ──────────────────────────────────────────────────────────────
app.MapPost("/mcp", async (HttpContext ctx, McpServer mcp, CancellationToken ct) =>
    await mcp.HandleAsync(ctx, ct))
    .RequireAuthorization("ApiKey")
    .WithName("McpEndpoint")
    .WithSummary("MCP Server (JSON-RPC 2.0 / Streamable HTTP)")
    .WithTags("MCP")
    .WithOpenApi();

app.Run();

// Exposed for WebApplicationFactory in integration tests
public partial class Program { }
