package repository

import (
	"context"
	"time"
)

// TOTPProfileRecord captures the persisted TOTP state for a member.
//
// SecretCipher is the AES-GCM blob; BackupCodesHash holds bcrypt hashes of
// the plaintext backup codes (single-use).
type TOTPProfileRecord struct {
	Enrolled        bool
	SecretCipher    []byte
	BackupCodesHash []string
	EnrolledAt      int64
}

// TOTPProfileRepository persists per-member TOTP secrets and backup codes.
//
// Implementations must be safe to call concurrently for distinct (tenant, uid)
// pairs; concurrent calls for the same member should be linearised by the
// caller (the usecase layer) before mutation.
type TOTPProfileRepository interface {
	Get(ctx context.Context, tenantID, uid string) (*TOTPProfileRecord, error)
	Save(ctx context.Context, tenantID, uid string, rec *TOTPProfileRecord) error
	Clear(ctx context.Context, tenantID, uid string) error

	// ConsumeBackupCode removes the supplied backup-code hash atomically and
	// returns true when removal succeeded.
	ConsumeBackupCode(ctx context.Context, tenantID, uid, hash string) (bool, error)
	// ReplaceBackupCodes overwrites the backup-code hashes in one shot.
	ReplaceBackupCodes(ctx context.Context, tenantID, uid string, hashes []string) error
}

// TOTPEnrollStore stages a freshly generated secret until the user confirms
// the first code. The blob is short-lived (Redis TTL).
type TOTPEnrollStore interface {
	Save(ctx context.Context, tenantID, uid string, cipher []byte, ttl time.Duration) error
	Get(ctx context.Context, tenantID, uid string) ([]byte, error)
	Delete(ctx context.Context, tenantID, uid string) error
}

// TOTPReplayStore prevents replay of a successful TOTP code within its
// allowed verification window.
type TOTPReplayStore interface {
	// MarkUsed returns true when the key was newly recorded (i.e. the code
	// had not been used yet); false means it was already consumed.
	MarkUsed(ctx context.Context, tenantID, uid string, timestep uint64, ttl time.Duration) (bool, error)
}
