package main

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

type SpawnConfig struct {
	RepoRoot     string
	Branch       string
	WorktreePath string
	Prompt       string
	LogPath      string
}

type SpawnResult struct {
	PID          int    `json:"pid"`
	Branch       string `json:"branch"`
	WorktreePath string `json:"worktree_path"`
	LogPath      string `json:"log_path"`
	StartedAt    int64  `json:"started_at"`
	Error        string `json:"error,omitempty"`
}

// Spawn creates a git worktree on a fresh branch (reset if exists) and starts
// the claude headless subprocess. If claude is not in PATH or
// AGENT_RUNNER_STUB=1, runs `echo STUB: <prompt>` as a placeholder.
func Spawn(cfg SpawnConfig) SpawnResult {
	res := SpawnResult{
		Branch:       cfg.Branch,
		WorktreePath: cfg.WorktreePath,
		LogPath:      cfg.LogPath,
		StartedAt:    time.Now().Unix(),
	}

	if cfg.RepoRoot == "" || cfg.Branch == "" || cfg.WorktreePath == "" {
		res.Error = "missing required fields (repo_root/branch/worktree_path)"
		return res
	}

	// Ensure parent dir for worktree exists
	if err := os.MkdirAll(filepath.Dir(cfg.WorktreePath), 0o755); err != nil {
		res.Error = "mkdir worktree parent: " + err.Error()
		return res
	}

	// If worktree path already exists, remove forcibly first
	if _, err := os.Stat(cfg.WorktreePath); err == nil {
		_ = exec.Command("git", "-C", cfg.RepoRoot, "worktree", "remove", "--force", cfg.WorktreePath).Run()
		_ = os.RemoveAll(cfg.WorktreePath)
	}

	// Delete branch if exists (best-effort)
	_ = exec.Command("git", "-C", cfg.RepoRoot, "branch", "-D", cfg.Branch).Run()

	// Create worktree on new branch from master (fallback to current HEAD if master missing)
	base := "master"
	if err := exec.Command("git", "-C", cfg.RepoRoot, "rev-parse", "--verify", base).Run(); err != nil {
		base = "HEAD"
	}
	cmd := exec.Command("git", "-C", cfg.RepoRoot, "worktree", "add", "-b", cfg.Branch, cfg.WorktreePath, base)
	out, err := cmd.CombinedOutput()
	if err != nil {
		res.Error = fmt.Sprintf("worktree add: %s: %s", err.Error(), string(out))
		return res
	}

	// Open log
	if cfg.LogPath == "" {
		cfg.LogPath = filepath.Join(cfg.WorktreePath, "agent.log")
		res.LogPath = cfg.LogPath
	}
	if err := os.MkdirAll(filepath.Dir(cfg.LogPath), 0o755); err != nil {
		res.Error = "mkdir log: " + err.Error()
		return res
	}
	logFile, err := os.OpenFile(cfg.LogPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o644)
	if err != nil {
		res.Error = "open log: " + err.Error()
		return res
	}

	// Decide command: claude or stub
	useStub := os.Getenv("AGENT_RUNNER_STUB") == "1"
	if !useStub {
		if _, err := exec.LookPath("claude"); err != nil {
			useStub = true
		}
	}

	var sub *exec.Cmd
	if useStub {
		sub = exec.Command("echo", "STUB:", cfg.Prompt)
	} else {
		sub = exec.Command("claude", "--headless", "--dangerously-skip-permissions", "-p", cfg.Prompt)
		sub.Dir = cfg.WorktreePath
	}
	if sub.Dir == "" {
		sub.Dir = cfg.WorktreePath
	}
	sub.Stdout = logFile
	sub.Stderr = logFile

	if err := sub.Start(); err != nil {
		logFile.Close()
		res.Error = "spawn: " + err.Error()
		return res
	}
	res.PID = sub.Process.Pid

	// Reap async — closes log when subprocess exits
	go func() {
		_ = sub.Wait()
		_ = logFile.Close()
	}()

	return res
}

// Cleanup kills the PID (best-effort), removes the worktree and deletes the branch.
func Cleanup(repoRoot string, pid int, worktreePath, branch string) error {
	var firstErr error
	if pid > 0 {
		if p, err := os.FindProcess(pid); err == nil {
			_ = p.Kill()
		}
	}
	if worktreePath != "" {
		out, err := exec.Command("git", "-C", repoRoot, "worktree", "remove", "--force", worktreePath).CombinedOutput()
		if err != nil && !strings.Contains(string(out), "is not a working tree") {
			firstErr = fmt.Errorf("worktree remove: %s: %s", err.Error(), string(out))
		}
		_ = os.RemoveAll(worktreePath)
	}
	if branch != "" {
		_ = exec.Command("git", "-C", repoRoot, "branch", "-D", branch).Run()
	}
	return firstErr
}
