using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Sqlite;
using Microsoft.IdentityModel.Tokens;

using Serilog;

using Watcher.Data;
using Watcher.Models;


var builder = WebApplication.CreateBuilder(args);


// Serilog konfigurieren – nur Logs, die nicht von Microsoft stammen
Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Information()
    .MinimumLevel.Override("Microsoft", Serilog.Events.LogEventLevel.Warning) // <--
    .Enrich.FromLogContext()
    .WriteTo.File(
        "logs/watcher-.log",
        outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss}] [{Level:u3}] {Message:lj}{NewLine}{Exception}",
        rollingInterval: RollingInterval.Day
    )
    .CreateLogger();

builder.Host.UseSerilog();


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


// HttpContentAccessor
builder.Services.AddHttpContextAccessor();


// ---------- Konfiguration ----------
DotNetEnv.Env.Load();

builder.Configuration
    .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
    .AddEnvironmentVariables();


// Konfiguration laden
var configuration = builder.Configuration;


// ---------- DB-Kontext ----------
var dbProvider = configuration["Database:Provider"] ?? "MySQL";
var connectionString = configuration["Database:ConnectionString"];
builder.Services.AddDbContext<AppDbContext>((serviceProvider, options) =>
{
    var config = serviceProvider.GetRequiredService<IConfiguration>();
    var provider = dbProvider;

    if (provider == "MySql")
    {
        var connStr = config.GetConnectionString("MySql") ?? config["Database:ConnectionStrings:MySql"];
        options.UseMySql(connStr, ServerVersion.AutoDetect(connStr));
    }
    else if (provider == "Sqlite")
    {
        var connStr = config.GetConnectionString("Sqlite") ?? config["Database:ConnectionStrings:Sqlite"];
        options.UseSqlite(connStr);
    }
    else
    {
        throw new Exception("Unsupported database provider configured.");
    }
});


// ---------- Authentifizierung konfigurieren ----------
// PocketID nur konfigurieren, wenn aktiviert
var pocketIdSection = builder.Configuration.GetSection("Authentication:PocketID");
var pocketIdEnabled = pocketIdSection.GetValue<bool>("Enabled");


var auth = builder.Services.AddAuthentication("Cookies");
auth.AddCookie("Cookies", options =>
{
    options.LoginPath = "/Auth/Login";
    options.AccessDeniedPath = "/Auth/AccessDenied";
});


builder.Services.AddAuthentication()
.AddOpenIdConnect("oidc", options =>
{
    options.Authority = pocketIdSection["Authority"];
    options.ClientId = pocketIdSection["ClientId"];
    options.ClientSecret = pocketIdSection["ClientSecret"];
    options.ResponseType = "code";
    options.CallbackPath = pocketIdSection["CallbackPath"];
    options.SaveTokens = true;

    options.GetClaimsFromUserInfoEndpoint = true;

    options.Scope.Clear();
    options.Scope.Add("openid");
    options.Scope.Add("profile");
    options.Scope.Add("email");

    options.Events = new OpenIdConnectEvents
    {
        OnTokenValidated = async ctx =>
        {
            var db = ctx.HttpContext.RequestServices.GetRequiredService<AppDbContext>();

            var principal = ctx.Principal;
            var pocketId = principal.FindFirst("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier")?.Value;
            var preferredUsername = principal.FindFirst("preferred_username")?.Value;
            var email = principal.FindFirst("email")?.Value;

            if (string.IsNullOrEmpty(pocketId))
                return;

            var user = await db.Users.FirstOrDefaultAsync(u => u.OIDC_Id == pocketId);

            if (user == null)
            {
                user = new User
                {
                    OIDC_Id = pocketId,
                    Username = preferredUsername ?? "",
                    Email = email,
                    LastLogin = DateTime.UtcNow,
                    IdentityProvider = "oidc",
                    Password = string.Empty
                };
                db.Users.Add(user);
            }
            else
            {
                user.LastLogin = DateTime.UtcNow;
                user.Username = preferredUsername ?? user.Username;
                user.Email = email ?? user.Email;
                db.Users.Update(user);
            }

            await db.SaveChangesAsync();
        }
    };
});


var app = builder.Build();


// Migrationen anwenden (für SQLite oder andere DBs)
using (var scope = app.Services.CreateScope())
{
    var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
    db.Database.Migrate();
}


// Standart-User in Datenbank schreiben
using (var scope = app.Services.CreateScope())
{
    var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();

    Console.WriteLine("Checking for users...");

    if (!db.Users.Any())
    {
        Console.WriteLine("No users found, creating default user...");

        var defaultUser = new User
        {
            OIDC_Id = string.Empty,
            Username = "admin",
            Email = string.Empty,
            LastLogin = DateTime.UtcNow,
            IdentityProvider = "local",
            Password = BCrypt.Net.BCrypt.HashPassword("changeme")
        };
        db.Users.Add(defaultUser);
        db.SaveChanges();

        Console.WriteLine("Default user created.");
    }
    else
    {
        Console.WriteLine("Users already exist.");
    }
}


// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    // 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.UseHttpsRedirection();
app.UseRouting();

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

app.UseStaticFiles();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Auth}/{action=Login}/"
    );

app.Run();
