// Package shell contiene operaciones I/O del parallel executor.
// Todas las funciones retornan Result[T] y tienen side effects (git, filesystem).
package shell

import (
	"context"
	"fmt"
	"os"
	"strings"
	"time"

	"github.com/lucasdataproyects/devfactory/core"
	dfshell "github.com/lucasdataproyects/devfactory/shell"

	pcore "github.com/lucasdataproyects/parallel-executor/core"
)

// CreateWorktree crea un git worktree para una issue.
// Crea branch desde master y configura el directorio de trabajo.
func CreateWorktree(spec pcore.WorktreeSpec, repoRoot string) core.Result[string] {
	// Verificar que no existe ya
	if dfshell.DirExists(spec.WorkDir) {
		return core.Ok(spec.WorkDir)
	}

	// Crear directorio padre si no existe
	parentDir := spec.WorkDir[:strings.LastIndex(spec.WorkDir, "/")]
	mkResult := dfshell.MkdirAll(parentDir)
	if mkResult.IsErr() {
		return core.Err[string](mkResult.Error())
	}

	// Actualizar master primero
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()
	updateResult := dfshell.RunWithContext(ctx, "git", "-C", repoRoot, "fetch", "origin", "master")
	if updateResult.IsErr() {
		// No fatal — puede no tener remote
	}

	// Crear worktree con nueva branch desde master
	result := dfshell.Run("git", "-C", repoRoot,
		"worktree", "add",
		"-b", spec.BranchName,
		spec.WorkDir,
		"master",
	)
	if result.IsErr() {
		// Branch puede existir — intentar sin -b
		result = dfshell.Run("git", "-C", repoRoot,
			"worktree", "add",
			spec.WorkDir,
			spec.BranchName,
		)
		if result.IsErr() {
			return core.Err[string](fmt.Errorf("failed to create worktree for issue #%04d: %w",
				spec.Issue.Number, result.Error()))
		}
	}

	return core.Ok(spec.WorkDir)
}

// RemoveWorktree elimina un worktree y su branch.
func RemoveWorktree(spec pcore.WorktreeSpec, repoRoot string) core.Result[struct{}] {
	if !dfshell.DirExists(spec.WorkDir) {
		return core.Ok(struct{}{})
	}

	// Remover worktree
	result := dfshell.Run("git", "-C", repoRoot,
		"worktree", "remove", "--force", spec.WorkDir,
	)
	if result.IsErr() {
		// Fallback: eliminar directorio manualmente
		os.RemoveAll(spec.WorkDir)
		// Prune worktrees huérfanos
		dfshell.Run("git", "-C", repoRoot, "worktree", "prune")
	}

	return core.Ok(struct{}{})
}

// CleanupAllWorktrees limpia todos los worktrees del directorio worktrees/.
func CleanupAllWorktrees(repoRoot string) core.Result[int] {
	worktreesDir := repoRoot + "/worktrees"
	if !dfshell.DirExists(worktreesDir) {
		return core.Ok(0)
	}

	entries := dfshell.ListDir(worktreesDir)
	if entries.IsErr() {
		return core.Ok(0)
	}

	count := 0
	for _, entry := range entries.Unwrap() {
		fullPath := worktreesDir + "/" + entry
		dfshell.Run("git", "-C", repoRoot, "worktree", "remove", "--force", fullPath)
		count++
	}

	// Prune
	dfshell.Run("git", "-C", repoRoot, "worktree", "prune")

	// Eliminar directorio vacío
	os.Remove(worktreesDir)

	return core.Ok(count)
}

// ListWorktrees devuelve los worktrees activos.
func ListWorktrees(repoRoot string) core.Result[[]string] {
	result := dfshell.Run("git", "-C", repoRoot, "worktree", "list", "--porcelain")
	if result.IsErr() {
		return core.Err[[]string](result.Error())
	}

	output := result.Unwrap().Output()
	lines := strings.Split(output, "\n")

	var paths []string
	for _, line := range lines {
		if strings.HasPrefix(line, "worktree ") {
			path := strings.TrimPrefix(line, "worktree ")
			if path != repoRoot {
				paths = append(paths, path)
			}
		}
	}

	return core.Ok(paths)
}

