BareGit
#pragma once

#include <cstdint>
#include <optional>
#include <string>

#include <mw/crypto.hpp>
#include <mw/error.hpp>
#include <mw/url.hpp>

#include "clock.h"
#include "data.h"
#include "email_sender.h"

/// Authentication-email throttle result with an HTTP retry delay.
struct AuthenticationRateLimitError
{
    /// Non-secret user-facing diagnostic.
    std::string msg;

    /// Whole seconds until another reservation may succeed.
    std::int64_t retry_after;
};

/// Raw session credential returned once after successful confirmation.
struct EstablishedSession
{
    /// Current authenticated user.
    User user;

    /// Raw session token placed only in the browser cookie.
    std::string token;

    /// Raw CSRF token included in authenticated mutation forms.
    std::string csrf_token;

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

/// Own passwordless challenges, delivery, sessions, and logout.
class AuthenticationService
{
public:
    /// Construct authentication over injected persistence and boundaries.
    AuthenticationService(
        DataSourceInterface& data_source,
        EmailSenderInterface& email_sender,
        ClockInterface& clock,
        mw::CryptoInterface& crypto,
        mw::URL base_url,
        std::uint32_t daily_attempt_limit);

    /// Reserve capacity, persist a challenge, and deliver its link.
    mw::E<void> requestEmail(const std::string& submitted_email);

    /// Read a valid delivered challenge without consuming it.
    mw::E<std::optional<AuthenticationChallenge>> validate(
        const std::string& token) const;

    /// Consume a challenge, create or load its user, and start a session.
    mw::E<EstablishedSession> confirm(
        const std::string& token,
        const std::optional<std::string>& current_session_token =
            std::nullopt);

    /// Load a valid non-sliding session from a raw cookie value.
    mw::E<std::optional<SessionContext>> session(
        const std::string& token) const;

    /// Revoke only the presented current session.
    mw::E<void> logout(const std::string& token);

private:
    /// Return current signed Unix seconds.
    std::int64_t nowSeconds() const;

    /// Build the absolute confirmation URL for one raw token.
    mw::URL confirmationUrl(const std::string& token) const;

    DataSourceInterface& data_source_;
    EmailSenderInterface& email_sender_;
    ClockInterface& clock_;
    mw::CryptoInterface& crypto_;
    mw::URL base_url_;
    std::uint32_t daily_attempt_limit_;
};