package opencoderuntime

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

var credentialFiles = []string{
	"account.json",
	"auth.json",
}

var stateFiles = []string{
	"model.json",
}

// Prepare creates an isolated OpenCode runtime under workDir and returns env entries for it.
func Prepare(workDir string) ([]string, error) {
	runtimeRoot := filepath.Join(workDir, ".opencode-runtime")
	dataHome := filepath.Join(runtimeRoot, "data")
	stateHome := filepath.Join(runtimeRoot, "state")

	if err := os.MkdirAll(filepath.Join(dataHome, "opencode"), 0o700); err != nil {
		return nil, fmt.Errorf("create opencode data dir: %w", err)
	}
	if err := os.MkdirAll(filepath.Join(stateHome, "opencode"), 0o700); err != nil {
		return nil, fmt.Errorf("create opencode state dir: %w", err)
	}
	if err := copyCredentials(filepath.Join(dataHome, "opencode")); err != nil {
		return nil, err
	}
	if err := copyState(filepath.Join(stateHome, "opencode")); err != nil {
		return nil, err
	}

	return []string{
		"XDG_DATA_HOME=" + dataHome,
		"XDG_STATE_HOME=" + stateHome,
	}, nil
}

func copyState(targetStateDir string) error {
	sourceStateDir, err := hostStateDir()
	if err != nil {
		return err
	}
	for _, name := range stateFiles {
		if err := copyFileIfExists(filepath.Join(sourceStateDir, name), filepath.Join(targetStateDir, name)); err != nil {
			return fmt.Errorf("copy opencode state %s: %w", name, err)
		}
	}
	return nil
}

func copyCredentials(targetDataDir string) error {
	sourceDataDir, err := hostDataDir()
	if err != nil {
		return err
	}
	for _, name := range credentialFiles {
		source := filepath.Join(sourceDataDir, name)
		target := filepath.Join(targetDataDir, name)
		if err := copyFileIfExists(source, target); err != nil {
			return fmt.Errorf("copy opencode credential %s: %w", name, err)
		}
	}
	return nil
}

func hostDataDir() (string, error) {
	dataHome := os.Getenv("XDG_DATA_HOME")
	if dataHome == "" {
		home, err := os.UserHomeDir()
		if err != nil {
			return "", fmt.Errorf("resolve home directory for opencode data: %w", err)
		}
		dataHome = filepath.Join(home, ".local", "share")
	}
	return filepath.Join(dataHome, "opencode"), nil
}

func hostStateDir() (string, error) {
	stateHome := os.Getenv("XDG_STATE_HOME")
	if stateHome == "" {
		home, err := os.UserHomeDir()
		if err != nil {
			return "", fmt.Errorf("resolve home directory for opencode state: %w", err)
		}
		stateHome = filepath.Join(home, ".local", "state")
	}
	return filepath.Join(stateHome, "opencode"), nil
}

func copyFileIfExists(source string, target string) error {
	info, err := os.Stat(source)
	if err != nil {
		if os.IsNotExist(err) {
			return nil
		}
		return err
	}
	if info.IsDir() {
		return fmt.Errorf("source is a directory")
	}
	data, err := os.ReadFile(source)
	if err != nil {
		return err
	}
	if err := os.MkdirAll(filepath.Dir(target), 0o700); err != nil {
		return err
	}
	mode := info.Mode().Perm()
	if mode == 0 {
		mode = 0o600
	}
	return os.WriteFile(target, data, mode)
}
