package config

import (
	"fmt"
	"os"
	"time"

	"gopkg.in/yaml.v3"
)

// Config is the top-level configuration structure.
type Config struct {
	Server      ServerConfig      `yaml:"server"`
	Auth        AuthConfig        `yaml:"auth"`
	DCMTK       DCMTKConfig       `yaml:"dcmtk"`
	PACS        PACSConfig        `yaml:"pacs"`
	OurAE       OurAEConfig       `yaml:"our_ae"`
	PatientAPI  PatientAPIConfig  `yaml:"patient_api"`
	CDPublisher CDPublisherConfig `yaml:"cd_publisher"`
	ISO         ISOConfig         `yaml:"iso"`
}

type ServerConfig struct {
	Port         int           `yaml:"port"`
	ReadTimeout  time.Duration `yaml:"read_timeout"`
	WriteTimeout time.Duration `yaml:"write_timeout"`
}

type AuthConfig struct {
	Enabled bool   `yaml:"enabled"`
	APIKey  string `yaml:"api_key"`
}

type DCMTKConfig struct {
	Storescp string `yaml:"storescp"`
	Movescu  string `yaml:"movescu"`
	Storescu string `yaml:"storescu"`
}

type PACSConfig struct {
	AETitle string `yaml:"ae_title"`
	Host    string `yaml:"host"`
	Port    int    `yaml:"port"`
}

type OurAEConfig struct {
	AETitle   string `yaml:"ae_title"`
	BasePort  int    `yaml:"base_port"`
	PortRange int    `yaml:"port_range"`
}

type PatientAPIConfig struct {
	BaseURL     string        `yaml:"base_url"`
	Endpoint    string        `yaml:"endpoint"`
	AuthType    string        `yaml:"auth_type"`
	AuthHeader  string        `yaml:"auth_header"`
	AuthToken   string        `yaml:"auth_token"`
	Timeout     time.Duration `yaml:"timeout"`
	Retry       int           `yaml:"retry"`
	RetryBackoff time.Duration `yaml:"retry_backoff"`
}

type CDPublisherConfig struct {
	Host string `yaml:"host"`
	Port int    `yaml:"port"`
}

type ISOConfig struct {
	MicrodicomPath string `yaml:"microdicom_path"`
	TempDir        string `yaml:"temp_dir"`
}

// Load reads and parses the config file from the given path.
// If path is empty, it reads from the MKISO_CONFIG env var, falling back
// to ./config.yaml.
func Load(path string) (*Config, error) {
	if path == "" {
		path = os.Getenv("MKISO_CONFIG")
	}
	if path == "" {
		path = "./config.yaml"
	}

	data, err := os.ReadFile(path)
	if err != nil {
		return nil, fmt.Errorf("read config file %q: %w", path, err)
	}

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

	if err := cfg.validate(); err != nil {
		return nil, fmt.Errorf("validate config: %w", err)
	}

	return &cfg, nil
}

func (c *Config) validate() error {
	if c.Server.Port == 0 {
		c.Server.Port = 8080
	}
	if c.Server.ReadTimeout == 0 {
		c.Server.ReadTimeout = 300 * time.Second
	}
	if c.Server.WriteTimeout == 0 {
		c.Server.WriteTimeout = 600 * time.Second
	}

	if c.DCMTK.Storescp == "" {
		return fmt.Errorf("dcmtk.storescp is required")
	}
	if c.DCMTK.Movescu == "" {
		return fmt.Errorf("dcmtk.movescu is required")
	}
	if c.DCMTK.Storescu == "" {
		return fmt.Errorf("dcmtk.storescu is required")
	}

	if c.PACS.Host == "" {
		c.PACS.Host = "localhost"
	}
	if c.PACS.Port == 0 {
		c.PACS.Port = 11112
	}
	if c.PACS.AETitle == "" {
		c.PACS.AETitle = "ABPACS"
	}

	if c.OurAE.AETitle == "" {
		c.OurAE.AETitle = "CDRECORD"
	}
	if c.OurAE.BasePort == 0 {
		c.OurAE.BasePort = 10104
	}
	if c.OurAE.PortRange == 0 {
		c.OurAE.PortRange = 100
	}

	if c.PatientAPI.BaseURL == "" {
		c.PatientAPI.BaseURL = "http://localhost:8090"
	}
	if c.PatientAPI.Endpoint == "" {
		c.PatientAPI.Endpoint = "/patient/by-accession"
	}
	if c.PatientAPI.Timeout == 0 {
		c.PatientAPI.Timeout = 10 * time.Second
	}
	if c.PatientAPI.Retry == 0 {
		c.PatientAPI.Retry = 3
	}
	if c.PatientAPI.RetryBackoff == 0 {
		c.PatientAPI.RetryBackoff = 500 * time.Millisecond
	}

	if c.CDPublisher.Host == "" {
		c.CDPublisher.Host = "172.16.0.120"
	}
	if c.CDPublisher.Port == 0 {
		c.CDPublisher.Port = 104
	}

	if c.ISO.MicrodicomPath == "" {
		c.ISO.MicrodicomPath = "/var/www/html/microdicom"
	}
	if c.ISO.TempDir == "" {
		c.ISO.TempDir = "/tmp"
	}

	return nil
}
