using System.Text.Json;
using MicCheck.Api.Data;
using MicCheck.Api.Webhooks;

namespace MicCheck.Api.Audit;

public class AuditService(IMicCheckDbContext db, IHttpContextAccessor httpContextAccessor, WebhookQueue webhookQueue) : IAuditService
{
    private static readonly JsonSerializerOptions JsonOptions = new()
    {
        WriteIndented = false,
        PropertyNamingPolicy = JsonNamingPolicy.CamelCase
    };

    public async Task RecordAsync(
        string resourceType,
        string resourceId,
        string action,
        int organizationId,
        int? projectId = null,
        int? environmentId = null,
        object? before = null,
        object? after = null,
        CancellationToken ct = default)
    {
        string? changes = null;
        if (before is not null || after is not null)
        {
            changes = JsonSerializer.Serialize(new
            {
                before,
                after
            }, JsonOptions);
        }

        var actorUserId = GetCurrentUserId();

        var log = new AuditLog
        {
            ResourceType = resourceType,
            ResourceId = resourceId,
            Action = action,
            OrganizationId = organizationId,
            ProjectId = projectId,
            EnvironmentId = environmentId,
            ActorUserId = actorUserId,
            Changes = changes,
            CreatedAt = DateTimeOffset.UtcNow
        };

        db.AuditLogs.Add(log);
        await db.SaveChangesAsync(ct);

        await webhookQueue.EnqueueAsync(new WebhookEvent
        {
            EventType = WebhookEventTypes.AuditLogCreated,
            EnvironmentId = environmentId,
            OrganizationId = organizationId,
            Data = new
            {
                audit_log_id = log.Id,
                resource_type = resourceType,
                resource_id = resourceId,
                action,
                created_at = log.CreatedAt
            }
        });
    }

    // Backward-compatible overload used by existing callers
    public async Task LogAsync(
        string resourceType,
        string resourceId,
        string action,
        int organizationId,
        int? projectId = null,
        int? environmentId = null,
        string? changes = null,
        CancellationToken ct = default)
    {
        var actorUserId = GetCurrentUserId();

        var log = new AuditLog
        {
            ResourceType = resourceType,
            ResourceId = resourceId,
            Action = action,
            OrganizationId = organizationId,
            ProjectId = projectId,
            EnvironmentId = environmentId,
            ActorUserId = actorUserId,
            Changes = changes,
            CreatedAt = DateTimeOffset.UtcNow
        };

        db.AuditLogs.Add(log);
        await db.SaveChangesAsync(ct);

        await webhookQueue.EnqueueAsync(new WebhookEvent
        {
            EventType = WebhookEventTypes.AuditLogCreated,
            EnvironmentId = environmentId,
            OrganizationId = organizationId,
            Data = new
            {
                audit_log_id = log.Id,
                resource_type = resourceType,
                resource_id = resourceId,
                action,
                created_at = log.CreatedAt
            }
        });
    }

    private int? GetCurrentUserId()
    {
        var claim = httpContextAccessor.HttpContext?.User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value;
        return int.TryParse(claim, out var id) ? id : null;
    }
}

public interface IAuditService
{
    Task RecordAsync(
        string resourceType,
        string resourceId,
        string action,
        int organizationId,
        int? projectId = null,
        int? environmentId = null,
        object? before = null,
        object? after = null,
        CancellationToken ct = default);

    Task LogAsync(
        string resourceType,
        string resourceId,
        string action,
        int organizationId,
        int? projectId = null,
        int? environmentId = null,
        string? changes = null,
        CancellationToken ct = default);
}
