package config

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

	"gopkg.in/yaml.v3"
)

// TrackersFileName is the basename of the Phase 1 tracker-list config file
// inside the declarative config directory.
const TrackersFileName = "trackers.yaml"

// TrackersDocument is the on-disk shape of trackers.yaml.
type TrackersDocument struct {
	Trackers []TrackerConfig `yaml:"trackers"`
}

// TrackerConfig is one entry. Adapter-specific keys live under `config:` and
// are passed to the adapter constructor as opaque JSON.
type TrackerConfig struct {
	ID                    string                 `yaml:"id"`
	Name                  string                 `yaml:"name"`
	Type                  string                 `yaml:"type"`
	BaseURL               string                 `yaml:"base_url"`
	Enabled               *bool                  `yaml:"enabled,omitempty"`
	ScrapeIntervalSeconds int                    `yaml:"scrape_interval_seconds"`
	ScrapeJitterSeconds   int                    `yaml:"scrape_jitter_seconds"`
	UseByparr             bool                   `yaml:"use_byparr"`
	Config                map[string]any         `yaml:"config,omitempty"`
}

// IsEnabled returns the effective enabled state with `true` as default
// (omitted == enabled).
func (t TrackerConfig) IsEnabled() bool {
	if t.Enabled == nil {
		return true
	}
	return *t.Enabled
}

// LoadTrackersFromDir reads `trackers.yaml` from dir and returns the typed
// document, fully validated. An absent file is NOT an error — operators may
// start with no trackers; the returned document just has an empty list.
func LoadTrackersFromDir(dir string) (*TrackersDocument, error) {
	path := filepath.Join(dir, TrackersFileName)
	data, err := os.ReadFile(path)
	if err != nil {
		if errors.Is(err, os.ErrNotExist) {
			return &TrackersDocument{}, nil
		}
		return nil, fmt.Errorf("config: read %s: %w", path, err)
	}
	return LoadTrackersFromBytes(data)
}

// LoadTrackersFromBytes parses a trackers.yaml document and validates it.
func LoadTrackersFromBytes(data []byte) (*TrackersDocument, error) {
	var doc TrackersDocument
	if err := yaml.Unmarshal(data, &doc); err != nil {
		return nil, fmt.Errorf("config: trackers.yaml parse: %w", err)
	}
	if err := doc.Validate(); err != nil {
		return nil, err
	}
	// Apply defaults.
	for i := range doc.Trackers {
		t := &doc.Trackers[i]
		if t.ScrapeIntervalSeconds == 0 {
			t.ScrapeIntervalSeconds = 300
		}
		if t.ScrapeJitterSeconds == 0 {
			t.ScrapeJitterSeconds = 60
		}
	}
	return &doc, nil
}

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