package auth

import (
	"crypto/rand"
	"encoding/hex"
	"errors"
	"sync"
	"time"
)

var (
	ErrInvalidToken = errors.New("invalid token")
	ErrTokenExpired = errors.New("token expired")
)

// TokenInfo holds information about a token
type TokenInfo struct {
	Token       string
	UserID      string
	Permissions []string
	CreatedAt   time.Time
	ExpiresAt   *time.Time
	Revoked     bool
}

// TokenManager manages authentication tokens
type TokenManager struct {
	tokens map[string]*TokenInfo
	mu     sync.RWMutex
}

// NewTokenManager creates a new token manager
func NewTokenManager() *TokenManager {
	return &TokenManager{
		tokens: make(map[string]*TokenInfo),
	}
}

// GenerateToken generates a new random token
func GenerateToken() (string, error) {
	b := make([]byte, 32)
	if _, err := rand.Read(b); err != nil {
		return "", err
	}
	return hex.EncodeToString(b), nil
}

// AddToken adds a new token to the manager
func (tm *TokenManager) AddToken(token string, userID string, permissions []string) {
	tm.mu.Lock()
	defer tm.mu.Unlock()

	tm.tokens[token] = &TokenInfo{
		Token:       token,
		UserID:      userID,
		Permissions: permissions,
		CreatedAt:   time.Now(),
		ExpiresAt:   nil, // No expiration for MVP
		Revoked:     false,
	}
}

// ValidateToken validates a token and returns its info
func (tm *TokenManager) ValidateToken(token string) (*TokenInfo, error) {
	tm.mu.RLock()
	defer tm.mu.RUnlock()

	info, exists := tm.tokens[token]
	if !exists {
		return nil, ErrInvalidToken
	}

	if info.Revoked {
		return nil, ErrInvalidToken
	}

	if info.ExpiresAt != nil && time.Now().After(*info.ExpiresAt) {
		return nil, ErrTokenExpired
	}

	return info, nil
}

// RevokeToken revokes a token
func (tm *TokenManager) RevokeToken(token string) error {
	tm.mu.Lock()
	defer tm.mu.Unlock()

	info, exists := tm.tokens[token]
	if !exists {
		return ErrInvalidToken
	}

	info.Revoked = true
	return nil
}

// ListTokens returns a list of all tokens (excluding sensitive data)
func (tm *TokenManager) ListTokens() []*TokenInfo {
	tm.mu.RLock()
	defer tm.mu.RUnlock()

	var result []*TokenInfo
	for _, info := range tm.tokens {
		result = append(result, info)
	}
	return result
}

// HasPermission checks if a token has a specific permission
func (tm *TokenManager) HasPermission(token string, permission string) (bool, error) {
	info, err := tm.ValidateToken(token)
	if err != nil {
		return false, err
	}

	// Admin has all permissions
	for _, p := range info.Permissions {
		if p == "admin" || p == permission {
			return true, nil
		}
	}

	return false, nil
}
