package config

import (
	"fmt"
	"os"
	"time"

	"gopkg.in/yaml.v2"
)

// Config represents the unified configuration for all services
type Config struct {
	Mode string `yaml:"mode"`

	Gateway      GatewayConfig      `yaml:"gateway"`
	BlossomServer BlossomServerConfig `yaml:"blossom_server"`
	DHT          DHTConfig          `yaml:"dht"`
	Storage      StorageConfig      `yaml:"storage"`
	Blossom      BlossomConfig      `yaml:"blossom"`
	Torrent      TorrentConfig      `yaml:"torrent"`
	Tracker      TrackerConfig      `yaml:"tracker"`
	Nostr        NostrConfig        `yaml:"nostr"`
	Proxy        ProxyConfig        `yaml:"proxy"`
	Admin        AdminConfig        `yaml:"admin"`
	RateLimiting RateLimitingConfig `yaml:"rate_limiting"`
	Branding     BrandingConfig     `yaml:"branding"`
	Transcoding  TranscodingConfig  `yaml:"transcoding"`
}

// GatewayConfig configures the HTTP API gateway
type GatewayConfig struct {
	Enabled       bool   `yaml:"enabled"`
	Port          int    `yaml:"port"`
	MaxUploadSize string `yaml:"max_upload_size"`
	PublicURL     string `yaml:"public_url"`
}

// BlossomServerConfig configures the embedded Blossom server
type BlossomServerConfig struct {
	Enabled     bool        `yaml:"enabled"`
	Port        int         `yaml:"port"`
	StoragePath string      `yaml:"storage_path"`
	MaxBlobSize string      `yaml:"max_blob_size"`
	RateLimit   RateLimit   `yaml:"rate_limit"`
}

// RateLimit configures rate limiting for the Blossom server
type RateLimit struct {
	RequestsPerMinute int `yaml:"requests_per_minute"`
	BurstSize         int `yaml:"burst_size"`
}

// DHTConfig configures the DHT node
type DHTConfig struct {
	Enabled           bool          `yaml:"enabled"`
	Port              int           `yaml:"port"`
	NodeID            string        `yaml:"node_id"`              // auto-generate if empty
	BootstrapSelf     bool          `yaml:"bootstrap_self"`       // add self as bootstrap node
	BootstrapNodes    []string      `yaml:"bootstrap_nodes"`
	AnnounceInterval  time.Duration `yaml:"announce_interval"`    // torrent announce interval
	CleanupInterval   time.Duration `yaml:"cleanup_interval"`     // node cleanup interval
	MaxTorrents       int           `yaml:"max_torrents"`         // max torrents to track
	MaxNodes          int           `yaml:"max_nodes"`            // max nodes to store
	MaxPeersPerTorrent int          `yaml:"max_peers_per_torrent"`
}

// StorageConfig configures shared storage settings
type StorageConfig struct {
	BlobThreshold int64           `yaml:"blob_threshold"`
	ChunkSize     int64           `yaml:"chunk_size"`
	MetadataDB    string          `yaml:"metadata_db"`
	BlobStorage   string          `yaml:"blob_storage"`
	ChunkStorage  string          `yaml:"chunk_storage"`
	Strategy      StorageStrategy `yaml:"strategy"`
}

// StorageStrategy defines how files should be stored based on size
type StorageStrategy struct {
	SmallFiles string `yaml:"small_files"` // "blob"
	LargeFiles string `yaml:"large_files"` // "torrent"
}

// BlossomConfig configures external Blossom servers
type BlossomConfig struct {
	Servers []string `yaml:"servers"`
}

// TorrentConfig configures BitTorrent settings
type TorrentConfig struct {
	Trackers []string `yaml:"trackers"`
}

// TrackerConfig configures the built-in BitTorrent tracker
type TrackerConfig struct {
	Enabled         bool          `yaml:"enabled"`
	AnnounceInterval int          `yaml:"announce_interval"`    // seconds
	MinInterval     int          `yaml:"min_interval"`         // seconds
	DefaultNumWant  int          `yaml:"default_numwant"`      // peers to return
	MaxNumWant      int          `yaml:"max_numwant"`          // maximum peers
	CleanupInterval time.Duration `yaml:"cleanup_interval"`    // cleanup frequency
	PeerTimeout     time.Duration `yaml:"peer_timeout"`        // peer expiration
}

// NostrConfig configures Nostr relay settings
type NostrConfig struct {
	Relays         []string `yaml:"relays"`
	PublishNIP35   bool     `yaml:"publish_nip35"`   // Publish NIP-35 torrent events for DTAN
	PublishNIP71   bool     `yaml:"publish_nip71"`   // Publish NIP-71 video events
	VideoRelays    []string `yaml:"video_relays"`    // Specific relays for video content
	PrivateKey     string   `yaml:"private_key"`     // Hex-encoded private key
	AutoPublish    bool     `yaml:"auto_publish"`    // Auto-publish on upload
	ThumbnailsDir  string   `yaml:"thumbnails_dir"`  // Directory for video thumbnails
}

// ProxyConfig configures smart proxy settings
type ProxyConfig struct {
	Enabled     bool          `yaml:"enabled"`
	CacheSize   int           `yaml:"cache_size"`
	CacheMaxAge time.Duration `yaml:"cache_max_age"`
}

// AdminConfig configures admin functionality
type AdminConfig struct {
	Enabled                  bool     `yaml:"enabled"`
	Pubkeys                  []string `yaml:"pubkeys"`
	AutoCleanup              bool     `yaml:"auto_cleanup"`
	CleanupAge               string   `yaml:"cleanup_age"`
	MaxFileAge               string   `yaml:"max_file_age"`
	ReportThreshold          int      `yaml:"report_threshold"`
	DefaultUserStorageLimit  string   `yaml:"default_user_storage_limit"`
}

// RateLimitingConfig configures rate limiting for different operations
type RateLimitingConfig struct {
	Upload   UploadRateConfig   `yaml:"upload"`
	Download DownloadRateConfig `yaml:"download"`
	Stream   StreamRateConfig   `yaml:"stream"`
	Auth     AuthRateConfig     `yaml:"auth"`
}

// UploadRateConfig configures upload rate limiting
type UploadRateConfig struct {
	RequestsPerSecond float64 `yaml:"requests_per_second"`
	BurstSize         int     `yaml:"burst_size"`
	MaxFileSize       string  `yaml:"max_file_size"`
}

// DownloadRateConfig configures download rate limiting  
type DownloadRateConfig struct {
	RequestsPerSecond float64 `yaml:"requests_per_second"`
	BurstSize         int     `yaml:"burst_size"`
}

// StreamRateConfig configures streaming rate limiting
type StreamRateConfig struct {
	RequestsPerSecond float64 `yaml:"requests_per_second"`
	BurstSize         int     `yaml:"burst_size"`
	MaxConcurrent     int     `yaml:"max_concurrent"`
}

// AuthRateConfig configures authentication rate limiting
type AuthRateConfig struct {
	LoginAttemptsPerMinute int `yaml:"login_attempts_per_minute"`
	BurstSize              int `yaml:"burst_size"`
}

// BrandingConfig configures site branding and appearance
type BrandingConfig struct {
	SiteName    string `yaml:"site_name"`
	LogoURL     string `yaml:"logo_url"`
	LogoWidth   string `yaml:"logo_width"`
	LogoHeight  string `yaml:"logo_height"`
	FaviconURL  string `yaml:"favicon_url"`
	Description string `yaml:"description"`
	FooterText  string `yaml:"footer_text"`
	SupportURL  string `yaml:"support_url"`
}

// TranscodingConfig configures video transcoding
type TranscodingConfig struct {
	Enabled        bool             `yaml:"enabled"`
	ConcurrentJobs int              `yaml:"concurrent_jobs"`
	WorkDir        string           `yaml:"work_dir"`
	AutoTranscode  bool             `yaml:"auto_transcode"`
	MinFileSize    string           `yaml:"min_file_size"`
	Qualities      []QualityConfig  `yaml:"qualities"`
	Retention      RetentionConfig  `yaml:"retention"`
	MaxCPUPercent  int              `yaml:"max_cpu_percent"`
	NiceLevel      int              `yaml:"nice_level"`
}

// QualityConfig represents a transcoding quality preset
type QualityConfig struct {
	Name    string `yaml:"name"`
	Width   int    `yaml:"width"`
	Height  int    `yaml:"height"`
	Bitrate string `yaml:"bitrate"`
}

// RetentionConfig configures file retention policies
type RetentionConfig struct {
	TranscodedFiles string `yaml:"transcoded_files"`
	FailedJobs      string `yaml:"failed_jobs"`
}

// LoadConfig loads configuration from a YAML file
func LoadConfig(filename string) (*Config, error) {
	data, err := os.ReadFile(filename)
	if err != nil {
		return nil, fmt.Errorf("failed to read config file %s: %w", filename, err)
	}

	var config Config
	if err := yaml.Unmarshal(data, &config); err != nil {
		return nil, fmt.Errorf("failed to parse config file %s: %w", filename, err)
	}

	// Set defaults
	if config.Mode == "" {
		config.Mode = "unified"
	}

	// Set branding defaults
	if config.Branding.SiteName == "" {
		config.Branding.SiteName = "BitTorrent Gateway"
	}
	if config.Branding.Description == "" {
		config.Branding.Description = "Decentralized file sharing gateway"
	}

	// Set Nostr defaults
	if len(config.Nostr.Relays) == 0 && (config.Nostr.PublishNIP35 || config.Nostr.PublishNIP71) {
		config.Nostr.Relays = []string{
			"wss://relay.damus.io",
			"wss://nos.lol",
			"wss://relay.nostr.band",
		}
	}
	if len(config.Nostr.VideoRelays) == 0 && config.Nostr.PublishNIP71 {
		config.Nostr.VideoRelays = config.Nostr.Relays // Use same relays by default
	}
	if config.Nostr.ThumbnailsDir == "" {
		config.Nostr.ThumbnailsDir = "data/thumbnails"
	}

	// Set transcoding defaults
	if config.Transcoding.WorkDir == "" {
		config.Transcoding.WorkDir = "./data/transcoded"
	}
	if config.Transcoding.ConcurrentJobs == 0 {
		config.Transcoding.ConcurrentJobs = 2
	}
	if config.Transcoding.MinFileSize == "" {
		config.Transcoding.MinFileSize = "50MB"
	}
	if config.Transcoding.MaxCPUPercent == 0 {
		config.Transcoding.MaxCPUPercent = 80
	}
	if config.Transcoding.NiceLevel == 0 {
		config.Transcoding.NiceLevel = 10
	}

	return &config, nil
}

// IsServiceEnabled checks if a specific service should be enabled based on mode
func (c *Config) IsServiceEnabled(service string) bool {
	switch c.Mode {
	case "unified":
		switch service {
		case "gateway":
			return c.Gateway.Enabled
		case "blossom":
			return c.BlossomServer.Enabled
		case "dht":
			return c.DHT.Enabled
		case "tracker":
			return c.Tracker.Enabled
		}
	case "gateway-only":
		return service == "gateway" && c.Gateway.Enabled
	case "blossom-only":
		return service == "blossom" && c.BlossomServer.Enabled
	case "dht-only":
		return service == "dht" && c.DHT.Enabled
	}
	return false
}

// GetBlobThreshold returns the blob threshold in bytes
func (c *Config) GetBlobThreshold() int64 {
	return c.Storage.BlobThreshold
}

// GetChunkSize returns the chunk size in bytes
func (c *Config) GetChunkSize() int64 {
	return c.Storage.ChunkSize
}

// GetMaxBlobSizeBytes converts the max blob size string to bytes
func (c *Config) GetMaxBlobSizeBytes() (int64, error) {
	return parseSize(c.BlossomServer.MaxBlobSize)
}

// GetMaxUploadSizeBytes converts the max upload size string to bytes
func (c *Config) GetMaxUploadSizeBytes() (int64, error) {
	return parseSize(c.Gateway.MaxUploadSize)
}

// GetDefaultUserStorageLimitBytes converts the default user storage limit to bytes
func (c *Config) GetDefaultUserStorageLimitBytes() (int64, error) {
	if c.Admin.DefaultUserStorageLimit == "" {
		return 10 * 1024 * 1024 * 1024, nil // 10GB default
	}
	return parseSize(c.Admin.DefaultUserStorageLimit)
}

// parseSize parses size strings like "2MB", "100MB", "10GB"
func parseSize(sizeStr string) (int64, error) {
	if sizeStr == "" {
		return 0, fmt.Errorf("empty size string")
	}

	var size int64
	var unit string
	n, err := fmt.Sscanf(sizeStr, "%d%s", &size, &unit)
	if err != nil || n != 2 {
		return 0, fmt.Errorf("invalid size format: %s", sizeStr)
	}

	switch unit {
	case "B", "b":
		return size, nil
	case "KB", "kb", "K", "k":
		return size * 1024, nil
	case "MB", "mb", "M", "m":
		return size * 1024 * 1024, nil
	case "GB", "gb", "G", "g":
		return size * 1024 * 1024 * 1024, nil
	case "TB", "tb", "T", "t":
		return size * 1024 * 1024 * 1024 * 1024, nil
	default:
		return 0, fmt.Errorf("unknown unit: %s", unit)
	}
}

// GetRateLimitValues returns rate limiting values for middleware
func (c *Config) GetRateLimitValues() (float64, int, float64, int, float64, int) {
	upload := c.RateLimiting.Upload
	download := c.RateLimiting.Download
	stream := c.RateLimiting.Stream
	
	// Provide defaults if not configured
	uploadRate := upload.RequestsPerSecond
	if uploadRate <= 0 {
		uploadRate = 1.0
	}
	uploadBurst := upload.BurstSize
	if uploadBurst <= 0 {
		uploadBurst = 5
	}
	
	downloadRate := download.RequestsPerSecond
	if downloadRate <= 0 {
		downloadRate = 50.0
	}
	downloadBurst := download.BurstSize
	if downloadBurst <= 0 {
		downloadBurst = 100
	}
	
	streamRate := stream.RequestsPerSecond
	if streamRate <= 0 {
		streamRate = 10.0
	}
	streamBurst := stream.BurstSize
	if streamBurst <= 0 {
		streamBurst = 20
	}
	
	return uploadRate, uploadBurst, downloadRate, downloadBurst, streamRate, streamBurst
}