using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using JobTrackerApi.Data;
using Microsoft.EntityFrameworkCore;

namespace JobTrackerApi.Services;

public static class SessionRevocation
{
    public static async Task RevokeCurrentAsync(JobTrackerContext db, string userId, string sessionId, CancellationToken cancellationToken)
    {
        var session = await db.UserSessions.IgnoreQueryFilters()
            .FirstOrDefaultAsync(x => x.Id == sessionId && x.UserId == userId && x.RevokedAtUtc == null, cancellationToken);
        if (session is null) return;

        session.RevokedAtUtc = DateTimeOffset.UtcNow;
        await db.SaveChangesAsync(cancellationToken);
    }

    public static async Task RevokeAllAsync(JobTrackerContext db, string userId, string? trustedDeviceHashToKeep, CancellationToken cancellationToken)
    {
        var sessions = await db.UserSessions.IgnoreQueryFilters()
            .Where(x => x.UserId == userId && x.RevokedAtUtc == null)
            .ToListAsync(cancellationToken);
        var devices = await db.TrustedDevices.IgnoreQueryFilters()
            .Where(x => x.UserId == userId && (trustedDeviceHashToKeep == null || x.TokenHash != trustedDeviceHashToKeep))
            .ToListAsync(cancellationToken);
        var now = DateTimeOffset.UtcNow;
        foreach (var session in sessions) session.RevokedAtUtc = now;
        db.TrustedDevices.RemoveRange(devices);
        if (sessions.Count > 0 || devices.Count > 0)
            await db.SaveChangesAsync(cancellationToken);
    }

    public static bool TryReadIdentity(ClaimsPrincipal principal, string? cookieToken, out string userId, out string sessionId)
    {
        userId = principal.FindFirstValue(ClaimTypes.NameIdentifier) ?? principal.FindFirstValue("sub") ?? "";
        sessionId = principal.FindFirstValue("sid") ?? "";
        if (userId.Length > 0 && sessionId.Length > 0) return true;

        if (string.IsNullOrWhiteSpace(cookieToken) || cookieToken.Length > 16_384) return false;
        var handler = new JwtSecurityTokenHandler { MapInboundClaims = false };
        if (!handler.CanReadToken(cookieToken)) return false;

        try
        {
            var token = handler.ReadJwtToken(cookieToken);
            userId = token.Claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier || x.Type == "sub")?.Value ?? "";
            sessionId = token.Claims.FirstOrDefault(x => x.Type == "sid")?.Value ?? "";
            return userId.Length > 0 && sessionId.Length > 0;
        }
        catch
        {
            return false;
        }
    }
}
