using Data;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.EntityFrameworkCore;
using MudBlazor.Services;
using Serilog;
using System.Text.Json;
using WebApp;
using WebApp.Authentication;
using WebApp.Components;
using WebApp.Logging;

var builder = WebApplication.CreateBuilder(args);

// Load optional appsettings from Data directory in production (overrides defaults)
// In development, use appsettings.Development.json instead
if (builder.Environment.IsProduction())
{
    var dataAppSettingsPath = Path.Combine(builder.Environment.ContentRootPath, "Data", "appsettings.json");
    if (!File.Exists(dataAppSettingsPath))
    {
        Console.WriteLine($"appsettings.json not found at {dataAppSettingsPath}. Creating from template...");

        try
        {
            // Ensure Data directory exists
            Directory.CreateDirectory(Path.Combine(builder.Environment.ContentRootPath, "Data"));

            // Copy ChapterSettings from the base appsettings.json
            var baseAppSettingsPath = Path.Combine(builder.Environment.ContentRootPath, "appsettings.json");
            if (File.Exists(baseAppSettingsPath))
            {
                var baseConfig = File.ReadAllText(baseAppSettingsPath);
                var baseDoc = JsonDocument.Parse(baseConfig);

                if (baseDoc.RootElement.TryGetProperty("ChapterSettings", out var chapterSettings))
                {
                    var templateSettings = new
                    {
                        ChapterSettings = JsonSerializer.Deserialize<object>(chapterSettings.GetRawText())
                    };

                    var json = JsonSerializer.Serialize(templateSettings, new JsonSerializerOptions
                    {
                        WriteIndented = true
                    });

                    File.WriteAllText(dataAppSettingsPath, json);
                    Console.WriteLine("appsettings.json created in Data directory. Please update ChapterSettings with your chapter information.");
                }
                else
                {
                    Console.WriteLine("WARNING: ChapterSettings not found in base appsettings.json");
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"WARNING: Unable to create appsettings.json: {ex.Message}");
        }
    }

    // Add Data/appsettings.json as additional configuration source
    if (File.Exists(dataAppSettingsPath))
    {
        builder.Configuration.AddJsonFile(dataAppSettingsPath, optional: true, reloadOnChange: true);
        Console.WriteLine($"Loaded configuration from {dataAppSettingsPath}");
    }
}

// Configure Serilog with custom handling for antiforgery errors
builder.Host.UseSerilog((context, configuration) =>
{
    var consoleOutputTemplate = "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} {Properties:j}{NewLine}{Exception}";
    var fileOutputTemplate = "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj} {Properties:j}{NewLine}{Exception}";

    // Create console logger with antiforgery error handling
    var consoleConfig = new LoggerConfiguration()
        .WriteTo.Console(outputTemplate: consoleOutputTemplate);
    var consoleLogger = consoleConfig.CreateLogger();

    // Create file logger with antiforgery error handling
    var fileConfig = new LoggerConfiguration()
        .WriteTo.File(
            path: "logs/webapp-.txt",
            rollingInterval: RollingInterval.Day,
            retainedFileCountLimit: 30,
            outputTemplate: fileOutputTemplate);
    var fileLogger = fileConfig.CreateLogger();

    configuration
        .ReadFrom.Configuration(context.Configuration)
        .Enrich.FromLogContext()
        .WriteTo.Sink(new AntiforgeryLogEventSink(consoleLogger))
        .WriteTo.Sink(new AntiforgeryLogEventSink(fileLogger));
});

// Configure authentication secrets for production (Docker, etc.)
if (builder.Environment.IsProduction())
{
    // Option 1: Load from volume-mounted secrets file in Data directory
    var secretsPath = Path.Combine(builder.Environment.ContentRootPath, "Data", "auth-secrets.json");
    if (File.Exists(secretsPath))
    {
        builder.Configuration.AddJsonFile(secretsPath, optional: false, reloadOnChange: true);
    }

    // Option 2: Environment variables with prefix
    builder.Configuration.AddEnvironmentVariables(prefix: "TSA_");
}

// Add services to the container.
builder.Services.AddControllersWithViews();
builder.Services.AddRazorComponents()
    .AddInteractiveServerComponents();

builder.Services.AddMudServices();

// Configure Data Protection to persist keys across container restarts
var keysPath = Path.Combine(builder.Environment.ContentRootPath, "DataProtectionKeys");

// Try to create the directory if it doesn't exist
try
{
    if (!Directory.Exists(keysPath))
    {
        Directory.CreateDirectory(keysPath);
        Console.WriteLine($"Created DataProtectionKeys directory at: {keysPath}");
    }
}
catch (UnauthorizedAccessException ex)
{
    Console.WriteLine($"WARNING: Unable to create DataProtectionKeys directory at {keysPath}");
    Console.WriteLine($"Error: {ex.Message}");
    Console.WriteLine("This may cause issues with antiforgery tokens and authentication cookies.");
    Console.WriteLine("Ensure the volume mount has correct permissions for the container user.");
}

builder.Services.AddDataProtection()
    .PersistKeysToFileSystem(new DirectoryInfo(keysPath))
    .SetApplicationName("TSA-Chapter-Organizer");

// Ensure Data directory exists
var dataPath = Path.Combine(builder.Environment.ContentRootPath, "Data");
try
{
    if (!Directory.Exists(dataPath))
    {
        Directory.CreateDirectory(dataPath);
        Console.WriteLine($"Created Data directory at: {dataPath}");
    }
}
catch (Exception ex)
{
    Console.WriteLine($"WARNING: Unable to create Data directory at {dataPath}: {ex.Message}");
}

// Configure SQLite with hardcoded connection string
var connectionString = "Data Source=Data/app.db";
builder.Services.AddDbContext<AppDbContext>(options =>
{
    options.UseSqlite(connectionString,
        sqliteOptions => sqliteOptions.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery));
});

builder.Services.AddQuickGridEntityFrameworkAdapter();

builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddScoped<ClipboardService>();
builder.Services.AddScoped<WebApp.LocalStorageService>();

// State container for maintaining state per user connection (Blazor Server)
builder.Services.AddScoped<StateContainer>();

// Add authentication services
builder.Services.AddHttpContextAccessor();
builder.Services.AddScoped<AuthenticationService>();
builder.Services.AddSingleton<LoginRateLimitService>();
builder.Services.AddHostedService<LoginRateLimitService>(sp => sp.GetRequiredService<LoginRateLimitService>()); 

// Add authentication options
builder.Services.AddAuthentication("Auth")
    .AddCookie("Auth", options =>
    {
        options.ExpireTimeSpan = TimeSpan.FromMinutes(20);
        options.SlidingExpiration = true;
        options.LoginPath = "/login";

        // Enhanced security settings
        options.Cookie.HttpOnly = true;
        options.Cookie.SecurePolicy = builder.Environment.IsDevelopment()
            ? CookieSecurePolicy.SameAsRequest
            : CookieSecurePolicy.Always;
        options.Cookie.SameSite = SameSiteMode.Strict;
        options.Cookie.Name = "TSA.Auth";
    });
builder.Services.AddCascadingAuthenticationState();

var app = builder.Build();

// Check and apply database migrations
using (var scope = app.Services.CreateScope())
{
    var services = scope.ServiceProvider;
    var logger = services.GetRequiredService<ILogger<Program>>();

    try
    {
        var context = services.GetRequiredService<AppDbContext>();

        // Check for pending migrations
        var pendingMigrations = await context.Database.GetPendingMigrationsAsync();

        if (pendingMigrations.Any())
        {
            logger.LogInformation(
                "Applying database migrations: {Migrations}",
                string.Join(", ", pendingMigrations));
            await context.Database.MigrateAsync();
            logger.LogInformation("Database migrations applied successfully");
        }
        else
        {
            logger.LogInformation("Database is up to date");
        }
    }
    catch (Exception ex)
    {
        logger.LogError(ex, "An error occurred while checking database migrations");
        throw;
    }
}

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error", createScopeForErrors: true);
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
    app.UseMigrationsEndPoint();
}
else
{
    // Only redirect to HTTPS in development (production should use reverse proxy)
    app.UseHttpsRedirection();
}
app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

app.UseStaticFiles();
app.UseAntiforgery();

app.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode();

// Used for AuthController
app.MapControllerRoute("default", "{controller}/{action}");

// Development-only password hash generator endpoint
if (app.Environment.IsDevelopment())
{
    app.MapGet("/dev/hash-password", (string password) =>
    {
        var hash = PasswordHashGenerator.GenerateHash(password);
        return Results.Ok(new
        {
            password,
            hash,
            message = "Copy the hash value to your User Secrets configuration"
        });
    }).WithName("GeneratePasswordHash");
}

app.Run();
