package config

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"

	"gopkg.in/yaml.v3"
)

// TorrentClientsFileName is the basename of the Phase 3 torrent-client config.
const TorrentClientsFileName = "torrent-clients.yaml"

// TorrentClientsDocument is the on-disk shape of torrent-clients.yaml.
type TorrentClientsDocument struct {
	TorrentClients []TorrentClientConfig `yaml:"torrent_clients"`
}

// TorrentClientConfig is one client entry. Credentials are NEVER stored in
// YAML — the password lives in the secrets store under
// `torrent_client:<id>:password`.
type TorrentClientConfig struct {
	ID                   string         `yaml:"id"`
	Name                 string         `yaml:"name"`
	Type                 string         `yaml:"type"`   // "qbit" | "qui"
	BaseURL              string         `yaml:"base_url"`
	ProxyViaQUI          bool           `yaml:"proxy_via_qui"`
	Enabled              *bool          `yaml:"enabled,omitempty"`
	PollIntervalSeconds  int            `yaml:"poll_interval_seconds"`
	Config               map[string]any `yaml:"config,omitempty"`
}

// IsEnabled returns true if Enabled is omitted or explicitly true.
func (t TorrentClientConfig) IsEnabled() bool {
	if t.Enabled == nil {
		return true
	}
	return *t.Enabled
}

// LoadTorrentClientsFromDir reads torrent-clients.yaml from dir. An absent
// file is intentionally NOT an error — operators may start with no clients.
func LoadTorrentClientsFromDir(dir string) (*TorrentClientsDocument, error) {
	path := filepath.Join(dir, TorrentClientsFileName)
	data, err := os.ReadFile(path)
	if err != nil {
		if errors.Is(err, os.ErrNotExist) {
			return &TorrentClientsDocument{}, nil
		}
		return nil, fmt.Errorf("config: read %s: %w", path, err)
	}
	return LoadTorrentClientsFromBytes(data)
}

// LoadTorrentClientsFromBytes parses + validates.
func LoadTorrentClientsFromBytes(data []byte) (*TorrentClientsDocument, error) {
	var doc TorrentClientsDocument
	if err := yaml.Unmarshal(data, &doc); err != nil {
		return nil, fmt.Errorf("config: torrent-clients.yaml parse: %w", err)
	}
	if err := doc.Validate(); err != nil {
		return nil, err
	}
	for i := range doc.TorrentClients {
		t := &doc.TorrentClients[i]
		if t.PollIntervalSeconds == 0 {
			t.PollIntervalSeconds = 30
		}
	}
	return &doc, nil
}

// Validate enforces uniqueness and structural correctness.
func (d *TorrentClientsDocument) Validate() error {
	seen := map[string]struct{}{}
	for i, t := range d.TorrentClients {
		if t.ID == "" {
			return fmt.Errorf("config: torrent_clients[%d]: id required", i)
		}
		if t.Type == "" {
			return fmt.Errorf("config: torrent_clients[%d] (%s): type required", i, t.ID)
		}
		if t.BaseURL == "" {
			return fmt.Errorf("config: torrent_clients[%d] (%s): base_url required", i, t.ID)
		}
		if t.PollIntervalSeconds < 0 {
			return fmt.Errorf("config: torrent_clients[%d] (%s): poll_interval_seconds must be non-negative", i, t.ID)
		}
		if _, dup := seen[t.ID]; dup {
			return fmt.Errorf("config: duplicate torrent_client id %q", t.ID)
		}
		seen[t.ID] = struct{}{}
	}
	return nil
}
