package repository

import (
	"context"
	"time"

	"github.com/google/uuid"
	"github.com/kms/api-key-service/internal/domain"
)

// ApplicationRepository defines the interface for application data operations
type ApplicationRepository interface {
	// Create creates a new application
	Create(ctx context.Context, app *domain.Application) error
	
	// GetByID retrieves an application by its ID
	GetByID(ctx context.Context, appID string) (*domain.Application, error)
	
	// List retrieves applications with pagination
	List(ctx context.Context, limit, offset int) ([]*domain.Application, error)
	
	// Update updates an existing application
	Update(ctx context.Context, appID string, updates *domain.UpdateApplicationRequest) (*domain.Application, error)
	
	// Delete deletes an application
	Delete(ctx context.Context, appID string) error
	
	// Exists checks if an application exists
	Exists(ctx context.Context, appID string) (bool, error)
}

// StaticTokenRepository defines the interface for static token data operations
type StaticTokenRepository interface {
	// Create creates a new static token
	Create(ctx context.Context, token *domain.StaticToken) error
	
	// GetByID retrieves a static token by its ID
	GetByID(ctx context.Context, tokenID uuid.UUID) (*domain.StaticToken, error)
	
	// GetByKeyHash retrieves a static token by its key hash
	GetByKeyHash(ctx context.Context, keyHash string) (*domain.StaticToken, error)
	
	// GetByAppID retrieves all static tokens for an application
	GetByAppID(ctx context.Context, appID string) ([]*domain.StaticToken, error)
	
	// List retrieves static tokens with pagination
	List(ctx context.Context, limit, offset int) ([]*domain.StaticToken, error)
	
	// Delete deletes a static token
	Delete(ctx context.Context, tokenID uuid.UUID) error
	
	// Exists checks if a static token exists
	Exists(ctx context.Context, tokenID uuid.UUID) (bool, error)
}

// PermissionRepository defines the interface for permission data operations
type PermissionRepository interface {
	// CreateAvailablePermission creates a new available permission
	CreateAvailablePermission(ctx context.Context, permission *domain.AvailablePermission) error
	
	// GetAvailablePermission retrieves an available permission by ID
	GetAvailablePermission(ctx context.Context, permissionID uuid.UUID) (*domain.AvailablePermission, error)
	
	// GetAvailablePermissionByScope retrieves an available permission by scope
	GetAvailablePermissionByScope(ctx context.Context, scope string) (*domain.AvailablePermission, error)
	
	// ListAvailablePermissions retrieves available permissions with pagination and filtering
	ListAvailablePermissions(ctx context.Context, category string, includeSystem bool, limit, offset int) ([]*domain.AvailablePermission, error)
	
	// UpdateAvailablePermission updates an available permission
	UpdateAvailablePermission(ctx context.Context, permissionID uuid.UUID, permission *domain.AvailablePermission) error
	
	// DeleteAvailablePermission deletes an available permission
	DeleteAvailablePermission(ctx context.Context, permissionID uuid.UUID) error
	
	// ValidatePermissionScopes checks if all given scopes exist and are valid
	ValidatePermissionScopes(ctx context.Context, scopes []string) ([]string, error) // returns invalid scopes
	
	// GetPermissionHierarchy returns all parent and child permissions for given scopes
	GetPermissionHierarchy(ctx context.Context, scopes []string) ([]*domain.AvailablePermission, error)
}

// GrantedPermissionRepository defines the interface for granted permission operations
type GrantedPermissionRepository interface {
	// GrantPermissions grants multiple permissions to a token
	GrantPermissions(ctx context.Context, grants []*domain.GrantedPermission) error
	
	// GetGrantedPermissions retrieves all granted permissions for a token
	GetGrantedPermissions(ctx context.Context, tokenType domain.TokenType, tokenID uuid.UUID) ([]*domain.GrantedPermission, error)
	
	// GetGrantedPermissionScopes retrieves only the scopes for a token (more efficient)
	GetGrantedPermissionScopes(ctx context.Context, tokenType domain.TokenType, tokenID uuid.UUID) ([]string, error)
	
	// RevokePermission revokes a specific permission from a token
	RevokePermission(ctx context.Context, grantID uuid.UUID, revokedBy string) error
	
	// RevokeAllPermissions revokes all permissions from a token
	RevokeAllPermissions(ctx context.Context, tokenType domain.TokenType, tokenID uuid.UUID, revokedBy string) error
	
	// HasPermission checks if a token has a specific permission
	HasPermission(ctx context.Context, tokenType domain.TokenType, tokenID uuid.UUID, scope string) (bool, error)
	
	// HasAnyPermission checks if a token has any of the specified permissions
	HasAnyPermission(ctx context.Context, tokenType domain.TokenType, tokenID uuid.UUID, scopes []string) (map[string]bool, error)
}

// SessionRepository defines the interface for user session data operations
type SessionRepository interface {
	// Create creates a new user session
	Create(ctx context.Context, session *domain.UserSession) error
	
	// GetByID retrieves a session by its ID
	GetByID(ctx context.Context, sessionID uuid.UUID) (*domain.UserSession, error)
	
	// GetByUserID retrieves all sessions for a user
	GetByUserID(ctx context.Context, userID string) ([]*domain.UserSession, error)
	
	// GetByUserAndApp retrieves sessions for a specific user and application
	GetByUserAndApp(ctx context.Context, userID, appID string) ([]*domain.UserSession, error)
	
	// GetActiveByUserID retrieves all active sessions for a user
	GetActiveByUserID(ctx context.Context, userID string) ([]*domain.UserSession, error)
	
	// List retrieves sessions with filtering and pagination
	List(ctx context.Context, req *domain.SessionListRequest) (*domain.SessionListResponse, error)
	
	// Update updates an existing session
	Update(ctx context.Context, sessionID uuid.UUID, updates *domain.UpdateSessionRequest) error
	
	// UpdateActivity updates the last activity timestamp for a session
	UpdateActivity(ctx context.Context, sessionID uuid.UUID) error
	
	// Revoke revokes a session
	Revoke(ctx context.Context, sessionID uuid.UUID, revokedBy string) error
	
	// RevokeAllByUser revokes all sessions for a user
	RevokeAllByUser(ctx context.Context, userID string, revokedBy string) error
	
	// RevokeAllByUserAndApp revokes all sessions for a user and application
	RevokeAllByUserAndApp(ctx context.Context, userID, appID string, revokedBy string) error
	
	// ExpireOldSessions marks expired sessions as expired
	ExpireOldSessions(ctx context.Context) (int, error)
	
	// DeleteExpiredSessions removes expired sessions older than the specified duration
	DeleteExpiredSessions(ctx context.Context, olderThan time.Duration) (int, error)
	
	// Exists checks if a session exists
	Exists(ctx context.Context, sessionID uuid.UUID) (bool, error)
	
	// GetSessionCount returns the total number of sessions for a user
	GetSessionCount(ctx context.Context, userID string) (int, error)
	
	// GetActiveSessionCount returns the number of active sessions for a user
	GetActiveSessionCount(ctx context.Context, userID string) (int, error)
}

// DatabaseProvider defines the interface for database operations
type DatabaseProvider interface {
	// GetDB returns the underlying database connection
	GetDB() interface{}
	
	// Ping checks the database connection
	Ping(ctx context.Context) error
	
	// Close closes all database connections
	Close() error
	
	// BeginTx starts a database transaction
	BeginTx(ctx context.Context) (TransactionProvider, error)
	
	// Migrate runs database migrations
	Migrate(ctx context.Context, migrationPath string) error
}

// TransactionProvider defines the interface for database transaction operations
type TransactionProvider interface {
	// Commit commits the transaction
	Commit() error
	
	// Rollback rolls back the transaction
	Rollback() error
	
	// GetTx returns the underlying transaction
	GetTx() interface{}
}

// CacheProvider defines the interface for caching operations
type CacheProvider interface {
	// Get retrieves a value from cache
	Get(ctx context.Context, key string) ([]byte, error)
	
	// Set stores a value in cache with expiration
	Set(ctx context.Context, key string, value []byte, expiration time.Duration) error
	
	// Delete removes a value from cache
	Delete(ctx context.Context, key string) error
	
	// Exists checks if a key exists in cache
	Exists(ctx context.Context, key string) (bool, error)
	
	// Flush clears all cache entries
	Flush(ctx context.Context) error
	
	// Close closes the cache connection
	Close() error
}

// TokenProvider defines the interface for token operations
type TokenProvider interface {
	// GenerateUserToken generates a JWT token for user authentication
	GenerateUserToken(ctx context.Context, userToken *domain.UserToken, hmacKey string) (string, error)
	
	// ValidateUserToken validates and parses a JWT token
	ValidateUserToken(ctx context.Context, token string, hmacKey string) (*domain.UserToken, error)
	
	// GenerateStaticToken generates a static API key
	GenerateStaticToken(ctx context.Context) (string, error)
	
	// HashStaticToken creates a secure hash of a static token
	HashStaticToken(ctx context.Context, token string) (string, error)
	
	// ValidateStaticToken validates a static token against its hash
	ValidateStaticToken(ctx context.Context, token, hash string) (bool, error)
	
	// RenewUserToken renews a user token while preserving max validity
	RenewUserToken(ctx context.Context, currentToken *domain.UserToken, renewalDuration time.Duration, hmacKey string) (string, error)
}

// HashProvider defines the interface for cryptographic hashing operations
type HashProvider interface {
	// Hash creates a secure hash of the input
	Hash(ctx context.Context, input string) (string, error)
	
	// Compare compares an input against a hash
	Compare(ctx context.Context, input, hash string) (bool, error)
	
	// GenerateKey generates a secure random key
	GenerateKey(ctx context.Context, length int) (string, error)
}

// LoggerProvider defines the interface for logging operations
type LoggerProvider interface {
	// Info logs an info level message
	Info(ctx context.Context, msg string, fields ...interface{})
	
	// Warn logs a warning level message
	Warn(ctx context.Context, msg string, fields ...interface{})
	
	// Error logs an error level message
	Error(ctx context.Context, msg string, err error, fields ...interface{})
	
	// Debug logs a debug level message
	Debug(ctx context.Context, msg string, fields ...interface{})
	
	// With returns a logger with additional fields
	With(fields ...interface{}) LoggerProvider
}

// ConfigProvider defines the interface for configuration operations
type ConfigProvider interface {
	// GetString retrieves a string configuration value
	GetString(key string) string
	
	// GetInt retrieves an integer configuration value
	GetInt(key string) int
	
	// GetBool retrieves a boolean configuration value
	GetBool(key string) bool
	
	// GetDuration retrieves a duration configuration value
	GetDuration(key string) time.Duration
	
	// GetStringSlice retrieves a string slice configuration value
	GetStringSlice(key string) []string
	
	// IsSet checks if a configuration key is set
	IsSet(key string) bool
	
	// Validate validates all required configuration values
	Validate() error
}

// AuthenticationProvider defines the interface for user authentication
type AuthenticationProvider interface {
	// GetUserID extracts the user ID from the request context/headers
	GetUserID(ctx context.Context) (string, error)
	
	// ValidateUser validates if the user is authentic
	ValidateUser(ctx context.Context, userID string) error
	
	// GetUserClaims retrieves additional user information/claims
	GetUserClaims(ctx context.Context, userID string) (map[string]string, error)
	
	// Name returns the provider name for identification
	Name() string
}

// RateLimitProvider defines the interface for rate limiting operations
type RateLimitProvider interface {
	// Allow checks if a request should be allowed for the given identifier
	Allow(ctx context.Context, identifier string) (bool, error)
	
	// Remaining returns the number of remaining requests for the identifier
	Remaining(ctx context.Context, identifier string) (int, error)
	
	// Reset returns when the rate limit will reset for the identifier
	Reset(ctx context.Context, identifier string) (time.Time, error)
}

// MetricsProvider defines the interface for metrics collection
type MetricsProvider interface {
	// IncrementCounter increments a counter metric
	IncrementCounter(ctx context.Context, name string, labels map[string]string)
	
	// RecordHistogram records a value in a histogram
	RecordHistogram(ctx context.Context, name string, value float64, labels map[string]string)
	
	// SetGauge sets a gauge metric value
	SetGauge(ctx context.Context, name string, value float64, labels map[string]string)
	
	// RecordDuration records the duration of an operation
	RecordDuration(ctx context.Context, name string, duration time.Duration, labels map[string]string)
}
