// Package auth provides API key authentication for rdev.
package auth

import (
	"crypto/rand"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"strings"
	"time"
)

const (
	// KeyPrefix is the prefix for all rdev API keys.
	KeyPrefix = "rdev_sk_"

	// KeyRandomLength is the length of the random portion of the key.
	KeyRandomLength = 32

	// KeyIdentifierLength is the length of the identifier portion.
	KeyIdentifierLength = 8
)

// Expiration durations for API keys.
var (
	Expiration30Days  = 30 * 24 * time.Hour
	Expiration60Days  = 60 * 24 * time.Hour
	Expiration90Days  = 90 * 24 * time.Hour
	Expiration1Year   = 365 * 24 * time.Hour
	ExpirationNoLimit time.Duration = 0
)

// ParseExpiration converts an expiration string to duration.
// Valid values: "30d", "60d", "90d", "1y", "never"
func ParseExpiration(s string) (time.Duration, error) {
	switch strings.ToLower(s) {
	case "30d", "30":
		return Expiration30Days, nil
	case "60d", "60":
		return Expiration60Days, nil
	case "90d", "90":
		return Expiration90Days, nil
	case "1y", "1year", "365d":
		return Expiration1Year, nil
	case "never", "none", "":
		return ExpirationNoLimit, nil
	default:
		return 0, fmt.Errorf("invalid expiration: %s (use 30d, 60d, 90d, 1y, or never)", s)
	}
}

// ExpiresAt calculates the expiration timestamp from a duration.
// Returns nil if duration is 0 (no expiration).
func ExpiresAt(d time.Duration) *time.Time {
	if d == 0 {
		return nil
	}
	t := time.Now().Add(d)
	return &t
}

// GenerateKey creates a new API key with format: rdev_sk_<identifier>_<random>
// Returns the full key (to show once) and the identifier (for storage).
func GenerateKey() (fullKey, identifier string, err error) {
	// Generate identifier (8 chars)
	idBytes := make([]byte, KeyIdentifierLength/2)
	if _, err := rand.Read(idBytes); err != nil {
		return "", "", fmt.Errorf("generate identifier: %w", err)
	}
	identifier = hex.EncodeToString(idBytes)

	// Generate random portion (32 chars)
	randomBytes := make([]byte, KeyRandomLength/2)
	if _, err := rand.Read(randomBytes); err != nil {
		return "", "", fmt.Errorf("generate random: %w", err)
	}
	random := hex.EncodeToString(randomBytes)

	fullKey = fmt.Sprintf("%s%s_%s", KeyPrefix, identifier, random)
	return fullKey, identifier, nil
}

// HashKey computes the SHA-256 hash of an API key.
func HashKey(key string) string {
	h := sha256.Sum256([]byte(key))
	return hex.EncodeToString(h[:])
}

// ValidateKeyFormat checks if a key has the correct format.
func ValidateKeyFormat(key string) bool {
	if !strings.HasPrefix(key, KeyPrefix) {
		return false
	}

	// Format: rdev_sk_<8-char-id>_<32-char-random>
	parts := strings.Split(strings.TrimPrefix(key, KeyPrefix), "_")
	if len(parts) != 2 {
		return false
	}

	if len(parts[0]) != KeyIdentifierLength {
		return false
	}

	if len(parts[1]) != KeyRandomLength {
		return false
	}

	return true
}

// ExtractPrefix extracts the identifier prefix from a key.
func ExtractPrefix(key string) string {
	if !strings.HasPrefix(key, KeyPrefix) {
		return ""
	}
	trimmed := strings.TrimPrefix(key, KeyPrefix)
	parts := strings.Split(trimmed, "_")
	if len(parts) < 1 {
		return ""
	}
	return parts[0]
}
