package cmd

import (
	"fmt"
	"strings"

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

var labelCmd = &cobra.Command{
	Use:   "label",
	Short: "Manage labels",
	Long:  "List, create, edit, and delete repository labels.",
}

var labelListCmd = &cobra.Command{
	Use:   "list",
	Short: "List labels for a repository",
	Long:  "List all labels defined in a repository.",
	Example: `  # List labels for the current repository
  fj label list

  # List labels for a specific repository
  fj label list -R owner/repo

  # Output as JSON
  fj label list --json`,
	RunE: runLabelList,
}

var labelCreateCmd = &cobra.Command{
	Use:   "create <name>",
	Short: "Create a label",
	Long:  "Create a new label in a repository.",
	Example: `  # Create a label with a color
  fj label create bug -c ff0000

  # Create a label with color and description
  fj label create feature -c 00ff00 -d "New feature request"

  # Create a label in a specific repository
  fj label create urgent -c ff0000 -R owner/repo`,
	Args: cobra.ExactArgs(1),
	RunE: runLabelCreate,
}

var labelEditCmd = &cobra.Command{
	Use:   "edit <name>",
	Short: "Edit a label",
	Long:  "Edit an existing label in a repository.",
	Example: `  # Rename a label
  fj label edit bug --name bugfix

  # Change the color of a label
  fj label edit bug -c 00ff00

  # Update description
  fj label edit bug -d "Something is broken"`,
	Args: cobra.ExactArgs(1),
	RunE: runLabelEdit,
}

var labelDeleteCmd = &cobra.Command{
	Use:   "delete <name>",
	Short: "Delete a label",
	Long:  "Delete a label from a repository.",
	Example: `  # Delete a label
  fj label delete bug

  # Delete without confirmation
  fj label delete bug -y

  # Delete a label from a specific repository
  fj label delete bug -R owner/repo`,
	Args: cobra.ExactArgs(1),
	RunE: runLabelDelete,
}

func init() {
	rootCmd.AddCommand(labelCmd)
	labelCmd.AddCommand(labelListCmd)
	labelCmd.AddCommand(labelCreateCmd)
	labelCmd.AddCommand(labelEditCmd)
	labelCmd.AddCommand(labelDeleteCmd)

	labelListCmd.Flags().StringP("repo", "R", "", "Repository in owner/name format")
	addJSONFlags(labelListCmd, "Output as JSON")

	labelCreateCmd.Flags().StringP("repo", "R", "", "Repository in owner/name format")
	labelCreateCmd.Flags().StringP("color", "c", "", "Label color (hex, e.g. 00ff00)")
	labelCreateCmd.Flags().StringP("description", "d", "", "Label description")
	addJSONFlags(labelCreateCmd, "Output as JSON")

	labelEditCmd.Flags().StringP("repo", "R", "", "Repository in owner/name format")
	labelEditCmd.Flags().String("name", "", "New name for the label")
	labelEditCmd.Flags().StringP("color", "c", "", "New color (hex, e.g. 00ff00)")
	labelEditCmd.Flags().StringP("description", "d", "", "New description")
	addJSONFlags(labelEditCmd, "Output as JSON")

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

func newLabelClient(cmd *cobra.Command) (*api.Client, string, string, error) {
	repo, _ := cmd.Flags().GetString("repo")
	owner, name, err := parseRepo(repo)
	if err != nil {
		return nil, "", "", err
	}

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

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

	return client, owner, name, nil
}

// findLabelByName lists all repo labels and returns the one matching the given name.
func findLabelByName(client *api.Client, owner, repo, labelName string) (*gitea.Label, error) {
	labels, _, err := client.ListRepoLabels(owner, repo, gitea.ListLabelsOptions{})
	if err != nil {
		return nil, fmt.Errorf("failed to list labels: %w", err)
	}

	for _, l := range labels {
		if strings.EqualFold(l.Name, labelName) {
			return l, nil
		}
	}

	return nil, fmt.Errorf("label not found: %s", labelName)
}

func runLabelList(cmd *cobra.Command, args []string) error {
	client, owner, name, err := newLabelClient(cmd)
	if err != nil {
		return err
	}

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

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

	if len(labels) == 0 {
		fmt.Fprintln(ios.Out, "No labels found")
		return nil
	}

	tp := ios.NewTablePrinter()
	tp.AddHeader("NAME", "COLOR", "DESCRIPTION")
	for _, l := range labels {
		tp.AddRow(l.Name, l.Color, l.Description)
	}
	return tp.Render()
}

func runLabelCreate(cmd *cobra.Command, args []string) error {
	labelName := args[0]
	color, _ := cmd.Flags().GetString("color")
	description, _ := cmd.Flags().GetString("description")

	client, owner, name, err := newLabelClient(cmd)
	if err != nil {
		return err
	}

	ios.StartSpinner("Creating label...")
	label, _, err := client.CreateLabel(owner, name, gitea.CreateLabelOption{
		Name:        labelName,
		Color:       color,
		Description: description,
	})
	ios.StopSpinner()
	if err != nil {
		return fmt.Errorf("failed to create label: %w", err)
	}

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

	cs := ios.ColorScheme()
	fmt.Fprintf(ios.Out, "%s Label created: %s\n", cs.SuccessIcon(), label.Name)
	return nil
}

func runLabelEdit(cmd *cobra.Command, args []string) error {
	labelName := args[0]

	client, owner, name, err := newLabelClient(cmd)
	if err != nil {
		return err
	}

	ios.StartSpinner("Fetching label...")
	existing, err := findLabelByName(client, owner, name, labelName)
	ios.StopSpinner()
	if err != nil {
		return err
	}

	opt := gitea.EditLabelOption{}
	changed := false

	if cmd.Flags().Changed("name") {
		n, _ := cmd.Flags().GetString("name")
		opt.Name = &n
		changed = true
	}
	if cmd.Flags().Changed("color") {
		c, _ := cmd.Flags().GetString("color")
		opt.Color = &c
		changed = true
	}
	if cmd.Flags().Changed("description") {
		d, _ := cmd.Flags().GetString("description")
		opt.Description = &d
		changed = true
	}

	if !changed {
		return fmt.Errorf("no changes specified; use flags like --name, --color, or --description")
	}

	ios.StartSpinner("Updating label...")
	label, _, err := client.EditLabel(owner, name, existing.ID, opt)
	ios.StopSpinner()
	if err != nil {
		return fmt.Errorf("failed to edit label: %w", err)
	}

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

	cs := ios.ColorScheme()
	fmt.Fprintf(ios.Out, "%s Label updated: %s\n", cs.SuccessIcon(), label.Name)
	return nil
}

func runLabelDelete(cmd *cobra.Command, args []string) error {
	labelName := args[0]
	yes, _ := cmd.Flags().GetBool("yes")

	client, owner, name, err := newLabelClient(cmd)
	if err != nil {
		return err
	}

	ios.StartSpinner("Fetching label...")
	existing, err := findLabelByName(client, owner, name, labelName)
	ios.StopSpinner()
	if err != nil {
		return err
	}

	if !yes {
		confirmed, err := ios.ConfirmAction(fmt.Sprintf("Delete label %q?", labelName))
		if err != nil {
			return err
		}
		if !confirmed {
			fmt.Fprintln(ios.ErrOut, "Aborted")
			return nil
		}
	}

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

	cs := ios.ColorScheme()
	fmt.Fprintf(ios.Out, "%s Label deleted: %s\n", cs.SuccessIcon(), labelName)
	return nil
}
