package script

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

// Sandbox 插件沙箱配置
type Sandbox struct {
	// 允许访问的路径列表（绝对路径）
	AllowedPaths []string
	// 允许写入的路径列表（必须是 AllowedPaths 的子集）
	WritablePaths []string
	// 禁止访问的路径（黑名单，优先级高于白名单）
	DeniedPaths []string
	// 是否允许网络访问
	AllowNetwork bool
	// 最大文件读取大小 (bytes)
	MaxReadSize int64
	// 最大文件写入大小 (bytes)
	MaxWriteSize int64
	// 最大内存使用量 (bytes)，0 表示不限制
	MaxMemory int64
	// 最大调用栈深度
	MaxStackDepth int
}

// DefaultSandbox 返回默认沙箱配置（最小权限）
func DefaultSandbox() *Sandbox {
	return &Sandbox{
		AllowedPaths:  []string{},
		WritablePaths: []string{},
		DeniedPaths:   defaultDeniedPaths(),
		AllowNetwork:  false,
		MaxReadSize:   10 * 1024 * 1024, // 10MB
		MaxWriteSize:  1 * 1024 * 1024,  // 1MB
		MaxMemory:     64 * 1024 * 1024, // 64MB
		MaxStackDepth: 1000,             // 最大调用栈深度
	}
}

// defaultDeniedPaths 返回默认禁止访问的路径
func defaultDeniedPaths() []string {
	home, _ := os.UserHomeDir()
	denied := []string{
		"/etc/passwd",
		"/etc/shadow",
		"/etc/sudoers",
		"/root",
		"/.ssh",
		"/.gnupg",
		"/.aws",
		"/.kube",
		"/proc",
		"/sys",
	}
	if home != "" {
		denied = append(denied,
			filepath.Join(home, ".ssh"),
			filepath.Join(home, ".gnupg"),
			filepath.Join(home, ".aws"),
			filepath.Join(home, ".kube"),
			filepath.Join(home, ".config"),
			filepath.Join(home, ".local"),
		)
	}
	return denied
}

// ValidateReadPath 验证读取路径是否允许
func (s *Sandbox) ValidateReadPath(path string) error {
	return s.validatePath(path, false)
}

// ValidateWritePath 验证写入路径是否允许
func (s *Sandbox) ValidateWritePath(path string) error {
	return s.validatePath(path, true)
}

func (s *Sandbox) validatePath(path string, write bool) error {
	// 清理路径，防止路径遍历攻击
	cleanPath, err := s.cleanPath(path)
	if err != nil {
		return err
	}

	// 检查黑名单（优先级最高）
	if s.isDenied(cleanPath) {
		return fmt.Errorf("access denied: path is in denied list")
	}

	// 检查白名单
	allowedList := s.AllowedPaths
	if write {
		allowedList = s.WritablePaths
	}

	if len(allowedList) == 0 {
		return fmt.Errorf("access denied: no paths allowed")
	}

	if !s.isAllowed(cleanPath, allowedList) {
		if write {
			return fmt.Errorf("access denied: path not in writable list")
		}
		return fmt.Errorf("access denied: path not in allowed list")
	}

	return nil
}

// cleanPath 清理并验证路径
func (s *Sandbox) cleanPath(path string) (string, error) {
	// 转换为绝对路径
	absPath, err := filepath.Abs(path)
	if err != nil {
		return "", fmt.Errorf("invalid path: %w", err)
	}

	// 清理路径（解析 .. 和 .）
	cleanPath := filepath.Clean(absPath)

	// 检查符号链接（防止通过符号链接绕过限制）
	realPath, err := filepath.EvalSymlinks(cleanPath)
	if err != nil {
		// 文件可能不存在，使用清理后的路径
		if !os.IsNotExist(err) {
			return "", fmt.Errorf("invalid path: %w", err)
		}
		realPath = cleanPath
	}

	// 再次检查路径遍历
	if strings.Contains(realPath, "..") {
		return "", fmt.Errorf("path traversal detected")
	}

	return realPath, nil
}

// isDenied 检查路径是否在黑名单中
func (s *Sandbox) isDenied(path string) bool {
	for _, denied := range s.DeniedPaths {
		if strings.HasPrefix(path, denied) || path == denied {
			return true
		}
	}
	return false
}

// isAllowed 检查路径是否在白名单中
func (s *Sandbox) isAllowed(path string, allowedList []string) bool {
	for _, allowed := range allowedList {
		if strings.HasPrefix(path, allowed) || path == allowed {
			return true
		}
	}
	return false
}
