package git

import (
	"bufio"
	"bytes"
	"context"
	"strings"
)

// WorktreeAdd 在 dir（main worktree）创建一个支线 worktree。
//
//	path:       绝对路径，由 workspace 包计算
//	branch:     用 -B 强制创建/重置 —— 已存在的分支会被重置（本地未推送
//	            commit 会丢失，可从 reflog 找回）。
//	startPoint: 为空时分支基于当前 HEAD；非空时分支基于该 start-point，
//	            且 --track 会把 upstream 指向它。
//
// 失败时如果 stderr 含 "already exists" / "is already used by worktree"，
// errors.IsConflict 返回 true，调用方应映射为 WORKTREE_BRANCH_CONFLICT。
func (r *DefaultRunner) WorktreeAdd(ctx context.Context, dir, branch, path, startPoint string) error {
	ctx, cancel := withCtx(ctx, r.cfg.OpsTimeout)
	defer cancel()
	args := []string{"worktree", "add", "--track", "-B", branch, path}
	if startPoint != "" {
		args = append(args, startPoint)
	}
	_, _, err := r.exec(ctx, dir, nil, args...)
	return err
}

// WorktreeRemove 删除 path 这个 worktree（包含磁盘目录）。
// 即使 worktree 当前不存在 / 已被外部 rm 掉，调用 git worktree remove --force 也会成功
// 或返回特定 stderr，调用方据此决定是否吞错。
func (r *DefaultRunner) WorktreeRemove(ctx context.Context, dir, path string) error {
	ctx, cancel := withCtx(ctx, r.cfg.OpsTimeout)
	defer cancel()
	_, _, err := r.exec(ctx, dir, nil, "worktree", "remove", "--force", path)
	return err
}

// WorktreeList 返回 dir 下所有 worktree 的元数据（含 main）。
func (r *DefaultRunner) WorktreeList(ctx context.Context, dir string) ([]Worktree, error) {
	ctx, cancel := withCtx(ctx, r.cfg.OpsTimeout)
	defer cancel()
	out, _, err := r.exec(ctx, dir, nil, "worktree", "list", "--porcelain")
	if err != nil {
		return nil, err
	}
	return parseWorktreeList(out), nil
}

// parseWorktreeList 解析 git worktree list --porcelain 输出。
// 每个 worktree 由若干 KV 行组成，记录间空行分隔：
//
//	worktree /abs/path
//	HEAD     <sha>
//	branch   refs/heads/<name>     (or "detached")
//	bare                            (only for bare main)
func parseWorktreeList(buf []byte) []Worktree {
	var out []Worktree
	var cur Worktree
	flush := func() {
		if cur.Path != "" {
			out = append(out, cur)
		}
		cur = Worktree{}
	}
	sc := bufio.NewScanner(bytes.NewReader(buf))
	for sc.Scan() {
		line := sc.Text()
		if line == "" {
			flush()
			continue
		}
		switch {
		case strings.HasPrefix(line, "worktree "):
			cur.Path = strings.TrimPrefix(line, "worktree ")
		case strings.HasPrefix(line, "HEAD "):
			cur.HEAD = strings.TrimPrefix(line, "HEAD ")
		case strings.HasPrefix(line, "branch "):
			cur.Branch = strings.TrimPrefix(strings.TrimPrefix(line, "branch "), "refs/heads/")
		case line == "bare":
			cur.Bare = true
		}
	}
	flush()
	return out
}
