// agent_home.go 实现 agent CLI 受管 home 目录的物化与重定向 env 注入。
//
// 受管 home 按 user + agent-kind 维度隔离：<agent_homes_dir>/<user_id>/<agent_kind_id>/。
// 早期实现仅按 agent-kind 维度（多个用户共享同一 home 与缓存凭据），沙箱化要求
// per-USER home，故 home 路径以 user_id 为一级目录、agent_kind_id 为二级目录。
// spawn 前把 DB 中的配置文件解密落盘（全量覆写受管文件，不清空目录 —— agent 运行期
// 写入的缓存 / 凭据得以保留），再按 client_type 注入配置目录重定向变量。受管 home 位于
// DataDir 下，容器场景随持久卷存活。旧的 per-kind 目录无害遗留，下次 spawn 用新路径。
package acp

import (
	"context"
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"strings"
)

// materializeAgentHome 创建受管 home 并物化该 AgentKind 的全部配置文件。
// home 按 user + agent-kind 隔离：<AgentHomesDir>/<user_id>/<kind_id>。
// 返回 home 绝对路径。配置文件为空时仍创建目录（agent 需要可写 home）。
func (s *Supervisor) materializeAgentHome(ctx context.Context, sess *Session, kind *AgentKind) (string, error) {
	home, err := filepath.Abs(filepath.Join(s.cfg.AgentHomesDir, sess.UserID.String(), kind.ID.String()))
	if err != nil {
		return "", fmt.Errorf("resolve agent home: %w", err)
	}
	if err := os.MkdirAll(home, 0o700); err != nil {
		return "", fmt.Errorf("create agent home: %w", err)
	}
	// client 专属配置目录预创建：CODEX_HOME 指向的目录必须已存在（codex 硬性要求），
	// 其余预创建无害且省去 agent 首次启动的目录初始化分支。
	if sub := clientConfigSubdir(kind.ClientType); sub != "" {
		if err := os.MkdirAll(filepath.Join(home, sub), 0o700); err != nil {
			return "", fmt.Errorf("create client config dir: %w", err)
		}
	}

	files, err := s.repo.ListConfigFiles(ctx, kind.ID)
	if err != nil {
		return "", err
	}
	sep := string(filepath.Separator)
	for _, f := range files {
		plain, derr := s.crypto.Decrypt(f.EncryptedContent)
		if derr != nil {
			return "", fmt.Errorf("decrypt config file %s: %w", f.RelPath, derr)
		}
		dst := filepath.Clean(filepath.Join(home, filepath.FromSlash(f.RelPath)))
		// rel_path 写入时已校验；此处兜底防 DB 被直接篡改后逃逸受管目录。
		if !strings.HasPrefix(dst, home+sep) {
			return "", fmt.Errorf("config file %s escapes agent home", f.RelPath)
		}
		if err := os.MkdirAll(filepath.Dir(dst), 0o700); err != nil {
			return "", fmt.Errorf("create config file dir %s: %w", f.RelPath, err)
		}
		if err := os.WriteFile(dst, plain, 0o600); err != nil {
			return "", fmt.Errorf("write config file %s: %w", f.RelPath, err)
		}
	}
	return home, nil
}

// agentHomeEnv 返回受管 home 对应的重定向 env 默认值。优先级最低：AgentKind env
// 与系统 extraEnv 可覆盖同名 key。
func agentHomeEnv(home string, ct ClientType) map[string]string {
	env := map[string]string{"HOME": home}
	if runtime.GOOS == "windows" {
		// Windows 上 Node/Rust 解析 home 走 USERPROFILE（开发环境）。
		env["USERPROFILE"] = home
	}
	switch ct {
	case ClientClaudeCode:
		env["CLAUDE_CONFIG_DIR"] = filepath.Join(home, ".claude")
	case ClientCodex:
		env["CODEX_HOME"] = filepath.Join(home, ".codex")
	case ClientGemini:
		// GEMINI_CLI_HOME 替换的是 home 本身，.gemini 段由 CLI 拼接。
		env["GEMINI_CLI_HOME"] = home
	}
	return env
}

// clientConfigSubdir 返回 client 专属配置子目录（相对受管 home），无则空串。
func clientConfigSubdir(ct ClientType) string {
	switch ct {
	case ClientClaudeCode:
		return ".claude"
	case ClientCodex:
		return ".codex"
	case ClientGemini:
		return ".gemini"
	}
	return ""
}
