package config

import (
	"os"
	"path/filepath"
	"sort"
	"strings"

	"github.com/joho/godotenv"

	fileencoding "reasonix/internal/fileutil/encoding"
)

type dotEnvFile struct {
	Path       string
	Values     map[string]string
	Duplicates []string
}

// loadDotEnv loads Reasonix's global .env for provider credentials. The
// workspace .env values returned by loadDotEnvForRoot are ignored here because
// loadDotEnv has no Config to carry a workspace-scoped expansion environment.
func loadDotEnv() {
	loadDotEnvForRoot(".")
}

// loadDotEnvForRoot returns workspace .env values for scoped plugin/MCP/proxy
// expansion, then loads Reasonix's global .env for provider credentials.
// Workspace .env values are deliberately not written into the process
// environment, so multiple desktop/ACP workspaces cannot leak tokens into each
// other and project files cannot redirect Reasonix's own config/credential
// paths.
func loadDotEnvForRoot(root string) map[string]string {
	projectEnv := loadProjectDotEnvForExpansion(root)
	loadCredentialStoreForRoot(root)
	return projectEnv
}

func loadProjectDotEnvForExpansion(root string) map[string]string {
	root = resolveRoot(root)
	path := ".env"
	if root != "." {
		path = filepath.Join(root, ".env")
	}
	if current := UserCredentialsPath(); current != "" && samePath(path, current) {
		return nil
	}
	file, ok := readDotEnvFile(path)
	if !ok {
		return nil
	}
	return file.filtered(func(key string) bool {
		return !isProjectDotEnvControlKey(key)
	})
}

func isProjectDotEnvControlKey(key string) bool {
	key = strings.TrimSpace(key)
	if key == "" {
		return true
	}
	upper := strings.ToUpper(key)
	if strings.HasPrefix(upper, "REASONIX_") {
		return true
	}
	switch upper {
	case "HOME", "USERPROFILE", "APPDATA", "XDG_CONFIG_HOME", "XDG_CACHE_HOME", "XDG_STATE_HOME":
		return true
	default:
		return false
	}
}

func legacyCredentialsPaths() []string {
	current := UserCredentialsPath()
	seen := map[string]bool{}
	var paths []string
	add := func(path string) {
		if path == "" {
			return
		}
		path = filepath.Clean(path)
		if current != "" && samePath(path, current) {
			return
		}
		if seen[path] {
			return
		}
		seen[path] = true
		paths = append(paths, path)
	}
	if dir := legacyOSSupportDir(); dir != "" {
		add(filepath.Join(dir, "credentials"))
	}
	if dir := userSupportDir(); dir != "" {
		add(filepath.Join(dir, "credentials"))
		add(filepath.Join(dir, ".env"))
	}
	for _, cfg := range legacyXDGConfigPaths() {
		add(filepath.Join(filepath.Dir(cfg), "credentials"))
	}
	return paths
}

func loadDotEnvFileAs(path string, source CredentialSource) {
	file, ok := readDotEnvFile(path)
	if !ok {
		return
	}
	for key, val := range file.Values {
		key = strings.TrimSpace(key)
		if key == "" {
			continue
		}
		if _, exists := os.LookupEnv(key); exists && source.Kind != CredentialSourceCredentials {
			recordExistingCredentialSource(key)
			continue
		}
		if err := os.Setenv(key, val); err == nil && source.Kind != "" {
			source.Path = path
			recordCredentialSource(key, val, source)
		}
	}
}

func readDotEnvFile(path string) (dotEnvFile, bool) {
	raw, err := fileencoding.ReadFileUTF8(path)
	if err != nil {
		return dotEnvFile{}, false
	}
	values, err := godotenv.Unmarshal(string(raw))
	if err != nil {
		return dotEnvFile{}, false
	}
	return dotEnvFile{
		Path:       path,
		Values:     values,
		Duplicates: detectDotEnvDuplicateKeys(path),
	}, true
}

func (f dotEnvFile) filtered(allow func(string) bool) map[string]string {
	out := map[string]string{}
	for key, val := range f.Values {
		key = strings.TrimSpace(key)
		if key == "" || allow != nil && !allow(key) {
			continue
		}
		out[key] = val
	}
	if len(out) == 0 {
		return nil
	}
	return out
}

func (f dotEnvFile) warnings() []string {
	if len(f.Duplicates) == 0 {
		return nil
	}
	warnings := make([]string, 0, len(f.Duplicates))
	for _, key := range f.Duplicates {
		warnings = append(warnings, "duplicate .env key "+key+" in "+f.Path+"; last parsed value wins")
	}
	return warnings
}

func detectDotEnvDuplicateKeys(path string) []string {
	raw, err := fileencoding.ReadFileUTF8(path)
	if err != nil {
		return nil
	}
	seen := map[string]bool{}
	dups := map[string]bool{}
	for line := range strings.SplitSeq(strings.ReplaceAll(string(raw), "\r\n", "\n"), "\n") {
		values, err := godotenv.Unmarshal(line)
		if err != nil {
			continue
		}
		for key := range values {
			key = strings.TrimSpace(key)
			if key == "" {
				continue
			}
			if seen[key] {
				dups[key] = true
			}
			seen[key] = true
		}
	}
	out := make([]string, 0, len(dups))
	for key := range dups {
		out = append(out, key)
	}
	sort.Strings(out)
	return out
}

func envFileValue(path, wantKey string) (string, bool) {
	file, ok := readDotEnvFile(path)
	if !ok {
		return "", false
	}
	val, ok := file.Values[wantKey]
	return val, ok
}
