BareGit
#include "data.h"

#include <string>
#include <utility>

namespace
{

mw::Error unsupportedVersionError(std::int64_t version)
{
    return mw::runtimeError(
        "Database schema version " + std::to_string(version) +
        " is newer than supported version " +
        std::to_string(DB_SCHEMA_VERSION) + ".");
}

mw::Error migrationVersionError(
    std::int64_t expected_version,
    std::int64_t actual_version)
{
    return mw::runtimeError(
        "Database migration should have produced schema version " +
        std::to_string(expected_version) + ", but produced version " +
        std::to_string(actual_version) + ".");
}

} // namespace

mw::E<std::int64_t> DataSourceTransactionInterface::insertCard(
    [[maybe_unused]] const Card& card,
    [[maybe_unused]] const std::vector<GameFieldValue>& field_values,
    [[maybe_unused]] const std::vector<std::int64_t>& series_ids)
{
    return std::unexpected(mw::runtimeError(
        "Dynamic game card persistence is unavailable"));
}

mw::E<void> DataSourceTransactionInterface::updateCard(
    [[maybe_unused]] const Card& card,
    [[maybe_unused]] const std::vector<GameFieldValue>& field_values,
    [[maybe_unused]] const std::vector<std::int64_t>& series_ids)
{
    return std::unexpected(mw::runtimeError(
        "Dynamic game card persistence is unavailable"));
}

mw::E<std::optional<GameDefinitionSnapshot>>
DataSourceTransactionInterface::getGameDefinitionForUpdate(
    [[maybe_unused]] const std::string& short_name)
{
    return std::unexpected(mw::runtimeError(
        "Game definition transactions are unavailable"));
}

mw::E<GameUsage> DataSourceTransactionInterface::getGameUsage(
    [[maybe_unused]] const std::string& short_name)
{
    return std::unexpected(mw::runtimeError(
        "Game definition transactions are unavailable"));
}

mw::E<void> DataSourceTransactionInterface::insertGame(
    [[maybe_unused]] const Game& game)
{
    return std::unexpected(mw::runtimeError(
        "Game definition transactions are unavailable"));
}

mw::E<bool> DataSourceTransactionInterface::updateGame(
    [[maybe_unused]] const std::string& short_name,
    [[maybe_unused]] const std::string& display_name,
    [[maybe_unused]] const std::string& description,
    [[maybe_unused]] GameVisibility visibility,
    [[maybe_unused]] std::int64_t expected_revision)
{
    return std::unexpected(mw::runtimeError(
        "Game definition transactions are unavailable"));
}

mw::E<void> DataSourceTransactionInterface::deleteGame(
    [[maybe_unused]] const std::string& short_name)
{
    return std::unexpected(mw::runtimeError(
        "Game definition transactions are unavailable"));
}

mw::E<std::int64_t> DataSourceTransactionInterface::insertGameField(
    [[maybe_unused]] const GameField& field,
    [[maybe_unused]] const std::vector<GameChoice>& choices)
{
    return std::unexpected(mw::runtimeError(
        "Game definition transactions are unavailable"));
}

mw::E<void> DataSourceTransactionInterface::updateGameField(
    [[maybe_unused]] std::int64_t field_id,
    [[maybe_unused]] const std::string& label,
    [[maybe_unused]] const std::vector<GameChoice>& choices)
{
    return std::unexpected(mw::runtimeError(
        "Game definition transactions are unavailable"));
}

mw::E<void> DataSourceTransactionInterface::deleteGameField(
    [[maybe_unused]] std::int64_t field_id)
{
    return std::unexpected(mw::runtimeError(
        "Game definition transactions are unavailable"));
}

mw::E<std::int64_t> DataSourceTransactionInterface::countFieldUsage(
    [[maybe_unused]] std::int64_t field_id)
{
    return std::unexpected(mw::runtimeError(
        "Game definition transactions are unavailable"));
}

mw::E<std::int64_t> DataSourceTransactionInterface::countChoiceUsage(
    [[maybe_unused]] std::int64_t field_id,
    [[maybe_unused]] const std::string& value)
{
    return std::unexpected(mw::runtimeError(
        "Game definition transactions are unavailable"));
}

mw::E<void> DataSourceTransactionInterface::updateGameFieldOrder(
    [[maybe_unused]] const std::string& short_name,
    [[maybe_unused]] const std::vector<std::int64_t>& field_ids)
{
    return std::unexpected(mw::runtimeError(
        "Game definition transactions are unavailable"));
}

mw::E<bool> DataSourceTransactionInterface::incrementGameRevision(
    [[maybe_unused]] const std::string& short_name,
    [[maybe_unused]] std::int64_t expected_revision)
{
    return std::unexpected(mw::runtimeError(
        "Game definition transactions are unavailable"));
}

mw::E<bool> DataSourceTransactionInterface::seriesBelongToGame(
    [[maybe_unused]] const std::string& short_name,
    [[maybe_unused]] const std::vector<std::int64_t>& series_ids)
{
    return std::unexpected(mw::runtimeError(
        "Transactional series validation is unavailable"));
}

mw::E<std::optional<User>>
DataSourceTransactionInterface::getUserForUpdate(
    [[maybe_unused]] std::int64_t user_id)
{
    return std::unexpected(mw::runtimeError(
        "User transactions are unavailable"));
}

mw::E<std::optional<User>>
DataSourceTransactionInterface::getUserByEmailKeyForUpdate(
    [[maybe_unused]] const std::string& email_key)
{
    return std::unexpected(mw::runtimeError(
        "User transactions are unavailable"));
}

mw::E<bool> DataSourceTransactionInterface::userOwnsCardForUpdate(
    [[maybe_unused]] std::int64_t user_id,
    [[maybe_unused]] std::int64_t card_id)
{
    return std::unexpected(mw::runtimeError(
        "Collection transactions are unavailable"));
}

mw::E<std::vector<Card>>
DataSourceTransactionInterface::getPoolCardsForUpdate()
{
    return std::unexpected(mw::runtimeError(
        "Collection transactions are unavailable"));
}

mw::E<std::int64_t> DataSourceTransactionInterface::insertUser(
    [[maybe_unused]] const User& user)
{
    return std::unexpected(mw::runtimeError(
        "User transactions are unavailable"));
}

mw::E<bool> DataSourceTransactionInterface::updateUsername(
    [[maybe_unused]] std::int64_t user_id,
    [[maybe_unused]] const std::string& username,
    [[maybe_unused]] const std::string& username_key)
{
    return std::unexpected(mw::runtimeError(
        "User transactions are unavailable"));
}

mw::E<bool> DataSourceTransactionInterface::promoteUser(
    [[maybe_unused]] std::int64_t user_id)
{
    return std::unexpected(mw::runtimeError(
        "User transactions are unavailable"));
}

mw::E<AuthenticationReservation>
DataSourceTransactionInterface::reserveAuthenticationEmail(
    [[maybe_unused]] const std::string& email_key,
    [[maybe_unused]] std::int64_t now,
    [[maybe_unused]] bool use_global_quota,
    [[maybe_unused]] std::int64_t utc_day,
    [[maybe_unused]] std::uint32_t daily_limit)
{
    return std::unexpected(mw::runtimeError(
        "Authentication transactions are unavailable"));
}

mw::E<std::int64_t>
DataSourceTransactionInterface::insertAuthenticationChallenge(
    [[maybe_unused]] const std::string& email,
    [[maybe_unused]] const std::string& email_key,
    [[maybe_unused]] const TokenHash& token_hash,
    [[maybe_unused]] std::int64_t created_at,
    [[maybe_unused]] std::int64_t expires_at)
{
    return std::unexpected(mw::runtimeError(
        "Authentication transactions are unavailable"));
}

mw::E<bool>
DataSourceTransactionInterface::markAuthenticationChallengeDelivered(
    [[maybe_unused]] std::int64_t challenge_id,
    [[maybe_unused]] std::int64_t delivered_at)
{
    return std::unexpected(mw::runtimeError(
        "Authentication transactions are unavailable"));
}

mw::E<void>
DataSourceTransactionInterface::deleteAuthenticationChallenge(
    [[maybe_unused]] std::int64_t challenge_id)
{
    return std::unexpected(mw::runtimeError(
        "Authentication transactions are unavailable"));
}

mw::E<std::optional<AuthenticationChallenge>>
DataSourceTransactionInterface::consumeAuthenticationChallenge(
    [[maybe_unused]] const TokenHash& token_hash,
    [[maybe_unused]] std::int64_t now)
{
    return std::unexpected(mw::runtimeError(
        "Authentication transactions are unavailable"));
}

mw::E<void>
DataSourceTransactionInterface::invalidateAuthenticationChallenges(
    [[maybe_unused]] const std::string& email_key,
    [[maybe_unused]] std::int64_t except_challenge_id,
    [[maybe_unused]] std::int64_t now)
{
    return std::unexpected(mw::runtimeError(
        "Authentication transactions are unavailable"));
}

mw::E<std::int64_t> DataSourceTransactionInterface::insertSession(
    [[maybe_unused]] std::int64_t user_id,
    [[maybe_unused]] const TokenHash& token_hash,
    [[maybe_unused]] const std::string& csrf_token,
    [[maybe_unused]] std::int64_t created_at,
    [[maybe_unused]] std::int64_t expires_at)
{
    return std::unexpected(mw::runtimeError(
        "Authentication transactions are unavailable"));
}

mw::E<void> DataSourceTransactionInterface::deleteSession(
    [[maybe_unused]] const TokenHash& token_hash)
{
    return std::unexpected(mw::runtimeError(
        "Authentication transactions are unavailable"));
}

mw::E<void> DataSourceTransactionInterface::updatePullState(
    [[maybe_unused]] std::int64_t user_id,
    [[maybe_unused]] std::uint32_t stored_pulls,
    [[maybe_unused]] std::int64_t refresh_day)
{
    return std::unexpected(mw::runtimeError(
        "Collection transactions are unavailable"));
}

mw::E<std::int64_t> DataSourceTransactionInterface::incrementHolding(
    [[maybe_unused]] std::int64_t user_id,
    [[maybe_unused]] std::int64_t card_id)
{
    return std::unexpected(mw::runtimeError(
        "Collection transactions are unavailable"));
}

mw::E<std::vector<Card>> DataSourceInterface::getCardsByCreator(
    [[maybe_unused]] std::int64_t creator_user_id,
    [[maybe_unused]] GameContentScope scope) const
{
    return std::unexpected(mw::runtimeError("User data is unavailable"));
}

mw::E<std::vector<Card>> DataSourceInterface::getPoolCards() const
{
    return std::unexpected(mw::runtimeError(
        "Collection data is unavailable"));
}

mw::E<std::optional<User>> DataSourceInterface::getUser(
    [[maybe_unused]] std::int64_t user_id) const
{
    return std::unexpected(mw::runtimeError("User data is unavailable"));
}

mw::E<std::optional<User>> DataSourceInterface::getUserByEmailKey(
    [[maybe_unused]] const std::string& email_key) const
{
    return std::unexpected(mw::runtimeError("User data is unavailable"));
}

mw::E<std::vector<User>> DataSourceInterface::getUsers() const
{
    return std::unexpected(mw::runtimeError("User data is unavailable"));
}

mw::E<std::optional<SessionContext>> DataSourceInterface::getSession(
    [[maybe_unused]] const TokenHash& token_hash,
    [[maybe_unused]] std::int64_t now) const
{
    return std::unexpected(mw::runtimeError(
        "Authentication data is unavailable"));
}

mw::E<std::optional<AuthenticationChallenge>>
DataSourceInterface::getAuthenticationChallenge(
    [[maybe_unused]] const TokenHash& token_hash,
    [[maybe_unused]] std::int64_t now) const
{
    return std::unexpected(mw::runtimeError(
        "Authentication data is unavailable"));
}

mw::E<std::vector<CollectionEntry>> DataSourceInterface::getCollection(
    [[maybe_unused]] std::int64_t user_id,
    [[maybe_unused]] GameContentScope scope) const
{
    return std::unexpected(mw::runtimeError(
        "Collection data is unavailable"));
}

mw::E<bool> DataSourceInterface::userOwnsCard(
    [[maybe_unused]] std::int64_t user_id,
    [[maybe_unused]] std::int64_t card_id) const
{
    return std::unexpected(mw::runtimeError(
        "Collection data is unavailable"));
}

mw::E<User> DataSourceInterface::reconcileAdministrator(
    [[maybe_unused]] const std::string& email,
    [[maybe_unused]] const std::string& email_key,
    [[maybe_unused]] std::int64_t created_at,
    [[maybe_unused]] std::int64_t pull_refresh_day)
{
    return std::unexpected(mw::runtimeError("User data is unavailable"));
}

mw::E<void> DataSourceInterface::cleanupAuthentication(
    [[maybe_unused]] std::int64_t now)
{
    return std::unexpected(mw::runtimeError(
        "Authentication data is unavailable"));
}

mw::E<std::vector<Game>> DataSourceInterface::getGames(
    [[maybe_unused]] GameContentScope scope) const
{
    return std::unexpected(mw::runtimeError("Game data is unavailable"));
}

mw::E<std::optional<GameDefinitionSnapshot>>
DataSourceInterface::getGameDefinition(
    [[maybe_unused]] const std::string& short_name,
    [[maybe_unused]] GameContentScope scope) const
{
    return std::unexpected(mw::runtimeError("Game data is unavailable"));
}

mw::E<std::optional<CardGameFields>>
DataSourceInterface::getCardFieldValues(
    [[maybe_unused]] std::int64_t card_id,
    [[maybe_unused]] GameContentScope scope) const
{
    return std::unexpected(mw::runtimeError("Game data is unavailable"));
}

mw::E<void> DataSourceInterface::migrateToLatest()
{
    auto version_result = getSchemaVersion();
    if(!version_result)
    {
        return std::unexpected(std::move(version_result.error()));
    }

    std::int64_t version = *version_result;
    if(version > DB_SCHEMA_VERSION)
    {
        return std::unexpected(unsupportedVersionError(version));
    }

    while(version < DB_SCHEMA_VERSION)
    {
        const std::int64_t expected_version = version + 1;
        mw::E<void> migration_result = migrateSchema0To1();
        if(!migration_result)
        {
            return std::unexpected(std::move(migration_result.error()));
        }

        version_result = getSchemaVersion();
        if(!version_result)
        {
            return std::unexpected(std::move(version_result.error()));
        }

        version = *version_result;
        if(version != expected_version)
        {
            return std::unexpected(
                migrationVersionError(expected_version, version));
        }
    }

    return {};
}