using System;
using System.Collections.Generic;

namespace StellaOps.Cryptography.Audit;

/// <summary>
/// Represents a structured security event emitted by the Authority host and plugins.
/// </summary>
public sealed record AuthEventRecord
{
    /// <summary>
    /// Canonical event identifier (e.g. <c>authority.password.grant</c>).
    /// </summary>
    public required string EventType { get; init; }

    /// <summary>
    /// UTC timestamp captured when the event occurred.
    /// </summary>
    public DateTimeOffset OccurredAt { get; init; } = DateTimeOffset.UtcNow;

    /// <summary>
    /// Stable correlation identifier that links the event across logs, traces, and persistence.
    /// </summary>
    public string? CorrelationId { get; init; }

    /// <summary>
    /// Outcome classification for the audited operation.
    /// </summary>
    public AuthEventOutcome Outcome { get; init; } = AuthEventOutcome.Unknown;

    /// <summary>
    /// Optional human-readable reason or failure descriptor.
    /// </summary>
    public string? Reason { get; init; }

    /// <summary>
    /// Identity of the end-user (subject) involved in the event, when applicable.
    /// </summary>
    public AuthEventSubject? Subject { get; init; }

    /// <summary>
    /// OAuth/OIDC client metadata associated with the event, when applicable.
    /// </summary>
    public AuthEventClient? Client { get; init; }

    /// <summary>
    /// Granted or requested scopes tied to the event.
    /// </summary>
    public IReadOnlyList<string> Scopes { get; init; } = Array.Empty<string>();

    /// <summary>
    /// Network attributes (remote IP, forwarded headers, user agent) captured for the request.
    /// </summary>
    public AuthEventNetwork? Network { get; init; }

    /// <summary>
    /// Additional classified properties carried with the event.
    /// </summary>
    public IReadOnlyList<AuthEventProperty> Properties { get; init; } = Array.Empty<AuthEventProperty>();
}

/// <summary>
/// Describes the outcome of an audited flow.
/// </summary>
public enum AuthEventOutcome
{
    /// <summary>
    /// Outcome has not been set.
    /// </summary>
    Unknown = 0,

    /// <summary>
    /// Operation succeeded.
    /// </summary>
    Success,

    /// <summary>
    /// Operation failed (invalid credentials, configuration issues, etc.).
    /// </summary>
    Failure,

    /// <summary>
    /// Operation failed due to a lockout policy.
    /// </summary>
    LockedOut,

    /// <summary>
    /// Operation was rejected due to rate limiting or throttling.
    /// </summary>
    RateLimited,

    /// <summary>
    /// Operation encountered an unexpected error.
    /// </summary>
    Error
}

/// <summary>
/// Represents a string value enriched with a data classification tag.
/// </summary>
public readonly record struct ClassifiedString(string? Value, AuthEventDataClassification Classification)
{
    /// <summary>
    /// An empty classified string.
    /// </summary>
    public static ClassifiedString Empty => new(null, AuthEventDataClassification.None);

    /// <summary>
    /// Indicates whether the classified string carries a non-empty value.
    /// </summary>
    public bool HasValue => !string.IsNullOrWhiteSpace(Value);

    /// <summary>
    /// Creates a classified string for public/non-sensitive data.
    /// </summary>
    public static ClassifiedString Public(string? value) => Create(value, AuthEventDataClassification.None);

    /// <summary>
    /// Creates a classified string tagged as personally identifiable information (PII).
    /// </summary>
    public static ClassifiedString Personal(string? value) => Create(value, AuthEventDataClassification.Personal);

    /// <summary>
    /// Creates a classified string tagged as sensitive (e.g. credentials, secrets).
    /// </summary>
    public static ClassifiedString Sensitive(string? value) => Create(value, AuthEventDataClassification.Sensitive);

    private static ClassifiedString Create(string? value, AuthEventDataClassification classification)
    {
        return new ClassifiedString(Normalize(value), classification);
    }

    private static string? Normalize(string? value)
    {
        return string.IsNullOrWhiteSpace(value) ? null : value;
    }
}

/// <summary>
/// Supported classifications for audit data values.
/// </summary>
public enum AuthEventDataClassification
{
    /// <summary>
    /// Data is not considered sensitive.
    /// </summary>
    None = 0,

    /// <summary>
    /// Personally identifiable information (PII) that warrants redaction in certain sinks.
    /// </summary>
    Personal,

    /// <summary>
    /// Highly sensitive information (credentials, secrets, tokens).
    /// </summary>
    Sensitive
}

/// <summary>
/// Captures subject metadata for an audit event.
/// </summary>
public sealed record AuthEventSubject
{
    /// <summary>
    /// Stable subject identifier (PII).
    /// </summary>
    public ClassifiedString SubjectId { get; init; } = ClassifiedString.Empty;

    /// <summary>
    /// Username or login name (PII).
    /// </summary>
    public ClassifiedString Username { get; init; } = ClassifiedString.Empty;

    /// <summary>
    /// Optional display name (PII).
    /// </summary>
    public ClassifiedString DisplayName { get; init; } = ClassifiedString.Empty;

    /// <summary>
    /// Optional plugin or tenant realm controlling the subject namespace.
    /// </summary>
    public ClassifiedString Realm { get; init; } = ClassifiedString.Empty;

    /// <summary>
    /// Additional classified attributes (e.g. email, phone).
    /// </summary>
    public IReadOnlyList<AuthEventProperty> Attributes { get; init; } = Array.Empty<AuthEventProperty>();
}

/// <summary>
/// Captures OAuth/OIDC client metadata for an audit event.
/// </summary>
public sealed record AuthEventClient
{
    /// <summary>
    /// Client identifier (PII for confidential clients).
    /// </summary>
    public ClassifiedString ClientId { get; init; } = ClassifiedString.Empty;

    /// <summary>
    /// Friendly client name (may be public).
    /// </summary>
    public ClassifiedString Name { get; init; } = ClassifiedString.Empty;

    /// <summary>
    /// Identity provider/plugin originating the client.
    /// </summary>
    public ClassifiedString Provider { get; init; } = ClassifiedString.Empty;
}

/// <summary>
/// Captures network metadata for an audit event.
/// </summary>
public sealed record AuthEventNetwork
{
    /// <summary>
    /// Remote address observed for the request (PII).
    /// </summary>
    public ClassifiedString RemoteAddress { get; init; } = ClassifiedString.Empty;

    /// <summary>
    /// Forwarded address supplied by proxies (PII).
    /// </summary>
    public ClassifiedString ForwardedFor { get; init; } = ClassifiedString.Empty;

    /// <summary>
    /// User agent string associated with the request.
    /// </summary>
    public ClassifiedString UserAgent { get; init; } = ClassifiedString.Empty;
}

/// <summary>
/// Represents an additional classified property associated with the audit event.
/// </summary>
public sealed record AuthEventProperty
{
    /// <summary>
    /// Property name (canonical snake-case identifier).
    /// </summary>
    public required string Name { get; init; }

    /// <summary>
    /// Classified value assigned to the property.
    /// </summary>
    public ClassifiedString Value { get; init; } = ClassifiedString.Empty;
}

/// <summary>
/// Sink that receives completed audit event records.
/// </summary>
public interface IAuthEventSink
{
    /// <summary>
    /// Persists the supplied audit event.
    /// </summary>
    ValueTask WriteAsync(AuthEventRecord record, CancellationToken cancellationToken);
}
