namespace ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Security;

/// <summary>
/// Read-side projection of one inbound API key, as surfaced by the management seam.
/// Hash-free by construction — the secret is never carried here; it is shown ONCE at
/// creation via <see cref="InboundApiKeyCreated"/>.
/// </summary>
/// <param name="KeyId">Stable key identifier (the middle segment of the token).</param>
/// <param name="Name">Operator-facing display name.</param>
/// <param name="Enabled">True while the key is active (not revoked/disabled).</param>
/// <param name="Methods">The API-method names this key is scoped to call, sorted ordinally.</param>
/// <param name="CreatedUtc">When the key was created.</param>
/// <param name="LastUsedUtc">When the key last authenticated a request, if ever.</param>
public sealed record InboundApiKeyInfo(
    string KeyId,
    string Name,
    bool Enabled,
    IReadOnlyList<string> Methods,
    DateTimeOffset CreatedUtc,
    DateTimeOffset? LastUsedUtc);

/// <summary>
/// Result of creating a key. <see cref="Token"/> is the assembled bearer token
/// (<c>sbk_&lt;keyId&gt;_&lt;secret&gt;</c>) and is the ONLY moment the secret is available —
/// it is never retrievable afterwards.
/// </summary>
/// <param name="KeyId">The new key's identifier.</param>
/// <param name="Token">The bearer token, shown once.</param>
public sealed record InboundApiKeyCreated(string KeyId, string Token);

/// <summary>
/// App-facing management seam for inbound API keys. This is the single shared path CLI
/// and CentralUI use to create / list / enable / disable / delete inbound keys and edit
/// their method-scopes. The interface lives in Commons and is deliberately free of any
/// dependency on the underlying auth library, so consumers depend only on this contract.
/// </summary>
/// <remarks>
/// Mutating operations (<see cref="CreateAsync"/>, <see cref="SetEnabledAsync"/>,
/// <see cref="SetMethodsAsync"/>, <see cref="DeleteAsync"/>) may <b>throw</b> on
/// store-level or configuration failures (e.g. an unavailable pepper) rather than
/// exclusively signalling failure via their <c>bool</c> return — callers must handle
/// exceptions in addition to checking the return value.
/// </remarks>
public interface IInboundApiKeyAdmin
{
    /// <summary>Creates a new key scoped to <paramref name="methods"/> and returns its
    /// identifier plus the bearer token (shown once).</summary>
    /// <param name="name">Operator-facing display name for the new key.</param>
    /// <param name="methods">API method names the key is permitted to call.</param>
    /// <param name="ct">Token to observe for cancellation.</param>
    /// <returns>A task that resolves to the new key identifier and the one-time bearer token.</returns>
    Task<InboundApiKeyCreated> CreateAsync(
        string name, IReadOnlyCollection<string> methods, CancellationToken ct = default);

    /// <summary>Lists all inbound keys (hash-free projection).</summary>
    /// <param name="ct">Token to observe for cancellation.</param>
    /// <returns>A task that resolves to the full list of inbound API key records.</returns>
    Task<IReadOnlyList<InboundApiKeyInfo>> ListAsync(CancellationToken ct = default);

    /// <summary>Enables or disables a key without changing its secret. Returns false if
    /// the key does not exist.</summary>
    /// <param name="keyId">Identifier of the key to update.</param>
    /// <param name="enabled">True to enable the key; false to disable it.</param>
    /// <param name="ct">Token to observe for cancellation.</param>
    /// <returns>A task that resolves to <c>true</c> if the key was updated; <c>false</c> if the key does not exist.</returns>
    Task<bool> SetEnabledAsync(string keyId, bool enabled, CancellationToken ct = default);

    /// <summary>Replaces the method-scope set on a key without changing its secret.
    /// Returns false if the key does not exist.</summary>
    /// <param name="keyId">Identifier of the key to update.</param>
    /// <param name="methods">Replacement set of API method names the key may call.</param>
    /// <param name="ct">Token to observe for cancellation.</param>
    /// <returns>A task that resolves to <c>true</c> if the key's method scope was replaced; <c>false</c> if the key does not exist.</returns>
    Task<bool> SetMethodsAsync(
        string keyId, IReadOnlyCollection<string> methods, CancellationToken ct = default);

    /// <summary>Removes a key (revoke-then-delete). Returns false if the key could not be
    /// deleted.</summary>
    /// <param name="keyId">Identifier of the key to delete.</param>
    /// <param name="ct">Token to observe for cancellation.</param>
    /// <returns>A task that resolves to <c>true</c> if the key was deleted; <c>false</c> if it could not be deleted.</returns>
    Task<bool> DeleteAsync(string keyId, CancellationToken ct = default);

    /// <summary>Returns the method-scope set for a key, or an empty list if not found.</summary>
    /// <remarks>Enumerates the full key list (O(n)); intended for admin-scale use, not hot paths.</remarks>
    /// <param name="keyId">Identifier of the key whose method scope to retrieve.</param>
    /// <param name="ct">Token to observe for cancellation.</param>
    /// <returns>A task that resolves to the method names the key is scoped to, or an empty list if the key does not exist.</returns>
    Task<IReadOnlyList<string>> GetMethodsForKeyAsync(string keyId, CancellationToken ct = default);

    /// <summary>Returns the identifiers of all keys whose scopes contain
    /// <paramref name="methodName"/>.</summary>
    /// <remarks>Enumerates the full key list (O(n)); intended for admin-scale use, not hot paths.</remarks>
    /// <param name="methodName">API method name to search for across all key scopes.</param>
    /// <param name="ct">Token to observe for cancellation.</param>
    /// <returns>A task that resolves to the identifiers of all keys whose scopes include <paramref name="methodName"/>.</returns>
    Task<IReadOnlyList<string>> GetKeysForMethodAsync(string methodName, CancellationToken ct = default);
}
