using System.Security.Claims;
using System.Threading.RateLimiting;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using MudBlazor.Services;
using timetracker.Server.Components;
using timetracker.Data;
using timetracker.Shared;

var builder = WebApplication.CreateBuilder(args);

// Add Authentication
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(options =>
    {
        options.LoginPath = "/login";
        options.LogoutPath = "/auth/logout";
        options.ExpireTimeSpan = TimeSpan.FromDays(30);
        options.SlidingExpiration = true;
        options.Cookie.HttpOnly = true;
        options.Cookie.SameSite = SameSiteMode.Strict;
        options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
        // Cookie-Konfiguration für APIs & WASM
        options.Events.OnRedirectToLogin = context =>
        {
            if (context.Request.Path.StartsWithSegments("/api"))
            {
                context.Response.StatusCode = StatusCodes.Status401Unauthorized;
            }
            else
            {
                context.Response.Redirect(context.RedirectUri);
            }
            return Task.CompletedTask;
        };
    });

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("AdminOnly", policy =>
        policy.RequireClaim(ClaimTypes.Name, "marc"));
});

builder.Services.AddCascadingAuthenticationState();
builder.Services.AddHttpContextAccessor();

builder.Services.AddTransient<ITenantProvider, TenantProvider>();

// Add SignalR
builder.Services.AddSignalR();
builder.Services.AddSingleton<UserNotificationService>();
builder.Services.AddSingleton<IUserNotificationService>(sp => sp.GetRequiredService<UserNotificationService>());

// Register DB-backed services as the interfaces
builder.Services.AddScoped<IAuthService, AuthService>();
builder.Services.AddScoped<ITimetrackerService, TimetrackerService>();
builder.Services.AddScoped<IHolidayService, HolidayService>();

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

builder.Services.AddMudServices();
builder.Services.AddHttpClient<HolidayService>();
builder.Services.AddScoped(sp =>
{
    try
    {
        var navigationManager = sp.GetService<Microsoft.AspNetCore.Components.NavigationManager>();
        if (navigationManager != null && !string.IsNullOrEmpty(navigationManager.BaseUri))
        {
            return new System.Net.Http.HttpClient { BaseAddress = new Uri(navigationManager.BaseUri) };
        }
    }
    catch (InvalidOperationException)
    {
        // NavigationManager is registered but not initialized in this request scope
    }
    
    var httpContextAccessor = sp.GetService<Microsoft.AspNetCore.Http.IHttpContextAccessor>();
    var request = httpContextAccessor?.HttpContext?.Request;
    if (request != null)
    {
        var baseUri = $"{request.Scheme}://{request.Host}{request.PathBase}/";
        return new System.Net.Http.HttpClient { BaseAddress = new Uri(baseUri) };
    }
    
    return new System.Net.Http.HttpClient();
});

builder.Services.AddRateLimiter(options =>
{
    options.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
    options.AddPolicy("auth-limit", httpContext =>
        RateLimitPartition.GetFixedWindowLimiter(
            partitionKey: httpContext.Connection.RemoteIpAddress?.ToString() 
                ?? httpContext.Request.Headers["X-Forwarded-For"].ToString() 
                ?? "unknown",
            factory: _ => new FixedWindowRateLimiterOptions
            {
                Window = TimeSpan.FromMinutes(1),
                PermitLimit = 5,
                QueueLimit = 0
            }));
});

var dbProvider = builder.Configuration["DB_PROVIDER"] ?? "SQLite";

if (dbProvider.Equals("PostgreSQL", StringComparison.OrdinalIgnoreCase))
{
    var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") 
        ?? builder.Configuration["ConnectionStrings:DefaultConnection"];
    builder.Services.AddDbContextFactory<TimetrackerDbContext>(options =>
        options.UseNpgsql(connectionString)
               .ConfigureWarnings(w => w.Ignore(Microsoft.EntityFrameworkCore.Diagnostics.RelationalEventId.PendingModelChangesWarning)));
}
else
{
    var dbPath = Environment.GetEnvironmentVariable("TIMETRACKER_DB_PATH")
        ?? Path.Combine(builder.Environment.ContentRootPath, "timetracker.db");
    builder.Services.AddDbContextFactory<TimetrackerDbContext>(options =>
        options.UseSqlite($"Data Source={dbPath}")
               .ConfigureWarnings(w => w.Ignore(Microsoft.EntityFrameworkCore.Diagnostics.RelationalEventId.PendingModelChangesWarning)));
}

var app = builder.Build();

// Migrate or Ensure Database Created
using (var scope = app.Services.CreateScope())
{
    var factory = scope.ServiceProvider.GetRequiredService<IDbContextFactory<TimetrackerDbContext>>();
    await using var db = await factory.CreateDbContextAsync();
    await db.Database.MigrateAsync();
}

var forwardedHeadersOptions = new ForwardedHeadersOptions
{
    ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
};
forwardedHeadersOptions.KnownProxies.Clear();
forwardedHeadersOptions.KnownIPNetworks.Clear();
app.UseForwardedHeaders(forwardedHeadersOptions);

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseWebAssemblyDebugging();
}
else
{
    app.UseExceptionHandler("/Error", createScopeForErrors: true);
    app.UseHsts();
}

app.UseStatusCodePagesWithReExecute("/not-found", createScopeForStatusCodePages: true);
if (app.Configuration.GetValue("EnableHttpsRedirect", !app.Environment.IsDevelopment()))
{
    app.UseHttpsRedirection();
}

app.UseStaticFiles();
app.UseRateLimiter();
app.UseAuthentication();
app.UseAuthorization();
app.UseAntiforgery();

app.MapStaticAssets();

// Map Hub
app.MapHub<NotificationHub>("/hubs/notifications");

// Map Blazor WASM
app.MapRazorComponents<App>()
    .AddInteractiveWebAssemblyRenderMode()
    .AddAdditionalAssemblies(typeof(timetracker.Client._Imports).Assembly);

// ── Auth-API-Endpoints ────────────────────────────────────────────────────────
app.MapGet("/api/auth/me", (HttpContext ctx) =>
{
    if (ctx.User.Identity?.IsAuthenticated == true)
    {
        var idClaim = ctx.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        var name = ctx.User.Identity.Name ?? "";
        var isTenantAdmin = ctx.User.FindFirst("IsTenantAdmin")?.Value == "true";
        if (int.TryParse(idClaim, out var id))
        {
            return Results.Ok(new UserInfo { Id = id, Username = name, IsTenantAdmin = isTenantAdmin });
        }
    }
    return Results.Unauthorized();
});

app.MapPost("/api/auth/login", async (HttpContext ctx, [FromBody] LoginRequest req, IAuthService authService, ITenantProvider tenantProvider) =>
{
    var tenant = await tenantProvider.GetCurrentTenantAsync();
    if (tenant != null && !tenant.IsApproved)
    {
        return Results.BadRequest("Diese Firma ist noch nicht freigeschaltet.");
    }

    var user = await authService.LoginAsync(req.Username, req.Password);
    if (user == null)
        return Results.BadRequest("Benutzername oder Passwort falsch.");

    if (tenant != null)
    {
        if (user.TenantId != tenant.Id)
        {
            return Results.BadRequest("Benutzer gehört nicht zu dieser Firma.");
        }
    }
    else
    {
        if (user.Username != "marc" && user.TenantId != null)
        {
            return Results.BadRequest("Benutzer gehört zu einer Firma. Bitte melden Sie sich über Ihre Firmen-Subdomain an.");
        }
    }

    var claims = new List<Claim> {
        new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
        new Claim(ClaimTypes.Name, user.Username),
        new Claim("IsTenantAdmin", user.IsTenantAdmin ? "true" : "false")
    };
    if (user.TenantId.HasValue)
    {
        claims.Add(new Claim("TenantId", user.TenantId.Value.ToString()));
    }

    var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
    await ctx.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme,
        new ClaimsPrincipal(identity),
        new AuthenticationProperties { IsPersistent = true });

    return Results.Ok(new UserInfo { Id = user.Id, Username = user.Username, IsTenantAdmin = user.IsTenantAdmin });
}).RequireRateLimiting("auth-limit");

app.MapPost("/api/auth/register", async (HttpContext ctx, [FromBody] RegisterRequest req, IAuthService authService, ITenantProvider tenantProvider) =>
{
    if (!string.IsNullOrEmpty(req.Honeypot))
    {
        return Results.BadRequest("Registrierung fehlgeschlagen.");
    }

    var tenant = await tenantProvider.GetCurrentTenantAsync();
    if (tenant != null && !tenant.IsApproved)
    {
        return Results.BadRequest("Registrierung blockiert: Firma noch nicht freigeschaltet.");
    }

    var (user, error) = await authService.RegisterAsync(req.Username, req.Password);
    if (user == null)
        return Results.BadRequest(error ?? "Registrierung fehlgeschlagen.");

    var claims = new List<Claim> {
        new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
        new Claim(ClaimTypes.Name, user.Username),
        new Claim("IsTenantAdmin", user.IsTenantAdmin ? "true" : "false")
    };
    if (user.TenantId.HasValue)
    {
        claims.Add(new Claim("TenantId", user.TenantId.Value.ToString()));
    }

    var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
    await ctx.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme,
        new ClaimsPrincipal(identity),
        new AuthenticationProperties { IsPersistent = true });

    return Results.Ok(new UserInfo { Id = user.Id, Username = user.Username, IsTenantAdmin = user.IsTenantAdmin });
}).RequireRateLimiting("auth-limit");

app.MapGet("/auth/logout", async (HttpContext ctx) =>
{
    await ctx.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
    return Results.Redirect("/login");
});

// ── Admin-API-Endpoints (Protected) ───────────────────────────────────────────
var usersApi = app.MapGroup("/api/users").RequireAuthorization("AdminOnly");

usersApi.MapGet("/", async (IAuthService authService) =>
{
    var users = await authService.GetAllUsersAsync();
    return Results.Ok(users);
});

usersApi.MapDelete("/{userId:int}", async (int userId, IAuthService authService) =>
{
    await authService.DeleteUserAsync(userId);
    return Results.NoContent();
});

usersApi.MapPut("/{userId:int}/rename", async (int userId, [FromBody] RenameRequest req, IAuthService authService) =>
{
    var error = await authService.RenameUserAsync(userId, req.Username);
    if (error != null)
    {
        return Results.BadRequest(error);
    }
    return Results.Ok();
});

usersApi.MapPut("/{userId:int}/change-password", async (int userId, [FromBody] ChangePasswordRequest req, IAuthService authService) =>
{
    var error = await authService.ChangePasswordAsync(userId, req.CurrentPassword, req.NewPassword);
    if (error != null)
    {
        return Results.BadRequest(error);
    }
    return Results.Ok();
});

usersApi.MapPut("/{userId:int}/assign-tenant", async (int userId, [FromBody] AssignTenantRequest req, IAuthService authService) =>
{
    var error = await authService.AssignTenantAsync(userId, req.TenantId);
    if (error != null)
    {
        return Results.BadRequest(error);
    }
    return Results.Ok();
});

usersApi.MapPut("/{userId:int}/reset-password", async (int userId, [FromBody] ResetPasswordRequest req, IAuthService authService) =>
{
    var error = await authService.ResetPasswordAsync(userId, req.NewPassword);
    if (error != null)
    {
        return Results.BadRequest(error);
    }
    return Results.Ok();
});

// ── Timetracker-API-Endpoints (Protected) ─────────────────────────────────────
var trackerApi = app.MapGroup("/api/tracker").RequireAuthorization();

trackerApi.MapPost("/tenant/settings", async (ClaimsPrincipal claimsPrincipal, [FromBody] TenantSettingsDto dto, ITenantProvider tenantProvider, TimetrackerDbContext db) =>
{
    var isAdmin = claimsPrincipal.FindFirst("IsTenantAdmin")?.Value == "true";
    if (!isAdmin) return Results.Forbid();

    var tenant = await tenantProvider.GetCurrentTenantAsync();
    if (tenant == null) return Results.BadRequest("Keine Firma aktiv.");

    var dbTenant = await db.Tenants.FindAsync(tenant.Id);
    if (dbTenant == null) return Results.NotFound("Firma nicht gefunden.");

    if (!string.IsNullOrWhiteSpace(dto.Name))
    {
        dbTenant.Name = dto.Name.Trim();
    }
    if (!string.IsNullOrWhiteSpace(dto.PrimaryColor))
    {
        dbTenant.PrimaryColor = dto.PrimaryColor.Trim();
    }
    if (!string.IsNullOrWhiteSpace(dto.SecondaryColor))
    {
        dbTenant.SecondaryColor = dto.SecondaryColor.Trim();
    }

    await db.SaveChangesAsync();
    return Results.Ok();
});

trackerApi.MapGet("/week", async (ClaimsPrincipal claimsPrincipal, [FromQuery] string monday, ITimetrackerService trackerService) =>
{
    var idClaim = claimsPrincipal.FindFirst(ClaimTypes.NameIdentifier)?.Value;
    if (!int.TryParse(idClaim, out var userId)) return Results.Unauthorized();

    if (DateOnly.TryParse(monday, out var date))
    {
        var days = await trackerService.GetWeekAsync(userId, date);
        return Results.Ok(days);
    }
    return Results.BadRequest("Ungültiges Datum.");
});

trackerApi.MapPost("/workday", async (ClaimsPrincipal claimsPrincipal, [FromBody] WorkDay workDay, ITimetrackerService trackerService) =>
{
    var idClaim = claimsPrincipal.FindFirst(ClaimTypes.NameIdentifier)?.Value;
    if (!int.TryParse(idClaim, out var userId)) return Results.Unauthorized();

    workDay.UserId = userId; // Enforce owner
    await trackerService.UpsertWorkDayAsync(workDay);
    return Results.Ok();
});

trackerApi.MapGet("/settings", async (ClaimsPrincipal claimsPrincipal, ITimetrackerService trackerService) =>
{
    var idClaim = claimsPrincipal.FindFirst(ClaimTypes.NameIdentifier)?.Value;
    if (!int.TryParse(idClaim, out var userId)) return Results.Unauthorized();

    var settings = await trackerService.GetSettingsAsync(userId);
    return Results.Ok(settings);
});

trackerApi.MapPost("/settings", async (ClaimsPrincipal claimsPrincipal, [FromBody] AppSettings settings, ITimetrackerService trackerService) =>
{
    var idClaim = claimsPrincipal.FindFirst(ClaimTypes.NameIdentifier)?.Value;
    if (!int.TryParse(idClaim, out var userId)) return Results.Unauthorized();

    settings.UserId = userId; // Enforce owner
    await trackerService.SaveSettingsAsync(settings);
    return Results.Ok();
});

trackerApi.MapGet("/vacation/{year:int}", async (ClaimsPrincipal claimsPrincipal, int year, ITimetrackerService trackerService) =>
{
    var idClaim = claimsPrincipal.FindFirst(ClaimTypes.NameIdentifier)?.Value;
    if (!int.TryParse(idClaim, out var userId)) return Results.Unauthorized();

    var days = await trackerService.GetVacationDaysAsync(userId, year);
    return Results.Ok(days);
});

trackerApi.MapPost("/vacation", async (ClaimsPrincipal claimsPrincipal, [FromBody] VacationDay vacationDay, ITimetrackerService trackerService) =>
{
    var idClaim = claimsPrincipal.FindFirst(ClaimTypes.NameIdentifier)?.Value;
    if (!int.TryParse(idClaim, out var userId)) return Results.Unauthorized();

    vacationDay.UserId = userId; // Enforce owner
    await trackerService.AddVacationDayAsync(vacationDay);
    return Results.Ok();
});

trackerApi.MapDelete("/vacation/{id:int}", async (ClaimsPrincipal claimsPrincipal, int id, ITimetrackerService trackerService) =>
{
    var idClaim = claimsPrincipal.FindFirst(ClaimTypes.NameIdentifier)?.Value;
    if (!int.TryParse(idClaim, out var userId)) return Results.Unauthorized();

    await trackerService.RemoveVacationDayAsync(userId, id);
    return Results.NoContent();
});

trackerApi.MapPost("/overtime", async (ClaimsPrincipal claimsPrincipal, [FromBody] AppSettings settings, ITimetrackerService trackerService) =>
{
    var idClaim = claimsPrincipal.FindFirst(ClaimTypes.NameIdentifier)?.Value;
    if (!int.TryParse(idClaim, out var userId)) return Results.Unauthorized();

    settings.UserId = userId; // Enforce owner
    var ts = await trackerService.GetTotalOvertimeAsync(userId, settings);
    return Results.Ok(ts.TotalHours);
});

trackerApi.MapGet("/month", async (ClaimsPrincipal claimsPrincipal, [FromQuery] int year, [FromQuery] int month, ITimetrackerService trackerService) =>
{
    var idClaim = claimsPrincipal.FindFirst(ClaimTypes.NameIdentifier)?.Value;
    if (!int.TryParse(idClaim, out var userId)) return Results.Unauthorized();

    var days = await trackerService.GetMonthAsync(userId, year, month);
    return Results.Ok(days);
});

trackerApi.MapGet("/export/week", async (ClaimsPrincipal claimsPrincipal, [FromQuery] string monday, ITimetrackerService trackerService, TimetrackerDbContext db) =>
{
    var idClaim = claimsPrincipal.FindFirst(ClaimTypes.NameIdentifier)?.Value;
    if (!int.TryParse(idClaim, out var userId)) return Results.Unauthorized();

    if (DateOnly.TryParse(monday, out var date))
    {
        var mondayDate = date;
        int diff = ((int)mondayDate.DayOfWeek - (int)DayOfWeek.Monday + 7) % 7;
        mondayDate = mondayDate.AddDays(-diff);
        var sundayDate = mondayDate.AddDays(6);

        var fileBytes = await ExcelExporter.GenerateExcelExportAsync(
            userId,
            mondayDate,
            sundayDate,
            $"Wochenbericht KW {System.Globalization.ISOWeek.GetWeekOfYear(new DateTime(mondayDate.Year, mondayDate.Month, mondayDate.Day))}",
            trackerService,
            db);

        return Results.File(
            fileBytes,
            "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
            $"Wochenbericht_KW_{System.Globalization.ISOWeek.GetWeekOfYear(new DateTime(mondayDate.Year, mondayDate.Month, mondayDate.Day))}_{mondayDate:yyyyMMdd}.xlsx");
    }
    return Results.BadRequest("Ungültiges Datum.");
});

trackerApi.MapGet("/export/month", async (ClaimsPrincipal claimsPrincipal, [FromQuery] int year, [FromQuery] int month, ITimetrackerService trackerService, TimetrackerDbContext db) =>
{
    var idClaim = claimsPrincipal.FindFirst(ClaimTypes.NameIdentifier)?.Value;
    if (!int.TryParse(idClaim, out var userId)) return Results.Unauthorized();

    if (month < 1 || month > 12) return Results.BadRequest("Ungültiger Monat.");

    var startDate = new DateOnly(year, month, 1);
    var endDate = startDate.AddMonths(1).AddDays(-1);

    var deCulture = new System.Globalization.CultureInfo("de-DE");
    var monthName = deCulture.DateTimeFormat.GetMonthName(month);

    var fileBytes = await ExcelExporter.GenerateExcelExportAsync(
        userId,
        startDate,
        endDate,
        $"Monatsbericht {monthName} {year}",
        trackerService,
        db);

    return Results.File(
        fileBytes,
        "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
        $"Monatsbericht_{monthName}_{year}.xlsx");
});

trackerApi.MapGet("/export/year", async (ClaimsPrincipal claimsPrincipal, [FromQuery] int year, ITimetrackerService trackerService, TimetrackerDbContext db) =>
{
    var idClaim = claimsPrincipal.FindFirst(ClaimTypes.NameIdentifier)?.Value;
    if (!int.TryParse(idClaim, out var userId)) return Results.Unauthorized();

    var startDate = new DateOnly(year, 1, 1);
    var endDate = new DateOnly(year, 12, 31);

    var fileBytes = await ExcelExporter.GenerateExcelExportAsync(
        userId,
        startDate,
        endDate,
        $"Jahresbericht {year}",
        trackerService,
        db);

    return Results.File(
        fileBytes,
        "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
        $"Jahresbericht_{year}.xlsx");
});

// ── Holiday-API-Endpoints (Protected) ─────────────────────────────────────────
var holidaysApi = app.MapGroup("/api/holidays").RequireAuthorization();

holidaysApi.MapGet("/", async ([FromQuery] int year, [FromQuery] string? stateCode, IHolidayService holidayService) =>
{
    var list = await holidayService.GetHolidaysAsync(year, stateCode);
    return Results.Ok(list);
});

holidaysApi.MapPost("/fetch/{year:int}", async (int year, IHolidayService holidayService) =>
{
    var (success, message) = await holidayService.FetchAndStoreAsync(year);
    return Results.Ok(new { Success = success, Message = message });
});

holidaysApi.MapDelete("/{id:int}", async (int id, IHolidayService holidayService) =>
{
    await holidayService.DeleteAsync(id);
    return Results.NoContent();
});

app.MapGet("/api/tenant/current", async (ITenantProvider tenantProvider) =>
{
    var tenant = await tenantProvider.GetCurrentTenantAsync();
    if (tenant == null)
    {
        return Results.Ok(new TenantInfo { IsTenant = false });
    }
    return Results.Ok(new TenantInfo
    {
        IsTenant = true,
        Name = tenant.Name,
        Subdomain = tenant.Subdomain,
        IsApproved = tenant.IsApproved,
        PrimaryColor = tenant.PrimaryColor,
        SecondaryColor = tenant.SecondaryColor
    });
});

app.MapPost("/api/auth/register-firm", async ([FromBody] RegisterFirmRequest req, IAuthService authService) =>
{
    var (tenant, error) = await authService.RegisterFirmAsync(req);
    if (tenant == null)
        return Results.BadRequest(error ?? "Registrierung der Firma fehlgeschlagen.");

    return Results.Ok(new { Success = true });
}).RequireRateLimiting("auth-limit");

// ── SuperAdmin-API-Endpoints (Protected) ───────────────────────────────────────────
var superAdminApi = app.MapGroup("/api/superadmin").RequireAuthorization("AdminOnly");

superAdminApi.MapGet("/tenants", async (TimetrackerDbContext db) =>
{
    var tenants = await db.Tenants
        .OrderByDescending(t => t.CreatedAt)
        .ToListAsync();

    var stats = new List<object>();
    foreach (var t in tenants)
    {
        var userCount = await db.Users.IgnoreQueryFilters().CountAsync(u => u.TenantId == t.Id);
        var workDaysCount = await db.WorkDays.IgnoreQueryFilters().CountAsync(w => w.TenantId == t.Id);
        var tenantUsers = await db.Users.IgnoreQueryFilters()
            .Where(u => u.TenantId == t.Id)
            .Select(u => new { u.Id, u.Username, u.IsTenantAdmin })
            .ToListAsync();

        stats.Add(new
        {
            t.Id,
            t.Name,
            t.Subdomain,
            t.IsApproved,
            t.CreatedAt,
            UserCount = userCount,
            WorkDaysCount = workDaysCount,
            Users = tenantUsers
        });
    }
    return Results.Ok(stats);
});

superAdminApi.MapPost("/tenants/{id:int}/approve", async (int id, TimetrackerDbContext db) =>
{
    var tenant = await db.Tenants.FindAsync(id);
    if (tenant == null) return Results.NotFound();
    tenant.IsApproved = true;
    await db.SaveChangesAsync();
    return Results.Ok();
});

superAdminApi.MapPost("/tenants/{id:int}/toggle-active", async (int id, TimetrackerDbContext db) =>
{
    var tenant = await db.Tenants.FindAsync(id);
    if (tenant == null) return Results.NotFound();
    tenant.IsApproved = !tenant.IsApproved;
    await db.SaveChangesAsync();
    return Results.Ok();
});

superAdminApi.MapDelete("/tenants/{id:int}", async (int id, TimetrackerDbContext db, UserNotificationService notifier) =>
{
    var tenant = await db.Tenants.FindAsync(id);
    if (tenant == null) return Results.NotFound();

    await using var transaction = await db.Database.BeginTransactionAsync();
    try
    {
        var userIds = await db.Users.IgnoreQueryFilters()
            .Where(u => u.TenantId == id)
            .Select(u => u.Id)
            .ToListAsync();

        var workDays = await db.WorkDays.IgnoreQueryFilters().Where(w => w.TenantId == id).ToListAsync();
        db.WorkDays.RemoveRange(workDays);

        var vacationDays = await db.VacationDays.IgnoreQueryFilters().Where(v => v.TenantId == id).ToListAsync();
        db.VacationDays.RemoveRange(vacationDays);

        var settings = await db.AppSettings.IgnoreQueryFilters().Where(s => s.TenantId == id).ToListAsync();
        db.AppSettings.RemoveRange(settings);

        var users = await db.Users.IgnoreQueryFilters().Where(u => u.TenantId == id).ToListAsync();
        db.Users.RemoveRange(users);

        db.Tenants.Remove(tenant);

        await db.SaveChangesAsync();
        await transaction.CommitAsync();

        foreach (var uid in userIds)
        {
            await notifier.NotifyUserDeletedAsync(uid);
        }
        await notifier.NotifyUsersChangedAsync();

        return Results.NoContent();
    }
    catch (Exception ex)
    {
        await transaction.RollbackAsync();
        return Results.BadRequest($"Fehler beim Löschen der Firma: {ex.Message}");
    }
});

app.Run();

// ── Models for Request Bodies ──────────────────────────────────────────────────
public record LoginRequest(string Username, string Password);
public record RegisterRequest(string Username, string Password, string? Honeypot = null);
public record RenameRequest(string Username);
public record ChangePasswordRequest(string CurrentPassword, string NewPassword);
