package pr

import (
	"fmt"
	"net/url"

	"github.com/gitlink-org/gitlink-cli/internal/output"
	"github.com/gitlink-org/gitlink-cli/shortcuts/common"
)

func Shortcuts() []*common.Shortcut {
	return []*common.Shortcut{
		newApproveShortcut(),
		newRequestChangesShortcut(),
		newReviewsShortcut(),
		{
			Name:        "list",
			Description: "List pull requests",
			Flags: []common.Flag{
				{Name: "state", Short: "s", Usage: "Filter: open, merged, closed", Default: "open"},
				{Name: "page", Short: "p", Usage: "Page number", Default: "1"},
				{Name: "limit", Short: "l", Usage: "Items per page", Default: "20"},
			},
			Run: func(ctx *common.RuntimeContext) error {
				if err := ctx.ResolveOwnerRepo(); err != nil {
					return err
				}
				q := url.Values{}
				q.Set("page", ctx.Arg("page"))
				q.Set("limit", ctx.Arg("limit"))
				if s := ctx.Arg("state"); s != "" {
					q.Set("state", s)
				}
				env, err := ctx.CallAPIWithQuery("GET", ctx.RepoPath()+"/pulls", q)
				if err != nil {
					return fmt.Errorf("获取 PR 列表失败: %w", err)
				}
				return ctx.Output(env)
			},
		},
		{
			Name:        "create",
			Description: "Create a pull request",
			DryRun:      true,
			DryRunHint: func(ctx *common.RuntimeContext) (string, error) {
				title := ctx.Arg("title")
				head := ctx.Arg("head")
				base := ctx.Arg("base")
				if base == "" {
					base = "master"
				}
				return fmt.Sprintf("Create PR: %s (%s -> %s)", title, head, base), nil
			},
			Flags: []common.Flag{
				{Name: "title", Short: "t", Usage: "PR title", Required: true},
				{Name: "body", Short: "b", Usage: "PR description"},
				{Name: "head", Usage: "Source branch", Required: true},
				{Name: "base", Usage: "Target branch", Default: "master"},
			},
			Run: func(ctx *common.RuntimeContext) error {
				if err := ctx.ResolveOwnerRepo(); err != nil {
					return err
				}
				title, err := ctx.RequireArg("title", `--title "Fix login crash"`)
				if err != nil {
					return err
				}
				head, err := ctx.RequireArg("head", `--head feat/new-login`)
				if err != nil {
					return err
				}
				base := ctx.Arg("base")
				if base == "" {
					base = "master"
				}
				payload := map[string]interface{}{
					"title": title,
					"head":  head,
					"base":  base,
				}
				if b := ctx.Arg("body"); b != "" {
					payload["body"] = b
				}
				env, err := ctx.CallAPI("POST", ctx.RepoPath()+"/pulls", payload)
				if err != nil {
					return fmt.Errorf("创建 PR 失败: %w", err)
				}
				return ctx.Output(env)
			},
		},
		{
			Name:        "view",
			Description: "View pull request details",
			Flags: []common.Flag{
				{Name: "id", Short: "i", Usage: "PR number", Required: true},
			},
			Run: func(ctx *common.RuntimeContext) error {
				if err := ctx.ResolveOwnerRepo(); err != nil {
					return err
				}
				id, err := ctx.RequireArg("id", "--id 42")
				if err != nil {
					return err
				}
				env, err := ctx.CallAPI("GET", fmt.Sprintf("%s/pulls/%s", ctx.RepoPath(), id), nil)
				if err != nil {
					return fmt.Errorf("查看 PR 失败: %w", err)
				}
				return ctx.Output(env)
			},
		},
		{
			Name:        "merge",
			Description: "Merge a pull request",
			DryRun:      true,
			DryRunHint: func(ctx *common.RuntimeContext) (string, error) {
				id := ctx.Arg("id")
				method := ctx.Arg("method")
				if method == "" {
					method = "merge"
				}
				return fmt.Sprintf("Merge PR #%s (method: %s)", id, method), nil
			},
			Flags: []common.Flag{
				{Name: "id", Short: "i", Usage: "PR number", Required: true},
				{Name: "method", Short: "m", Usage: "Merge method: merge, rebase, squash", Default: "merge"},
			},
			Run: func(ctx *common.RuntimeContext) error {
				if err := ctx.ResolveOwnerRepo(); err != nil {
					return err
				}
				id, err := ctx.RequireArg("id", "--id 42")
				if err != nil {
					return err
				}
				method := ctx.Arg("method")
				if method == "" {
					method = "merge"
				}
				payload := map[string]interface{}{
					"do": method,
				}
				env, err := ctx.CallAPI("POST", fmt.Sprintf("%s/pulls/%s/pr_merge", ctx.RepoPath(), id), payload)
				if err != nil {
					return fmt.Errorf("合并 PR 失败: %w", err)
				}
				return ctx.Output(env)
			},
		},
		{
			Name:        "close",
			Description: "Close a pull request",
			DryRun:      true,
			DryRunHint: func(ctx *common.RuntimeContext) (string, error) {
				id := ctx.Arg("id")
				return fmt.Sprintf("Close PR #%s", id), nil
			},
			Flags: []common.Flag{
				{Name: "id", Short: "i", Usage: "PR number", Required: true},
			},
			Run: func(ctx *common.RuntimeContext) error {
				if err := ctx.ResolveOwnerRepo(); err != nil {
					return err
				}
				id, err := ctx.RequireArg("id", "--id 42")
				if err != nil {
					return err
				}
				env, err := ctx.CallAPI("POST", fmt.Sprintf("%s/pulls/%s/refuse_merge", ctx.RepoPath(), id), nil)
				if err != nil {
					return fmt.Errorf("关闭 PR 失败: %w", err)
				}
				return ctx.Output(env)
			},
		},
		{
			Name:        "files",
			Description: "List changed files in a pull request",
			Flags: []common.Flag{
				{Name: "id", Short: "i", Usage: "PR number", Required: true},
			},
			Run: func(ctx *common.RuntimeContext) error {
				if err := ctx.ResolveOwnerRepo(); err != nil {
					return err
				}
				id, err := ctx.RequireArg("id", "--id 42")
				if err != nil {
					return err
				}
				env, err := ctx.CallAPI("GET", fmt.Sprintf("%s/pulls/%s/files", ctx.RepoPath(), id), nil)
				if err != nil {
					return fmt.Errorf("获取 PR 文件列表失败: %w", err)
				}
				return ctx.Output(env)
			},
		},
		{
			Name:        "diff",
			Description: "Show diff for a pull request",
			Flags: []common.Flag{
				{Name: "id", Short: "i", Usage: "PR number", Required: true},
			},
			Run: func(ctx *common.RuntimeContext) error {
				if err := ctx.ResolveOwnerRepo(); err != nil {
					return err
				}
				id, err := ctx.RequireArg("id", "--id 42")
				if err != nil {
					return err
				}
				env, err := ctx.CallAPI("GET", fmt.Sprintf("%s/pulls/%s/files", ctx.RepoPath(), id), nil)
				if err != nil {
					return fmt.Errorf("获取 PR diff 失败: %w", err)
				}
				return ctx.Output(env)
			},
		},
		{
			Name:        "comment",
			Description: "Add a comment to a pull request",
			DryRun:      true,
			DryRunHint: func(ctx *common.RuntimeContext) (string, error) {
				id := ctx.Arg("id")
				return fmt.Sprintf("Add comment to PR #%s", id), nil
			},
			Flags: []common.Flag{
				{Name: "id", Short: "i", Usage: "PR number", Required: true},
				{Name: "body", Short: "b", Usage: "Comment body", Required: true},
			},
			Run: func(ctx *common.RuntimeContext) error {
				if err := ctx.ResolveOwnerRepo(); err != nil {
					return err
				}
				id, err := ctx.RequireArg("id", "--id 42")
				if err != nil {
					return err
				}
				body, err := ctx.RequireArg("body", `--body "Looks good to me"`)
				if err != nil {
					return err
				}

				prEnv, err := ctx.CallAPI("GET", fmt.Sprintf("%s/pulls/%s", ctx.RepoPath(), id), nil)
				if err != nil {
					return fmt.Errorf("获取 PR 信息失败: %w", err)
				}
				issueID, err := extractIssueID(prEnv)
				if err != nil {
					return err
				}

				payload := map[string]interface{}{
					"notes": body,
				}
				env, err := ctx.CallAPI("POST", fmt.Sprintf("/v1/%s/%s/issues/%d/journals", ctx.Owner, ctx.Repo, issueID), payload)
				if err != nil {
					return fmt.Errorf("添加 PR 评论失败: %w", err)
				}
				return ctx.Output(env)
			},
		},
	}
}

func extractIssueID(env *output.Envelope) (int64, error) {
	data, ok := env.Data.(map[string]interface{})
	if !ok {
		return 0, fmt.Errorf("unexpected PR response format")
	}
	issue, ok := data["issue"].(map[string]interface{})
	if !ok {
		return 0, fmt.Errorf("PR response missing issue field")
	}
	idFloat, ok := issue["id"].(float64)
	if !ok {
		return 0, fmt.Errorf("PR response missing issue.id field")
	}
	return int64(idFloat), nil
}
