BareGit
#pragma once

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

#include <mw/error.hpp>

#include "asset_store.h"
#include "authorization.h"
#include "data.h"
#include "image_processor.h"
#include "markdown_renderer.h"
#include "non_secret_random.h"

/// Validated common fields and staged files for a new card.
struct CreateCardInput
{
    /// Human-readable card name.
    std::string name;

    /// Optional short Markdown description.
    std::optional<std::string> short_description;

    /// Optional long Markdown description.
    std::optional<std::string> long_description;

    /// Nonnegative rarity value.
    std::int64_t rarity;

    /// Complete private staging directory for this request.
    std::filesystem::path staging_directory;

    /// Staged front artwork path.
    std::filesystem::path front;

    /// Optional staged foil-control path.
    std::optional<std::filesystem::path> foil;

    /// Optional browser-rendered thumbnail for a foil card.
    std::optional<std::filesystem::path> thumbnail;

    /// Whether the request explicitly supplied a rarity field.
    bool rarity_was_submitted = true;
};

/// Create-card input retained as the loose-card handler's explicit name.
using CreateLooseCardInput = CreateCardInput;

/// Requested handling for an existing required artwork asset.
enum class FrontAssetAction
{
    KEEP,
    REPLACE
};

/// Requested handling for an existing optional foil-control asset.
enum class FoilAssetAction
{
    KEEP,
    REPLACE,
    REMOVE
};

/// Validated common fields and staged files for editing a loose card.
struct UpdateLooseCardInput
{
    /// Card state observed while rendering the edit form or accepting it.
    Card current_card;

    /// Revision submitted by the browser for optimistic concurrency.
    std::int64_t expected_revision;

    /// Replacement human-readable card name.
    std::string name;

    /// Replacement optional short Markdown description.
    std::optional<std::string> short_description;

    /// Replacement optional long Markdown description.
    std::optional<std::string> long_description;

    /// Replacement nonnegative rarity value.
    std::int64_t rarity;

    /// Complete private staging directory for this request.
    std::filesystem::path staging_directory;

    /// Whether to retain or replace the front artwork.
    FrontAssetAction front_action;

    /// Whether to retain, replace, or remove the foil control.
    FoilAssetAction foil_action;

    /// Staged replacement front artwork, when requested.
    std::optional<std::filesystem::path> front;

    /// Staged replacement foil control, when requested.
    std::optional<std::filesystem::path> foil;

    /// Browser-rendered thumbnail for a changed resulting foil card.
    std::optional<std::filesystem::path> thumbnail;

    /// Whether the request explicitly supplied a rarity field.
    bool rarity_was_submitted = true;
};

/// Coordinate image processing, persistence, and asset publication.
class CardService
{
public:
    /// Construct a card creation service from its owned boundaries.
    CardService(
        DataSourceInterface& data_source,
        NonSecretRandom& random,
        ImageProcessor image_processor,
        AssetStore asset_store);

    /// Create a loose card after re-reading and authorizing its actor.
    mw::E<std::string> createLooseCard(
        std::int64_t actor_user_id, CreateLooseCardInput input);

    /// Create a game card after re-reading and authorizing its actor.
    mw::E<std::string> createGameCard(
        std::int64_t actor_user_id,
        CreateCardInput input,
        std::string game_short_name,
        std::int64_t expected_game_revision,
        SubmittedGameFields submitted_fields,
        const std::vector<std::int64_t>& series_ids);

    /// Update a loose card after re-reading role and authorship.
    mw::E<std::string> updateLooseCard(
        std::int64_t actor_user_id, UpdateLooseCardInput input);

    /// Update a game card after re-reading role and authorship.
    mw::E<std::string> updateGameCard(
        std::int64_t actor_user_id,
        UpdateLooseCardInput input,
        std::int64_t expected_game_revision,
        SubmittedGameFields submitted_fields,
        const std::vector<std::int64_t>& series_ids);

    /// Delete a card only after re-reading an administrator actor.
    mw::E<void> deleteCard(
        std::int64_t actor_user_id, const Card& card);

private:
    /// Create a loose or database-defined game card through one pipeline.
    mw::E<std::string> createCard(
        std::int64_t actor_user_id,
        CreateCardInput input,
        std::optional<std::string> game_short_name,
        std::optional<std::int64_t> expected_game_revision,
        SubmittedGameFields submitted_fields,
        const std::vector<std::int64_t>& series_ids);

    /// Update a loose or database-defined game card through one pipeline.
    mw::E<std::string> updateCard(
        std::int64_t actor_user_id,
        UpdateLooseCardInput input,
        std::optional<std::int64_t> expected_game_revision,
        SubmittedGameFields submitted_fields,
        const std::vector<std::int64_t>& series_ids);

    DataSourceInterface& data_source_;
    NonSecretRandom& random_;
    ImageProcessor image_processor_;
    AssetStore asset_store_;
    MarkdownRenderer markdown_renderer_;
    AuthorizationService authorization_;
};