using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Security.Claims;
using watcher_monitoring.Configuration;
using watcher_monitoring.Data;
using watcher_monitoring.Models;

namespace watcher_monitoring.Controllers;

public class AuthController : Controller
{
    private readonly WatcherDbContext _context;
    private readonly ILogger<AuthController> _logger;
    private readonly OidcSettings _oidcSettings;

    public AuthController(WatcherDbContext context, ILogger<AuthController> logger, OidcSettings oidcSettings)
    {
        _context = context;
        _logger = logger;
        _oidcSettings = oidcSettings;
    }

    [AllowAnonymous]
    [HttpGet]
    public IActionResult Login(string? returnUrl = null)
    {
        // Wenn der Benutzer bereits angemeldet ist, zur Startseite weiterleiten
        if (User.Identity?.IsAuthenticated == true)
        {
            return RedirectToAction("Index", "Home");
        }

        ViewData["ReturnUrl"] = returnUrl;
        ViewData["OidcEnabled"] = _oidcSettings.IsValid;
        return View();
    }

    [AllowAnonymous]
    [HttpGet]
    public IActionResult OidcLogin(string? returnUrl = null)
    {
        if (!_oidcSettings.IsValid)
        {
            _logger.LogWarning("OIDC-Login versucht, aber OIDC ist nicht konfiguriert");
            return RedirectToAction("Login");
        }

        var properties = new AuthenticationProperties
        {
            RedirectUri = Url.Action("OidcCallback", "Auth", new { returnUrl }),
            Items = { { "returnUrl", returnUrl ?? "/" } }
        };

        return Challenge(properties, OpenIdConnectDefaults.AuthenticationScheme);
    }

    [AllowAnonymous]
    [HttpGet]
    public async Task<IActionResult> OidcCallback(string? returnUrl = null)
    {
        var authenticateResult = await HttpContext.AuthenticateAsync(OpenIdConnectDefaults.AuthenticationScheme);

        if (!authenticateResult.Succeeded)
        {
            _logger.LogWarning("OIDC-Authentifizierung fehlgeschlagen: {Failure}", authenticateResult.Failure?.Message);
            TempData["Error"] = "OIDC-Anmeldung fehlgeschlagen. Bitte versuchen Sie es erneut.";
            return RedirectToAction("Login");
        }

        var oidcClaims = authenticateResult.Principal?.Claims;
        var oidcSubject = oidcClaims?.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value
                       ?? oidcClaims?.FirstOrDefault(c => c.Type == "sub")?.Value;

        if (string.IsNullOrEmpty(oidcSubject))
        {
            _logger.LogError("OIDC-Claims enthalten keine Subject-ID");
            TempData["Error"] = "OIDC-Anmeldung fehlgeschlagen: Keine Benutzer-ID erhalten.";
            return RedirectToAction("Login");
        }

        var username = oidcClaims?.FirstOrDefault(c => c.Type == _oidcSettings.ClaimUsername)?.Value
                    ?? oidcClaims?.FirstOrDefault(c => c.Type == ClaimTypes.Name)?.Value
                    ?? oidcClaims?.FirstOrDefault(c => c.Type == "name")?.Value
                    ?? oidcSubject;

        var email = oidcClaims?.FirstOrDefault(c => c.Type == _oidcSettings.ClaimEmail)?.Value
                 ?? oidcClaims?.FirstOrDefault(c => c.Type == ClaimTypes.Email)?.Value
                 ?? $"{username}@oidc.local";

        var user = await _context.Users.FirstOrDefaultAsync(u => u.OidcSubject == oidcSubject);

        if (user == null)
        {
            if (!_oidcSettings.AutoProvisionUsers)
            {
                _logger.LogWarning("OIDC-User {Subject} existiert nicht und Auto-Provisioning ist deaktiviert", oidcSubject);
                TempData["Error"] = "Ihr Benutzerkonto existiert nicht. Bitte kontaktieren Sie den Administrator.";
                return RedirectToAction("Login");
            }

            var existingUsername = await _context.Users.AnyAsync(u => u.Username == username);
            if (existingUsername)
            {
                username = $"{username}_{oidcSubject[..Math.Min(8, oidcSubject.Length)]}";
            }

            user = new User
            {
                Username = username,
                Email = email,
                OidcSubject = oidcSubject,
                Password = string.Empty,
                IsActive = true,
                CreatedAt = DateTime.UtcNow,
                LastLogin = DateTime.UtcNow
            };

            _context.Users.Add(user);
            await _context.SaveChangesAsync();
            _logger.LogInformation("OIDC-User {Username} wurde automatisch erstellt (Subject: {Subject})", username, oidcSubject);
        }
        else
        {
            if (!user.IsActive)
            {
                _logger.LogWarning("OIDC-User {Username} ist deaktiviert", user.Username);
                TempData["Error"] = "Ihr Benutzerkonto ist deaktiviert.";
                return RedirectToAction("Login");
            }

            user.LastLogin = DateTime.UtcNow;
            user.Email = email;
            await _context.SaveChangesAsync();
        }

        var claims = new List<Claim>
        {
            new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
            new Claim(ClaimTypes.Name, user.Username),
            new Claim(ClaimTypes.Email, user.Email),
            new Claim("LastLogin", user.LastLogin.ToString("o")),
            new Claim("AuthMethod", "OIDC")
        };

        var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
        var claimsPrincipal = new ClaimsPrincipal(claimsIdentity);

        var authProperties = new AuthenticationProperties
        {
            IsPersistent = true,
            ExpiresUtc = DateTimeOffset.UtcNow.AddHours(8)
        };

        await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, claimsPrincipal, authProperties);

        _logger.LogInformation("OIDC-User {Username} erfolgreich angemeldet", user.Username);

        if (!string.IsNullOrEmpty(returnUrl) && Url.IsLocalUrl(returnUrl))
        {
            return Redirect(returnUrl);
        }

        return RedirectToAction("Index", "Home");
    }

    [AllowAnonymous]
    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Login(LoginViewModel model, string? returnUrl = null)
    {
        ViewData["ReturnUrl"] = returnUrl;

        if (!ModelState.IsValid)
        {
            return View(model);
        }

        try
        {
            // Benutzer suchen
            var user = await _context.Users
                .FirstOrDefaultAsync(u => u.Username == model.Username && u.IsActive);

            if (user == null)
            {
                _logger.LogWarning("Login-Versuch mit ungültigem Benutzernamen: {Username}", model.Username);
                TempData["Error"] = "Ungültiger Benutzername oder Passwort";
                return View(model);
            }

            // Passwort überprüfen (BCrypt)
            if (!BCrypt.Net.BCrypt.Verify(model.Password, user.Password))
            {
                _logger.LogWarning("Login-Versuch mit falschem Passwort für Benutzer: {Username}", model.Username);
                TempData["Error"] = "Ungültiger Benutzername oder Passwort";
                return View(model);
            }

            // LastLogin aktualisieren
            user.LastLogin = DateTime.UtcNow;
            await _context.SaveChangesAsync();

            // Claims erstellen
            var claims = new List<Claim>
            {
                new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
                new Claim(ClaimTypes.Name, user.Username),
                new Claim(ClaimTypes.Email, user.Email),
                new Claim("LastLogin", user.LastLogin.ToString("o"))
            };

            var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
            var claimsPrincipal = new ClaimsPrincipal(claimsIdentity);

            var authProperties = new AuthenticationProperties
            {
                IsPersistent = model.RememberMe,
                ExpiresUtc = model.RememberMe ? DateTimeOffset.UtcNow.AddDays(30) : DateTimeOffset.UtcNow.AddHours(8)
            };

            await HttpContext.SignInAsync(
                CookieAuthenticationDefaults.AuthenticationScheme,
                claimsPrincipal,
                authProperties);

            _logger.LogInformation("Benutzer {Username} erfolgreich angemeldet", user.Username);

            if (!string.IsNullOrEmpty(returnUrl) && Url.IsLocalUrl(returnUrl))
            {
                return Redirect(returnUrl);
            }

            return RedirectToAction("Index", "Home");
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Fehler beim Login-Vorgang");
            TempData["Error"] = "Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.";
            return View(model);
        }
    }

    [Authorize]
    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Logout()
    {
        var username = User.Identity?.Name ?? "Unbekannt";

        await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);

        _logger.LogInformation("Benutzer {Username} erfolgreich abgemeldet", username);

        return RedirectToAction("Login", "Auth");
    }

    [AllowAnonymous]
    public IActionResult AccessDenied()
    {
        return View();
    }
}
