// Package config loads workflow tool configuration from environment variables
// and/or a KEY=VALUE config file (`.env` format).
//
// # Load order (highest priority first)
//
//  1. Environment variables (always override everything else)
//  2. Config file — path resolution:
//     a. Explicit path passed to Load()
//     b. WORKFLOW_CONFIG environment variable
//     c. .env in the current working directory
//     d. .workflow.env in the current working directory
//
// # Config file format
//
// Plain KEY=VALUE pairs, one per line. Lines starting with # are comments.
//
//	# .env
//	LLM_URL=http://localhost:4000
//	LLM_KEY=sk-xxx
//	LLM_MODEL=gpt-4o
//	WORKSPACE_ROOT=./workspace
//	PORT=8080
//
//	# v3.16+: Multi-provider configuration
//	OPENAI_API_KEY=sk-openai-xxx
//	OPENAI_MODEL=gpt-4.1
//	ANTHROPIC_API_KEY=sk-ant-xxx
//	ANTHROPIC_MODEL=claude-sonnet-4-5-20250929
//
// # For new users
//
// Copy .env.example to .env and fill in your credentials.
// Never commit .env to version control.
package config

import (
	"bufio"
	"os"
	"strings"
)

// Config holds all runtime configuration for the workflow tools.
type Config struct {
	LLM       LLMConfig
	Providers map[string]ProviderConfig // v3.16+: per-provider settings keyed by provider name ("openai", "anthropic")
	Workspace WorkspaceConfig
	Server    ServerConfig
}

// LLMConfig holds global/default LLM endpoint settings (backward-compatible with pre-3.16).
type LLMConfig struct {
	URL   string // LLM_URL  (default: http://localhost:4000)
	Key   string // LLM_KEY  (fallback key if provider-specific key not set)
	Model string // LLM_MODEL (default: gpt-4o; v3.16+: supports "provider/modelId" format)
}

// ProviderConfig holds per-provider LLM settings (v3.16+).
type ProviderConfig struct {
	APIKey  string // e.g. OPENAI_API_KEY, ANTHROPIC_API_KEY
	Model   string // e.g. OPENAI_MODEL, ANTHROPIC_MODEL (default modelId for this provider)
	BaseURL string // e.g. OPENAI_BASE_URL (optional URL override)
}

// WorkspaceConfig holds local file workspace settings.
type WorkspaceConfig struct {
	Root string // WORKSPACE_ROOT (default: ./workspace)
}

// ServerConfig holds HTTP server settings.
type ServerConfig struct {
	Port string // PORT (default: 8080)
}

// defaults returns a Config pre-populated with all default values.
func defaults() Config {
	return Config{
		LLM: LLMConfig{
			URL:   "http://localhost:4000",
			Model: "gpt-4o",
		},
		Providers: make(map[string]ProviderConfig),
		Workspace: WorkspaceConfig{
			Root: "./workspace",
		},
		Server: ServerConfig{
			Port: "8080",
		},
	}
}

// Load reads configuration using the priority chain described in the package doc.
// Pass an empty string for configFile to use automatic file discovery.
func Load(configFile string) Config {
	cfg := defaults()

	// Resolve config file path
	path := resolveFilePath(configFile)

	// 1. Apply file values (lowest priority)
	if path != "" {
		applyFile(&cfg, path)
	}

	// 2. Apply environment variables (highest priority — always win)
	applyEnv(&cfg)

	return cfg
}

// resolveFilePath returns the config file path to use, or "" if none found.
func resolveFilePath(explicit string) string {
	// a. Explicit argument
	if explicit != "" {
		return explicit
	}
	// b. WORKFLOW_CONFIG env var
	if v := os.Getenv("WORKFLOW_CONFIG"); v != "" {
		return v
	}
	// c. .env in CWD
	if _, err := os.Stat(".env"); err == nil {
		return ".env"
	}
	// d. .workflow.env in CWD
	if _, err := os.Stat(".workflow.env"); err == nil {
		return ".workflow.env"
	}
	return ""
}

// applyFile reads KEY=VALUE pairs from path and applies them to cfg.
// Missing file or unrecognised keys are silently ignored.
func applyFile(cfg *Config, path string) {
	f, err := os.Open(path)
	if err != nil {
		return
	}
	defer f.Close()

	kv := parseKV(bufio.NewScanner(f))
	applyKV(cfg, kv)
}

// applyEnv reads relevant environment variables and applies them to cfg.
func applyEnv(cfg *Config) {
	kv := make(map[string]string)
	for _, key := range []string{
		"LLM_URL", "LLM_KEY", "LLM_MODEL",
		"OPENAI_API_KEY", "OPENAI_MODEL", "OPENAI_BASE_URL",
		"ANTHROPIC_API_KEY", "ANTHROPIC_MODEL", "ANTHROPIC_BASE_URL",
		"WORKSPACE_ROOT",
		"PORT",
	} {
		if v := os.Getenv(key); v != "" {
			kv[key] = v
		}
	}
	applyKV(cfg, kv)
}

// applyKV maps a flat key→value map onto cfg fields.
func applyKV(cfg *Config, kv map[string]string) {
	// Global LLM settings (backward-compatible)
	if v, ok := kv["LLM_URL"]; ok && v != "" {
		cfg.LLM.URL = v
	}
	if v, ok := kv["LLM_KEY"]; ok {
		cfg.LLM.Key = v // allow empty string (clears previous value)
	}
	if v, ok := kv["LLM_MODEL"]; ok && v != "" {
		cfg.LLM.Model = v
	}

	// v3.16+: Per-provider settings
	if cfg.Providers == nil {
		cfg.Providers = make(map[string]ProviderConfig)
	}
	applyProviderKV(cfg, "openai", kv, "OPENAI_API_KEY", "OPENAI_MODEL", "OPENAI_BASE_URL")
	applyProviderKV(cfg, "anthropic", kv, "ANTHROPIC_API_KEY", "ANTHROPIC_MODEL", "ANTHROPIC_BASE_URL")

	// Workspace and server settings
	if v, ok := kv["WORKSPACE_ROOT"]; ok && v != "" {
		cfg.Workspace.Root = v
	}
	if v, ok := kv["PORT"]; ok && v != "" {
		cfg.Server.Port = v
	}
}

// applyProviderKV applies provider-level config from the kv map.
func applyProviderKV(cfg *Config, name string, kv map[string]string, keyEnv, modelEnv, urlEnv string) {
	p := cfg.Providers[name]
	if v, ok := kv[keyEnv]; ok && v != "" {
		p.APIKey = v
	}
	if v, ok := kv[modelEnv]; ok && v != "" {
		p.Model = v
	}
	if v, ok := kv[urlEnv]; ok && v != "" {
		p.BaseURL = v
	}
	if p.APIKey != "" || p.Model != "" || p.BaseURL != "" {
		cfg.Providers[name] = p
	}
}

// parseKV scans lines from s, returning a KEY→value map.
// Lines that are blank or start with # are ignored.
// Lines without '=' are ignored.
func parseKV(s *bufio.Scanner) map[string]string {
	m := make(map[string]string)
	for s.Scan() {
		line := strings.TrimSpace(s.Text())
		if line == "" || strings.HasPrefix(line, "#") {
			continue
		}
		k, v, ok := strings.Cut(line, "=")
		if !ok {
			continue
		}
		k = strings.TrimSpace(k)
		v = strings.TrimSpace(v)
		// Strip optional surrounding quotes (" or ')
		if len(v) >= 2 && ((v[0] == '"' && v[len(v)-1] == '"') || (v[0] == '\'' && v[len(v)-1] == '\'')) {
			v = v[1 : len(v)-1]
		}
		m[k] = v
	}
	return m
}

// MaskKey returns a masked representation of an API key for logging.
func MaskKey(key string) string {
	switch {
	case key == "":
		return "(not set)"
	case len(key) <= 8:
		return strings.Repeat("*", len(key))
	default:
		return key[:4] + strings.Repeat("*", len(key)-8) + key[len(key)-4:]
	}
}
