#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_;
};