package config

import (
	"fmt"
	"os"
	"strconv"
	"strings"

	"gitea.maximumdirect.net/eric/notarius/internal/framework/pipeline"
)

func LoadFromEnv() (Config, error) {
	cfg := Default()
	if err := cfg.applyEnvOverridesWithLookup(os.LookupEnv); err != nil {
		return Config{}, err
	}
	return cfg, nil
}

func (c *Config) ApplyEnvOverrides() error {
	return c.applyEnvOverridesWithLookup(os.LookupEnv)
}

func (c *Config) ApplyEnvOverridesWithLookup(lookup func(string) (string, bool)) error {
	return c.applyEnvOverridesWithLookup(lookup)
}

func (c *Config) applyEnvOverridesWithLookup(lookup func(string) (string, bool)) error {
	if c == nil {
		return fmt.Errorf("config must not be nil")
	}
	if raw, ok := lookup("NOTARIUS_TOTAL_LLM_CONCURRENCY"); ok {
		value, err := parseIntEnv("NOTARIUS_TOTAL_LLM_CONCURRENCY", raw)
		if err != nil {
			return err
		}
		c.Concurrency.TotalLLM = value
	}
	if raw, ok := lookup("NOTARIUS_STAGE_WORKERS_EXTRACT"); ok {
		value, err := parseIntEnv("NOTARIUS_STAGE_WORKERS_EXTRACT", raw)
		if err != nil {
			return err
		}
		if c.Concurrency.StageWorkers == nil {
			c.Concurrency.StageWorkers = make(map[string]int)
		}
		c.Concurrency.StageWorkers["extract"] = value
		c.Concurrency.extractWorkersConfigured = true
	}
	c.Concurrency.recomputeStageWorkerDefaults()
	if raw, ok := lookup("NOTARIUS_OUTPUT_DIR"); ok {
		c.Output.Directory = strings.TrimSpace(raw)
		if c.Output.Directory == "" {
			return fmt.Errorf("NOTARIUS_OUTPUT_DIR: must not be empty")
		}
		if strings.ContainsRune(c.Output.Directory, '\x00') {
			return fmt.Errorf("NOTARIUS_OUTPUT_DIR: must not contain NUL")
		}
	}
	if raw, ok := lookup("NOTARIUS_CACHE_CHUNK_PLANS_MODE"); ok {
		mode, err := pipeline.ParseChunkCacheMode(raw)
		if err != nil {
			return fmt.Errorf("NOTARIUS_CACHE_CHUNK_PLANS_MODE: %w", err)
		}
		c.Cache.ChunkPlans.Mode = mode
	}
	if raw, ok := lookup("NOTARIUS_CACHE_CHUNK_PLANS_DIR"); ok {
		c.Cache.ChunkPlans.Directory = cleanOptionalPath(raw)
		if c.Cache.ChunkPlans.Directory == "" {
			return fmt.Errorf("NOTARIUS_CACHE_CHUNK_PLANS_DIR: must not be empty")
		}
		if strings.ContainsRune(c.Cache.ChunkPlans.Directory, '\x00') {
			return fmt.Errorf("NOTARIUS_CACHE_CHUNK_PLANS_DIR: must not contain NUL")
		}
	}
	if raw, ok := lookup("NOTARIUS_CACHE_CHECKPOINTS_DIR"); ok {
		c.Cache.Checkpoints.Directory = cleanOptionalPath(raw)
		if c.Cache.Checkpoints.Directory == "" {
			return fmt.Errorf("NOTARIUS_CACHE_CHECKPOINTS_DIR: must not be empty")
		}
		if strings.ContainsRune(c.Cache.Checkpoints.Directory, '\x00') {
			return fmt.Errorf("NOTARIUS_CACHE_CHECKPOINTS_DIR: must not contain NUL")
		}
	}
	if raw, ok := lookup("NOTARIUS_DEBUG_DIR"); ok {
		c.Debug.Directory = strings.TrimSpace(raw)
		if c.Debug.Directory == "" {
			return fmt.Errorf("NOTARIUS_DEBUG_DIR: must not be empty")
		}
		if strings.ContainsRune(c.Debug.Directory, '\x00') {
			return fmt.Errorf("NOTARIUS_DEBUG_DIR: must not contain NUL")
		}
	}
	return nil
}

func parseIntEnv(name string, raw string) (int, error) {
	value, err := strconv.Atoi(strings.TrimSpace(raw))
	if err != nil {
		return 0, fmt.Errorf("%s: must be an integer", name)
	}
	return value, nil
}
