package models

import (
	"time"

	"github.com/google/uuid"
)

type Token struct {
	AccessToken           string    `json:"access_token"`
	AccessTokenExpiresAt  time.Time `json:"access_token_expires_at"`
	RefreshToken          string    `json:"refresh_token"`
	RefreshTokenExpiresAt time.Time `json:"refresh_token_expires_at"`
}

// LoginResult is what LoginConfirm returns: either the full token pair, or a
// 2FA challenge (a short-lived, single-use pending token instead of a session).
// The embedded *Token is nil when TwoFARequired (its fields are then omitted).
type LoginResult struct {
	*Token
	TwoFARequired bool   `json:"two_fa_required,omitempty"`
	PendingToken  string `json:"pending_token,omitempty"`
	ExpiresIn     int    `json:"expires_in,omitempty"`
}

// TwoFAPending is the Redis-backed state for an in-flight 2FA login challenge,
// keyed by the pending session id. It binds the pending JWT's nonce (single-use)
// and counts attempts (brute-force guard, since RateLimitMiddleware is a no-op
// pre-login).
type TwoFAPending struct {
	UserID uuid.UUID `json:"user_id"`
	Nonce  string    `json:"nonce"`
	Tries  int       `json:"tries"`
}

type Session struct {
	ID     uuid.UUID `json:"id"`
	UserID uuid.UUID `json:"user_id"`

	// Current organization context for multi-org support
	CurrentOrganizationID *uuid.UUID `json:"current_organization_id,omitempty"`

	LocationCity        string `json:"location_city"`
	LocationRegion      string `json:"location_region"`
	LocationCountry     string `json:"location_country"`
	LocationCountryCode string `json:"location_country_code"`
	LocationPostalCode  string `json:"location_postal_code"`

	BrowserName string `json:"browser_name"`
	OSName      string `json:"os_name"`

	// How this session authenticated: email, google, apple, webauthn.
	AuthProvider string `json:"auth_provider"`

	CreatedAt time.Time  `json:"created_at"`
	RevokedAt *time.Time `json:"revoked_at"`
	ExpiresAt *time.Time `json:"expires_at"`

	LastRefreshedAt time.Time `json:"last_refreshed_at"`
	RefreshNonce    string    `json:"refresh_nonce"`
	AccessNonce     string    `json:"access_nonce"`
}

// AuthSession is what LoginStart and RegistrationStart return.
//
// Session plus CodeRequired=true is the original two-step flow: a code was
// emailed and the caller POSTs it to the matching /confirm endpoint. When the
// deployment has AUTH_LOGIN_CODE off, or the device is already known, no code
// is sent and the remaining fields carry the completed login instead. The extra
// fields are additive, so a client that only reads `session` still works.
type AuthSession struct {
	Session string `json:"session,omitempty"`

	CodeRequired bool `json:"code_required"`

	Token         *Token `json:"token,omitempty"`
	TwoFARequired bool   `json:"two_fa_required,omitempty"`
	PendingToken  string `json:"pending_token,omitempty"`
	ExpiresIn     int    `json:"expires_in,omitempty"`
}

type LoginSession struct {
	CodeHash string `json:"code_hash"`
	Tries    int    `json:"tries"`
	Nonce    string `json:"nonce"`
	// AnomalyReason carries the verdict that CAUSED this challenge, so the
	// completed sign-in is recorded as the anomaly it was. Recomputing it at
	// confirm would judge different history, and possibly a different address,
	// and could store a challenged sign-in as clean — which is exactly the
	// accounting the repeat threshold depends on.
	AnomalyReason string `json:"anomaly_reason,omitempty"`
}

type RegistrationSession struct {
	PasswordHash string `json:"password_hash"`
	CodeHash     string `json:"code_hash"`
	Tries        int    `json:"tries"`
	Nonce        string `json:"nonce"`
	// ReferralCode is the optional referral code captured at RegistrationStart,
	// applied for attribution once the account + org are created at confirm.
	ReferralCode string `json:"referral_code,omitempty"`
	// Invite is the invitation token captured at RegistrationStart, re-checked
	// and redeemed at confirm so the account lands in the inviting org.
	Invite string `json:"invite,omitempty"`
}
