package system

import (
	"context"
	"errors"
	"io"
	"time"
)

// Cache is the shared cache seam. The data implementation uses Redis when it
// is reachable and falls back to process memory for local development.
type Cache interface {
	Get(context.Context, string) (string, bool, error)
	Set(context.Context, string, string, time.Duration) error
	Delete(context.Context, string) error
	Increment(context.Context, string, time.Duration) (int64, error)
}

type StoredFile struct {
	Name         string
	Path         string
	URL          string
	Size         int64
	LastModified time.Time
	ContentType  string
}

// FileStorage owns the persistence boundary for uploaded files.
type FileStorage interface {
	Put(context.Context, string, io.Reader) (*StoredFile, error)
	Open(context.Context, string) (io.ReadCloser, error)
	Delete(context.Context, string) error
	Compose(context.Context, []string, string) (*StoredFile, string, error)
	DeletePrefix(context.Context, string) error
	List(context.Context, string, string, int) ([]*StoredFile, string, bool, error)
}

type JWTSettings struct {
	SigningKey string
	Issuer     string
	Expires    time.Duration
	Buffer     time.Duration
}

type CaptchaSettings struct {
	KeyLong         int
	ImageWidth      int
	ImageHeight     int
	StoreExpiration time.Duration
}

type MediaSettings struct {
	SessionTTL  int
	MaxFileSize int64
	ChunkDir    string
}

const DefaultMaxMediaFileSize int64 = 100 << 20

func (s MediaSettings) EffectiveMaxFileSize() int64 {
	if s.MaxFileSize > 0 {
		return s.MaxFileSize
	}
	return DefaultMaxMediaFileSize
}

// RuntimeSettings exposes only the active values needed by the application.
// The data implementation resolves every call from conf.Runtime so hot reloads
// take effect without rebuilding services.
type RuntimeSettings interface {
	RouterPrefix() string
	JWTSettings() JWTSettings
	CaptchaSettings() CaptchaSettings
	MediaSettings() MediaSettings
	UseMultipoint() bool
}

type IssuedToken struct {
	Value     string
	ExpiresAt time.Time
	TTL       time.Duration
}

type AuthClaims struct {
	UUID            string
	ID              uint
	Username        string
	NickName        string
	AuthorityID     uint
	UserType        string
	BufferTime      time.Duration
	MustChangePwd   bool
	PasswordVersion int64
	Issuer          string
	Audience        []string
	IssuedAt        time.Time
	NotBefore       time.Time
	ExpiresAt       time.Time
}

var (
	ErrTokenExpired          = errors.New("token expired")
	ErrTokenMalformed        = errors.New("token malformed")
	ErrTokenSignatureInvalid = errors.New("token signature invalid")
	ErrTokenNotValidYet      = errors.New("token not valid yet")
	ErrTokenInvalid          = errors.New("token invalid")
	ErrTokenDisabled         = errors.New("token disabled")
)

type TokenIssuer interface {
	IssueToken(*User, uint, bool, time.Duration) (*IssuedToken, error)
	ReissueToken(*AuthClaims, uint) (*IssuedToken, error)
	ParseToken(string) (*AuthClaims, error)
}
