package cmd

import (
	"fmt"
	"net/http"
	"strings"

	"code.gitea.io/sdk/gitea"
	"forgejo.zerova.net/public/fj/internal/api"
	"forgejo.zerova.net/public/fj/internal/config"
	"forgejo.zerova.net/public/fj/internal/text"
	"github.com/spf13/cobra"
)

var issueCmd = &cobra.Command{
	Use:   "issue",
	Short: "Manage issues",
	Long:  "Create, view, list, and manage issues.",
}

var issueListCmd = &cobra.Command{
	Use:   "list [flags]",
	Short: "List issues",
	Long:  "List issues in a repository.",
	Example: `  # List open issues
  fj issue list

  # List closed issues for a specific repo
  fj issue list -s closed -R owner/repo

  # Output as JSON
  fj issue list --json`,
	RunE: runIssueList,
}

var issueViewCmd = &cobra.Command{
	Use:   "view <number>",
	Short: "View an issue",
	Long:  "Display detailed information about an issue.",
	Example: `  # View issue #42
  fj issue view 42

  # View using URL
  fj issue view https://codeberg.org/owner/repo/issues/42

  # Open in browser
  fj issue view 42 --web

  # View an issue from a specific repo as JSON
  fj issue view 42 -R owner/repo --json`,
	Args: cobra.ExactArgs(1),
	RunE: runIssueView,
}

var issueCreateCmd = &cobra.Command{
	Use:   "create",
	Short: "Create an issue",
	Long:  "Create a new issue.",
	Example: `  # Create an issue with a title
  fj issue create -t "Fix login bug"

  # Create an issue with title, body, and labels
  fj issue create -t "Add dark mode" -b "We need a dark theme" -l feature -l ui`,
	RunE: runIssueCreate,
}

var issueCommentCmd = &cobra.Command{
	Use:   "comment <number>",
	Short: "Add a comment to an issue",
	Long:  "Add a comment to an existing issue.",
	Example: `  # Add a comment to issue #42
  fj issue comment 42 -b "This is fixed in the latest release"

  # Comment on an issue in a specific repo
  fj issue comment 10 -b "Confirmed on my end" -R owner/repo`,
	Args: cobra.ExactArgs(1),
	RunE: runIssueComment,
}

var issueCloseCmd = &cobra.Command{
	Use:   "close <number>",
	Short: "Close an issue",
	Long:  "Close an existing issue.",
	Example: `  # Close issue #42
  fj issue close 42

  # Close with a comment
  fj issue close 42 -c "Fixed in commit abc1234"`,
	Args: cobra.ExactArgs(1),
	RunE: runIssueClose,
}

var issueReopenCmd = &cobra.Command{
	Use:   "reopen <number>",
	Short: "Reopen an issue",
	Long:  "Reopen a closed issue.",
	Example: `  # Reopen issue #42
  fj issue reopen 42`,
	Args: cobra.ExactArgs(1),
	RunE: runIssueReopen,
}

var issueDeleteCmd = &cobra.Command{
	Use:   "delete <number>",
	Short: "Delete an issue",
	Long:  "Delete an issue permanently.",
	Example: `  # Delete issue #42
  fj issue delete 42

  # Delete without confirmation
  fj issue delete 42 -y`,
	Args: cobra.ExactArgs(1),
	RunE: runIssueDelete,
}

var issueEditCmd = &cobra.Command{
	Use:   "edit <number>",
	Short: "Edit an issue",
	Long:  "Edit an existing issue's title, body, or state.",
	Example: `  # Update the title of issue #42
  fj issue edit 42 -t "Updated title"

  # Reopen a closed issue
  fj issue edit 42 -s open

  # Add and remove labels
  fj issue edit 42 --add-label bug --remove-label wontfix

  # Add a dependency
  fj issue edit 42 --add-dependency 10`,
	Args: cobra.ExactArgs(1),
	RunE: runIssueEdit,
}

func init() {
	rootCmd.AddCommand(issueCmd)
	issueCmd.AddCommand(issueListCmd)
	issueCmd.AddCommand(issueViewCmd)
	issueCmd.AddCommand(issueCreateCmd)
	issueCmd.AddCommand(issueCommentCmd)
	issueCmd.AddCommand(issueCloseCmd)
	issueCmd.AddCommand(issueReopenCmd)
	issueCmd.AddCommand(issueDeleteCmd)
	issueCmd.AddCommand(issueEditCmd)

	issueReopenCmd.Flags().StringP("repo", "R", "", "Repository in owner/name format")

	issueListCmd.Flags().StringP("repo", "R", "", "Repository in owner/name format")
	issueListCmd.Flags().StringP("state", "s", "open", "Filter by state: open, closed, all")
	issueListCmd.Flags().IntP("limit", "L", 30, "Maximum number of results")
	issueListCmd.Flags().StringP("assignee", "a", "", "Filter by assignee username")
	issueListCmd.Flags().String("author", "", "Filter by author username")
	issueListCmd.Flags().StringSliceP("label", "l", nil, "Filter by label names")
	issueListCmd.Flags().StringP("search", "S", "", "Search keyword filter")
	addJSONFlags(issueListCmd, "Output issues as JSON")

	issueViewCmd.Flags().StringP("repo", "R", "", "Repository in owner/name format")
	addJSONFlags(issueViewCmd, "Output issue as JSON")
	issueViewCmd.Flags().BoolP("web", "w", false, "Open in web browser")

	issueCreateCmd.Flags().StringP("repo", "R", "", "Repository in owner/name format")
	issueCreateCmd.Flags().StringP("title", "t", "", "Title for the issue")
	issueCreateCmd.Flags().StringP("body", "b", "", "Body for the issue")
	issueCreateCmd.Flags().StringSliceP("label", "l", nil, "Labels to add (can be specified multiple times)")
	issueCreateCmd.Flags().StringSliceP("assignee", "a", nil, "Assign people by their login. Use \"@me\" to self-assign.")
	issueCreateCmd.Flags().StringP("milestone", "m", "", "Milestone name to associate with the issue")

	issueCommentCmd.Flags().StringP("repo", "R", "", "Repository in owner/name format")
	issueCommentCmd.Flags().StringP("body", "b", "", "Comment body")

	issueCloseCmd.Flags().StringP("repo", "R", "", "Repository in owner/name format")
	issueCloseCmd.Flags().StringP("comment", "c", "", "Comment body to add before closing")

	issueDeleteCmd.Flags().StringP("repo", "R", "", "Repository in owner/name format")
	issueDeleteCmd.Flags().BoolP("yes", "y", false, "Skip confirmation prompt")

	issueEditCmd.Flags().StringP("repo", "R", "", "Repository in owner/name format")
	issueEditCmd.Flags().StringP("title", "t", "", "New title for the issue")
	issueEditCmd.Flags().StringP("body", "b", "", "New body for the issue")
	issueEditCmd.Flags().StringP("state", "s", "", "New state for the issue (open or closed)")
	issueEditCmd.Flags().StringSlice("add-label", nil, "Labels to add (can be specified multiple times)")
	issueEditCmd.Flags().StringSlice("remove-label", nil, "Labels to remove (can be specified multiple times)")
	issueEditCmd.Flags().Int64Slice("add-dependency", nil, "Issue numbers to add as dependencies (can be specified multiple times)")
	issueEditCmd.Flags().Int64Slice("remove-dependency", nil, "Issue numbers to remove as dependencies (can be specified multiple times)")
}

func runIssueList(cmd *cobra.Command, args []string) error {
	repo, _ := cmd.Flags().GetString("repo")
	state, _ := cmd.Flags().GetString("state")
	limit, _ := cmd.Flags().GetInt("limit")
	assignee, _ := cmd.Flags().GetString("assignee")
	author, _ := cmd.Flags().GetString("author")
	labels, _ := cmd.Flags().GetStringSlice("label")
	search, _ := cmd.Flags().GetString("search")

	owner, name, err := parseRepo(repo)
	if err != nil {
		return err
	}

	cfg, err := config.Load()
	if err != nil {
		return err
	}

	client, err := api.NewClientFromConfig(cfg, "", getDetectedHost(), getCwd())
	if err != nil {
		return err
	}

	var stateType gitea.StateType
	switch strings.ToLower(state) {
	case "open":
		stateType = gitea.StateOpen
	case "closed":
		stateType = gitea.StateClosed
	case "all":
		stateType = gitea.StateAll
	default:
		return fmt.Errorf("invalid state: %s", state)
	}

	ios.StartSpinner("Fetching issues...")
	issues, _, err := client.ListRepoIssues(owner, name, gitea.ListIssueOption{
		State:       stateType,
		Labels:      labels,
		KeyWord:     search,
		CreatedBy:   author,
		AssignedBy:  assignee,
		ListOptions: gitea.ListOptions{PageSize: limit},
	})
	ios.StopSpinner()
	if err != nil {
		return fmt.Errorf("failed to list issues: %w", err)
	}

	nonPRIssues := make([]*gitea.Issue, 0, len(issues))
	for _, issue := range issues {
		if issue.PullRequest == nil {
			nonPRIssues = append(nonPRIssues, issue)
		}
	}

	if wantJSON(cmd) {
		return outputJSON(cmd, nonPRIssues)
	}

	if len(nonPRIssues) == 0 {
		fmt.Fprintf(ios.Out, "No %s issues in %s/%s\n", state, owner, name)
		return nil
	}

	tp := ios.NewTablePrinter()
	tp.AddHeader("NUMBER", "TITLE", "STATE")
	for _, issue := range nonPRIssues {
		tp.AddRow(fmt.Sprintf("#%d", issue.Index), issue.Title, string(issue.State))
	}
	return tp.Render()
}

func runIssueView(cmd *cobra.Command, args []string) error {
	repo, _ := cmd.Flags().GetString("repo")
	issueNumber, err := parseIssueArg(args[0])
	if err != nil {
		return fmt.Errorf("invalid issue number: %w", err)
	}

	owner, name, err := parseRepo(repo)
	if err != nil {
		return err
	}

	cfg, err := config.Load()
	if err != nil {
		return err
	}

	client, err := api.NewClientFromConfig(cfg, "", getDetectedHost(), getCwd())
	if err != nil {
		return err
	}

	ios.StartSpinner("Fetching issue...")
	issue, _, err := client.GetIssue(owner, name, issueNumber)
	if err != nil {
		ios.StopSpinner()
		return fmt.Errorf("failed to get issue: %w", err)
	}

	var comments []*gitea.Comment
	comments, _, err = client.ListIssueComments(owner, name, issueNumber, gitea.ListIssueCommentOptions{})
	if err != nil {
		comments = nil
	}
	ios.StopSpinner()

	if web, _ := cmd.Flags().GetBool("web"); web {
		return ios.OpenInBrowser(issue.HTMLURL)
	}

	if wantJSON(cmd) {
		payload := struct {
			Issue    *gitea.Issue     `json:"issue"`
			Comments []*gitea.Comment `json:"comments,omitempty"`
		}{
			Issue:    issue,
			Comments: comments,
		}
		return outputJSON(cmd, payload)
	}

	if err := ios.StartPager(); err != nil {
		fmt.Fprintf(ios.ErrOut, "warning: failed to start pager: %v\n", err)
	}
	defer ios.StopPager()

	cs := ios.ColorScheme()
	isTTY := ios.IsStdoutTTY()

	fmt.Fprintf(ios.Out, "Issue #%d\n", issue.Index)
	fmt.Fprintf(ios.Out, "Title: %s\n", cs.Bold(issue.Title))
	fmt.Fprintf(ios.Out, "State: %s\n", issue.State)
	fmt.Fprintf(ios.Out, "Author: %s\n", issue.Poster.UserName)
	fmt.Fprintf(ios.Out, "Created: %s\n", text.FormatDate(issue.Created, isTTY))
	fmt.Fprintf(ios.Out, "Updated: %s\n", text.FormatDate(issue.Updated, isTTY))
	if issue.Body != "" {
		fmt.Fprintf(ios.Out, "\n%s\n", issue.Body)
	}

	if len(comments) > 0 {
		fmt.Fprintf(ios.Out, "\nComments (%d):\n", len(comments))
		for _, comment := range comments {
			fmt.Fprintf(ios.Out, "\n---\n%s (@%s) - %s\n%s\n",
				comment.Poster.FullName,
				comment.Poster.UserName,
				text.FormatDate(comment.Created, isTTY),
				comment.Body)
		}
	}

	return nil
}

func runIssueCreate(cmd *cobra.Command, args []string) error {
	repo, _ := cmd.Flags().GetString("repo")
	title, _ := cmd.Flags().GetString("title")
	body, _ := cmd.Flags().GetString("body")
	labelNames, _ := cmd.Flags().GetStringSlice("label")
	assignees, _ := cmd.Flags().GetStringSlice("assignee")
	milestoneName, _ := cmd.Flags().GetString("milestone")

	owner, name, err := parseRepo(repo)
	if err != nil {
		return err
	}

	// Interactive mode: prompt for missing fields when TTY
	if title == "" && ios.IsStdinTTY() {
		title, err = promptLine("Title: ")
		if err != nil {
			return err
		}
		if title == "" {
			return fmt.Errorf("title is required")
		}
		if body == "" {
			body, _ = promptLine("Body (optional): ")
		}
	} else if title == "" {
		return fmt.Errorf("title is required (use -t flag)")
	}

	cfg, err := config.Load()
	if err != nil {
		return err
	}

	client, err := api.NewClientFromConfig(cfg, "", getDetectedHost(), getCwd())
	if err != nil {
		return err
	}

	var labelIDs []int64
	if len(labelNames) > 0 {
		labelIDs, err = resolveLabelIDs(client, owner, name, labelNames)
		if err != nil {
			return err
		}
	}

	// Resolve @me in assignees
	resolvedAssignees := make([]string, 0, len(assignees))
	for _, assignee := range assignees {
		if assignee == "@me" {
			user, _, userErr := client.GetMyUserInfo()
			if userErr != nil {
				return fmt.Errorf("failed to get current user info: %w", userErr)
			}
			resolvedAssignees = append(resolvedAssignees, user.UserName)
		} else {
			resolvedAssignees = append(resolvedAssignees, assignee)
		}
	}

	// Resolve milestone name to ID
	var milestoneID int64
	if milestoneName != "" {
		milestones, _, msErr := client.ListRepoMilestones(owner, name, gitea.ListMilestoneOption{})
		if msErr != nil {
			return fmt.Errorf("failed to list milestones: %w", msErr)
		}
		found := false
		for _, ms := range milestones {
			if ms.Title == milestoneName {
				milestoneID = ms.ID
				found = true
				break
			}
		}
		if !found {
			return fmt.Errorf("milestone not found: %s", milestoneName)
		}
	}

	ios.StartSpinner("Creating issue...")
	issue, _, err := client.CreateIssue(owner, name, gitea.CreateIssueOption{
		Title:     title,
		Body:      body,
		Labels:    labelIDs,
		Assignees: resolvedAssignees,
		Milestone: milestoneID,
	})
	ios.StopSpinner()
	if err != nil {
		return fmt.Errorf("failed to create issue: %w", err)
	}

	cs := ios.ColorScheme()
	fmt.Fprintf(ios.Out, "%s Issue created: #%d\n", cs.SuccessIcon(), issue.Index)
	fmt.Fprintf(ios.Out, "View at: %s\n", issue.HTMLURL)

	return nil
}

func runIssueComment(cmd *cobra.Command, args []string) error {
	repo, _ := cmd.Flags().GetString("repo")
	body, _ := cmd.Flags().GetString("body")
	issueNumber, err := parseIssueArg(args[0])
	if err != nil {
		return fmt.Errorf("invalid issue number: %w", err)
	}

	owner, name, err := parseRepo(repo)
	if err != nil {
		return err
	}

	if body == "" {
		return fmt.Errorf("comment body is required")
	}

	cfg, err := config.Load()
	if err != nil {
		return err
	}

	client, err := api.NewClientFromConfig(cfg, "", getDetectedHost(), getCwd())
	if err != nil {
		return err
	}

	ios.StartSpinner("Adding comment...")
	comment, _, err := client.CreateIssueComment(owner, name, issueNumber, gitea.CreateIssueCommentOption{
		Body: body,
	})
	ios.StopSpinner()
	if err != nil {
		return fmt.Errorf("failed to create comment: %w", err)
	}

	cs := ios.ColorScheme()
	fmt.Fprintf(ios.Out, "%s Comment added to issue #%d\n", cs.SuccessIcon(), issueNumber)
	fmt.Fprintf(ios.Out, "View at: %s\n", comment.HTMLURL)

	return nil
}

func runIssueClose(cmd *cobra.Command, args []string) error {
	repo, _ := cmd.Flags().GetString("repo")
	commentBody, _ := cmd.Flags().GetString("comment")
	issueNumber, err := parseIssueArg(args[0])
	if err != nil {
		return fmt.Errorf("invalid issue number: %w", err)
	}

	owner, name, err := parseRepo(repo)
	if err != nil {
		return err
	}

	cfg, err := config.Load()
	if err != nil {
		return err
	}

	client, err := api.NewClientFromConfig(cfg, "", getDetectedHost(), getCwd())
	if err != nil {
		return err
	}

	if commentBody != "" {
		ios.StartSpinner("Adding comment...")
		_, _, err = client.CreateIssueComment(owner, name, issueNumber, gitea.CreateIssueCommentOption{
			Body: commentBody,
		})
		ios.StopSpinner()
		if err != nil {
			return fmt.Errorf("failed to create comment: %w", err)
		}
	}

	ios.StartSpinner("Closing issue...")
	stateClosed := gitea.StateClosed
	_, _, err = client.EditIssue(owner, name, issueNumber, gitea.EditIssueOption{
		State: &stateClosed,
	})
	ios.StopSpinner()
	if err != nil {
		return fmt.Errorf("failed to close issue: %w", err)
	}

	cs := ios.ColorScheme()
	fmt.Fprintf(ios.Out, "%s Issue #%d closed\n", cs.SuccessIcon(), issueNumber)

	return nil
}

func runIssueEdit(cmd *cobra.Command, args []string) error {
	repo, _ := cmd.Flags().GetString("repo")
	title, _ := cmd.Flags().GetString("title")
	body, _ := cmd.Flags().GetString("body")
	stateStr, _ := cmd.Flags().GetString("state")
	addLabelNames, _ := cmd.Flags().GetStringSlice("add-label")
	removeLabelNames, _ := cmd.Flags().GetStringSlice("remove-label")
	addDeps, _ := cmd.Flags().GetInt64Slice("add-dependency")
	removeDeps, _ := cmd.Flags().GetInt64Slice("remove-dependency")

	issueNumber, err := parseIssueArg(args[0])
	if err != nil {
		return fmt.Errorf("invalid issue number: %w", err)
	}

	owner, name, err := parseRepo(repo)
	if err != nil {
		return err
	}

	if title == "" && body == "" && stateStr == "" && len(addLabelNames) == 0 && len(removeLabelNames) == 0 && len(addDeps) == 0 && len(removeDeps) == 0 {
		return fmt.Errorf("at least one of --title, --body, --state, --add-label, --remove-label, --add-dependency, or --remove-dependency must be provided")
	}

	cfg, err := config.Load()
	if err != nil {
		return err
	}

	client, err := api.NewClientFromConfig(cfg, "", getDetectedHost(), getCwd())
	if err != nil {
		return err
	}

	editOpt := gitea.EditIssueOption{}

	if title != "" {
		editOpt.Title = title
	}

	if body != "" {
		editOpt.Body = &body
	}

	if stateStr != "" {
		switch strings.ToLower(stateStr) {
		case "open":
			stateOpen := gitea.StateOpen
			editOpt.State = &stateOpen
		case "closed":
			stateClosed := gitea.StateClosed
			editOpt.State = &stateClosed
		default:
			return fmt.Errorf("invalid state: %s (must be 'open' or 'closed')", stateStr)
		}
	}

	ios.StartSpinner("Updating issue...")

	if title != "" || body != "" || stateStr != "" {
		_, _, err = client.EditIssue(owner, name, issueNumber, editOpt)
		if err != nil {
			ios.StopSpinner()
			return fmt.Errorf("failed to edit issue: %w", err)
		}
	}

	if len(addLabelNames) > 0 {
		labelIDs, err := resolveLabelIDs(client, owner, name, addLabelNames)
		if err != nil {
			ios.StopSpinner()
			return err
		}
		_, _, err = client.AddIssueLabels(owner, name, issueNumber, gitea.IssueLabelsOption{
			Labels: labelIDs,
		})
		if err != nil {
			ios.StopSpinner()
			return fmt.Errorf("failed to add labels: %w", err)
		}
	}

	if len(removeLabelNames) > 0 {
		labelIDs, err := resolveLabelIDs(client, owner, name, removeLabelNames)
		if err != nil {
			ios.StopSpinner()
			return err
		}
		for _, labelID := range labelIDs {
			_, err = client.DeleteIssueLabel(owner, name, issueNumber, labelID)
			if err != nil {
				ios.StopSpinner()
				return fmt.Errorf("failed to remove label %d: %w", labelID, err)
			}
		}
	}

	ios.StopSpinner()

	for _, depNumber := range addDeps {
		depIssue, _, err := client.GetIssue(owner, name, depNumber)
		if err != nil {
			return fmt.Errorf("failed to get issue #%d: %w", depNumber, err)
		}
		depBody := map[string]int64{"id": depIssue.ID}
		path := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/dependencies", owner, name, issueNumber)
		_, err = client.DoJSON(http.MethodPost, path, depBody, nil)
		if err != nil {
			return fmt.Errorf("failed to add dependency #%d: %w", depNumber, err)
		}
		fmt.Fprintf(ios.Out, "Added dependency: #%d depends on #%d\n", issueNumber, depNumber)
	}

	for _, depNumber := range removeDeps {
		depIssue, _, err := client.GetIssue(owner, name, depNumber)
		if err != nil {
			return fmt.Errorf("failed to get issue #%d: %w", depNumber, err)
		}
		depBody := map[string]int64{"id": depIssue.ID}
		path := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/dependencies", owner, name, issueNumber)
		_, err = client.DoJSON(http.MethodDelete, path, depBody, nil)
		if err != nil {
			return fmt.Errorf("failed to remove dependency #%d: %w", depNumber, err)
		}
		fmt.Fprintf(ios.Out, "Removed dependency: #%d no longer depends on #%d\n", issueNumber, depNumber)
	}

	cs := ios.ColorScheme()
	fmt.Fprintf(ios.Out, "%s Issue #%d updated\n", cs.SuccessIcon(), issueNumber)

	return nil
}

func runIssueDelete(cmd *cobra.Command, args []string) error {
	repo, _ := cmd.Flags().GetString("repo")
	yes, _ := cmd.Flags().GetBool("yes")

	issueNumber, err := parseIssueArg(args[0])
	if err != nil {
		return fmt.Errorf("invalid issue number: %w", err)
	}

	owner, name, err := parseRepo(repo)
	if err != nil {
		return err
	}

	cfg, err := config.Load()
	if err != nil {
		return err
	}

	client, err := api.NewClientFromConfig(cfg, "", getDetectedHost(), getCwd())
	if err != nil {
		return err
	}

	if !yes {
		confirmed, confirmErr := ios.ConfirmAction(fmt.Sprintf("Permanently delete issue #%d from %s/%s?", issueNumber, owner, name))
		if confirmErr != nil {
			return confirmErr
		}
		if !confirmed {
			fmt.Fprintln(ios.ErrOut, "Aborted")
			return nil
		}
	}

	ios.StartSpinner("Deleting issue...")
	_, err = client.DeleteIssue(owner, name, issueNumber)
	ios.StopSpinner()
	if err != nil {
		return fmt.Errorf("failed to delete issue: %w", err)
	}

	cs := ios.ColorScheme()
	fmt.Fprintf(ios.Out, "%s Issue #%d deleted from %s/%s\n", cs.SuccessIcon(), issueNumber, owner, name)
	return nil
}

func runIssueReopen(cmd *cobra.Command, args []string) error {
	repo, _ := cmd.Flags().GetString("repo")
	issueNumber, err := parseIssueArg(args[0])
	if err != nil {
		return fmt.Errorf("invalid issue number: %w", err)
	}

	owner, name, err := parseRepo(repo)
	if err != nil {
		return err
	}

	cfg, err := config.Load()
	if err != nil {
		return err
	}

	client, err := api.NewClientFromConfig(cfg, "", getDetectedHost(), getCwd())
	if err != nil {
		return err
	}

	ios.StartSpinner("Reopening issue...")
	stateOpen := gitea.StateOpen
	_, _, err = client.EditIssue(owner, name, issueNumber, gitea.EditIssueOption{
		State: &stateOpen,
	})
	ios.StopSpinner()
	if err != nil {
		return fmt.Errorf("failed to reopen issue: %w", err)
	}

	cs := ios.ColorScheme()
	fmt.Fprintf(ios.Out, "%s Issue #%d reopened\n", cs.SuccessIcon(), issueNumber)

	return nil
}

func resolveLabelIDs(client *api.Client, owner, name string, labelNames []string) ([]int64, error) {
	if len(labelNames) == 0 {
		return nil, nil
	}

	labels, _, err := client.ListRepoLabels(owner, name, gitea.ListLabelsOptions{})
	if err != nil {
		return nil, fmt.Errorf("failed to list labels: %w", err)
	}

	labelNameToID := make(map[string]int64)
	for _, label := range labels {
		labelNameToID[label.Name] = label.ID
	}

	var labelIDs []int64
	var missingLabels []string
	for _, labelName := range labelNames {
		labelID, exists := labelNameToID[labelName]
		if !exists {
			missingLabels = append(missingLabels, labelName)
			continue
		}
		labelIDs = append(labelIDs, labelID)
	}

	if len(missingLabels) > 0 {
		return nil, fmt.Errorf("labels not found: %s", strings.Join(missingLabels, ", "))
	}

	return labelIDs, nil
}
