BareGit
#pragma once

#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <vector>

#include <mw/error.hpp>

#include "card.h"
#include "game.h"
#include "user.h"

/// Raw SHA-256 credential digest stored as a 32-byte SQLite BLOB.
using TokenHash = std::vector<unsigned char>;

/// One distinct card and the quantity owned by a user.
struct CollectionEntry
{
    /// Current card presentation.
    Card card;

    /// Positive quantity owned.
    std::int64_t quantity;
};

/// Delivered, unconsumed authentication challenge metadata.
struct AuthenticationChallenge
{
    /// Internal challenge identity.
    std::int64_t id;

    /// Validated delivery spelling.
    std::string email;

    /// Normalized account identity.
    std::string email_key;

    /// Unix expiry timestamp.
    std::int64_t expires_at;
};

/// Valid session joined with its current user record.
struct SessionContext
{
    /// Internal session identity.
    std::int64_t session_id;

    /// Current user record.
    User user;

    /// Independent authenticated-form CSRF credential.
    std::string csrf_token;

    /// Non-sliding Unix expiry timestamp.
    std::int64_t expires_at;
};

/// Result of atomically reserving authentication email capacity.
enum class AuthenticationReservationStatus
{
    RESERVED,
    EMAIL_LIMITED,
    GLOBAL_LIMITED
};

/// Reservation status and retry delay for a rejected request.
struct AuthenticationReservation
{
    /// Atomic reservation result.
    AuthenticationReservationStatus status;

    /// Seconds until another attempt may succeed.
    std::int64_t retry_after;
};

/// Latest schema version implemented by this binary.
inline constexpr std::int64_t DB_SCHEMA_VERSION = 1;

/// Storage transaction used for an atomic group of persistence operations.
class DataSourceTransactionInterface
{
public:
    /// Destroy the transaction, rolling it back when it remains uncommitted.
    virtual ~DataSourceTransactionInterface() = default;

    /// Allocate and persist the next never-reused number for one game.
    virtual mw::E<std::uint64_t>
    allocateGameNumber(const std::string& game_short_name) = 0;

    /// Ensure a database-defined game has a persistent sequence row.
    virtual mw::E<void>
    ensureGameSequence(const std::string& game_short_name) = 0;

    /// Read a complete game definition while holding the writer lock.
    virtual mw::E<std::optional<GameDefinitionSnapshot>>
    getGameDefinitionForUpdate(const std::string& short_name);

    /// Return deletion-relevant usage for one game.
    virtual mw::E<GameUsage> getGameUsage(const std::string& short_name);

    /// Insert a game and its zero-valued number sequence.
    virtual mw::E<void> insertGame(const Game& game);

    /// Replace mutable game metadata and advance a matching revision.
    virtual mw::E<bool> updateGame(
        const std::string& short_name,
        const std::string& display_name,
        const std::string& description,
        GameVisibility visibility,
        std::int64_t expected_revision);

    /// Delete an unused game, its fields, choices, and sequence.
    virtual mw::E<void> deleteGame(const std::string& short_name);

    /// Insert a field and its initial ordered choices.
    virtual mw::E<std::int64_t> insertGameField(
        const GameField& field,
        const std::vector<GameChoice>& choices);

    /// Replace a field label and its complete ordered choice list.
    virtual mw::E<void> updateGameField(
        std::int64_t field_id,
        const std::string& label,
        const std::vector<GameChoice>& choices);

    /// Delete a field that has no stored card values.
    virtual mw::E<void> deleteGameField(std::int64_t field_id);

    /// Return the number of cards with a value for a field.
    virtual mw::E<std::int64_t> countFieldUsage(std::int64_t field_id);

    /// Return the number of cards using one exact choice.
    virtual mw::E<std::int64_t> countChoiceUsage(
        std::int64_t field_id, const std::string& value);

    /// Replace every field position for a game.
    virtual mw::E<void> updateGameFieldOrder(
        const std::string& short_name,
        const std::vector<std::int64_t>& field_ids);

    /// Return whether every requested series belongs to one game.
    virtual mw::E<bool> seriesBelongToGame(
        const std::string& short_name,
        const std::vector<std::int64_t>& series_ids);

    /// Advance an aggregate definition revision if it is still current.
    virtual mw::E<bool> incrementGameRevision(
        const std::string& short_name,
        std::int64_t expected_revision);

    /// Return whether a loose-card number already exists.
    virtual mw::E<bool> looseNumberExists(std::uint32_t number) = 0;

    /// Re-read a user while the transaction lock is held.
    virtual mw::E<std::optional<User>>
    getUserForUpdate(std::int64_t user_id);

    /// Re-read a user by normalized email while the lock is held.
    virtual mw::E<std::optional<User>> getUserByEmailKeyForUpdate(
        const std::string& email_key);

    /// Re-read a card while the transaction lock is held.
    virtual mw::E<std::optional<Card>>
    getCardForUpdate(std::int64_t card_id) = 0;

    /// Return whether a user owns a card while the lock is held.
    virtual mw::E<bool> userOwnsCardForUpdate(
        std::int64_t user_id, std::int64_t card_id);

    /// Return the positive-rarity pool in card-ID order under the lock.
    virtual mw::E<std::vector<Card>> getPoolCardsForUpdate();

    /// Insert a newly confirmed account and return its internal ID.
    virtual mw::E<std::int64_t> insertUser(const User& user);

    /// Atomically replace a user's normalized username pair.
    virtual mw::E<bool> updateUsername(
        std::int64_t user_id,
        const std::string& username,
        const std::string& username_key);

    /// Conditionally promote one player to creator.
    virtual mw::E<bool> promoteUser(std::int64_t user_id);

    /// Reserve per-email and optional global delivery capacity.
    virtual mw::E<AuthenticationReservation> reserveAuthenticationEmail(
        const std::string& email_key,
        std::int64_t now,
        bool use_global_quota,
        std::int64_t utc_day,
        std::uint32_t daily_limit);

    /// Insert a pending authentication challenge and return its ID.
    virtual mw::E<std::int64_t> insertAuthenticationChallenge(
        const std::string& email,
        const std::string& email_key,
        const TokenHash& token_hash,
        std::int64_t created_at,
        std::int64_t expires_at);

    /// Activate a pending challenge after successful delivery.
    virtual mw::E<bool> markAuthenticationChallengeDelivered(
        std::int64_t challenge_id, std::int64_t delivered_at);

    /// Delete one challenge after failed delivery.
    virtual mw::E<void> deleteAuthenticationChallenge(
        std::int64_t challenge_id);

    /// Atomically consume one valid delivered challenge.
    virtual mw::E<std::optional<AuthenticationChallenge>>
    consumeAuthenticationChallenge(
        const TokenHash& token_hash, std::int64_t now);

    /// Invalidate every other outstanding challenge for an email key.
    virtual mw::E<void> invalidateAuthenticationChallenges(
        const std::string& email_key,
        std::int64_t except_challenge_id,
        std::int64_t now);

    /// Insert a four-week session and return its internal ID.
    virtual mw::E<std::int64_t> insertSession(
        std::int64_t user_id,
        const TokenHash& token_hash,
        const std::string& csrf_token,
        std::int64_t created_at,
        std::int64_t expires_at);

    /// Delete a session by its token digest.
    virtual mw::E<void> deleteSession(const TokenHash& token_hash);

    /// Persist a lazily refreshed pull state.
    virtual mw::E<void> updatePullState(
        std::int64_t user_id,
        std::uint32_t stored_pulls,
        std::int64_t refresh_day);

    /// Insert or increment a holding and return its new quantity.
    virtual mw::E<std::int64_t> incrementHolding(
        std::int64_t user_id, std::int64_t card_id);

    /// Insert a card with generic custom values and series memberships.
    virtual mw::E<std::int64_t> insertCard(
        const Card& card,
        const std::vector<GameFieldValue>& field_values,
        const std::vector<std::int64_t>& series_ids);

    /// Replace a card and all of its generic custom values and memberships.
    virtual mw::E<void> updateCard(
        const Card& card,
        const std::vector<GameFieldValue>& field_values,
        const std::vector<std::int64_t>& series_ids);

    /// Delete a card and its dependent database rows.
    virtual mw::E<void> deleteCard(std::int64_t card_id) = 0;

    /// Insert a series and return its internal ID.
    virtual mw::E<std::int64_t> insertSeries(const Series& series) = 0;

    /// Replace a series name and description without changing its game.
    virtual mw::E<void> updateSeries(const Series& series) = 0;

    /// Delete a series and its membership rows.
    virtual mw::E<void> deleteSeries(std::int64_t series_id) = 0;

    /// Commit the transaction and release its lock.
    virtual mw::E<void> commit() = 0;
};

/// Common persistence API used by the application and services.
class DataSourceInterface
{
public:
    /// Destroy the data source after all transactions have ended.
    virtual ~DataSourceInterface() = default;

    /// Return the stored schema version.
    virtual mw::E<std::int64_t> getSchemaVersion() const = 0;

    /// Apply every required migration through the current schema version.
    mw::E<void> migrateToLatest();

    /// Migrate an empty version-0 database to schema version 1.
    virtual mw::E<void> migrateSchema0To1() = 0;

    /// Start an immediate transaction with exclusive mutation ownership.
    virtual mw::E<std::unique_ptr<DataSourceTransactionInterface>>
    beginTransaction() = 0;

    /// Return all cards for the unpaginated index.
    virtual mw::E<std::vector<Card>> getCards(
        GameContentScope scope) const = 0;

    /// Return all cards authored by one user.
    virtual mw::E<std::vector<Card>> getCardsByCreator(
        std::int64_t creator_user_id,
        GameContentScope scope) const;

    /// Return every current positive-rarity card in card-ID order.
    virtual mw::E<std::vector<Card>> getPoolCards() const;

    /// Return a card by its parsed identity.
    virtual mw::E<std::optional<Card>>
    getCard(
        const CardIdentity& identity,
        GameContentScope scope) const = 0;

    /// Return games ordered by display name and then short name.
    virtual mw::E<std::vector<Game>> getGames(
        GameContentScope scope) const;

    /// Return a complete, consistently read game definition.
    virtual mw::E<std::optional<GameDefinitionSnapshot>>
    getGameDefinition(
        const std::string& short_name,
        GameContentScope scope) const;

    /// Return a card's definition and ordered custom values together.
    virtual mw::E<std::optional<CardGameFields>>
    getCardFieldValues(
        std::int64_t card_id,
        GameContentScope scope) const;

    /// Return a user by internal identity.
    virtual mw::E<std::optional<User>> getUser(
        std::int64_t user_id) const;

    /// Return a user by normalized immutable email identity.
    virtual mw::E<std::optional<User>> getUserByEmailKey(
        const std::string& email_key) const;

    /// Return every user for administrator management.
    virtual mw::E<std::vector<User>> getUsers() const;

    /// Return a valid session joined with its current user.
    virtual mw::E<std::optional<SessionContext>> getSession(
        const TokenHash& token_hash, std::int64_t now) const;

    /// Read one valid delivered challenge without consuming it.
    virtual mw::E<std::optional<AuthenticationChallenge>>
    getAuthenticationChallenge(
        const TokenHash& token_hash, std::int64_t now) const;

    /// Return a user's distinct collection entries.
    virtual mw::E<std::vector<CollectionEntry>> getCollection(
        std::int64_t user_id,
        GameContentScope scope) const;

    /// Return whether a user currently owns a card.
    virtual mw::E<bool> userOwnsCard(
        std::int64_t user_id, std::int64_t card_id) const;

    /// Reconcile the one immutable configured administrator identity.
    virtual mw::E<User> reconcileAdministrator(
        const std::string& email,
        const std::string& email_key,
        std::int64_t created_at,
        std::int64_t pull_refresh_day);

    /// Best-effort removal of obsolete authentication persistence rows.
    virtual mw::E<void> cleanupAuthentication(std::int64_t now);

    /// Return all series, ordered by game and name.
    virtual mw::E<std::vector<Series>> getSeries(
        GameContentScope scope) const = 0;

    /// Return one series by internal ID.
    virtual mw::E<std::optional<Series>>
    getSeries(
        std::int64_t series_id,
        GameContentScope scope) const = 0;

    /// Return the series memberships for one card.
    virtual mw::E<std::vector<std::int64_t>>
    getCardSeries(std::int64_t card_id) const = 0;

protected:
    /// Set the schema version inside a concrete migration transaction.
    virtual mw::E<void> setSchemaVersion(std::int64_t version) = 0;
};