package cmd

import (
	"fmt"
	"io"
	"os"

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

var prCommentCmd = &cobra.Command{
	Use:   "comment <number>",
	Short: "Add a comment to a pull request",
	Long:  "Add a comment to an existing pull request.",
	Example: `  # Add a comment
  fj pr comment 123 -b "Looks good!"

  # Comment from a file
  fj pr comment 123 --body-file review-notes.md

  # Comment from stdin
  echo "LGTM" | fj pr comment 123 --body-file -

  # Output as JSON
  fj pr comment 123 -b "Nice work" --json`,
	Args: cobra.ExactArgs(1),
	RunE: runPRComment,
}

var prReviewCmd = &cobra.Command{
	Use:   "review <number>",
	Short: "Submit a review on a pull request",
	Long:  "Submit a review on a pull request. Exactly one of --approve, --request-changes, or --comment must be specified.",
	Example: `  # Approve a PR
  fj pr review 123 --approve -b "LGTM"

  # Request changes
  fj pr review 123 --request-changes -b "Please fix the error handling"

  # Submit a review comment
  fj pr review 123 --comment -b "Some observations"

  # Request changes with body from file
  fj pr review 123 --request-changes --body-file feedback.md`,
	Args: cobra.ExactArgs(1),
	RunE: runPRReview,
}

func init() {
	prCmd.AddCommand(prCommentCmd)
	prCmd.AddCommand(prReviewCmd)

	prCommentCmd.Flags().StringP("repo", "R", "", "Repository in owner/name format")
	prCommentCmd.Flags().StringP("body", "b", "", "Comment body")
	prCommentCmd.Flags().String("body-file", "", "Read body from file (use \"-\" for stdin)")
	addJSONFlags(prCommentCmd, "Output created comment as JSON")

	prReviewCmd.Flags().StringP("repo", "R", "", "Repository in owner/name format")
	prReviewCmd.Flags().BoolP("approve", "a", false, "Approve the pull request")
	prReviewCmd.Flags().BoolP("request-changes", "r", false, "Request changes on the pull request")
	prReviewCmd.Flags().BoolP("comment", "c", false, "Submit as a review comment")
	prReviewCmd.Flags().StringP("body", "b", "", "Review body/message")
	prReviewCmd.Flags().String("body-file", "", "Read body from file (use \"-\" for stdin)")
	addJSONFlags(prReviewCmd, "Output created review as JSON")
}

// readBody resolves the body text from --body and --body-file flags.
// --body takes precedence; if --body-file is set and --body is empty, the file
// (or stdin when the path is "-") is read instead.
func readBody(cmd *cobra.Command) (string, error) {
	body, _ := cmd.Flags().GetString("body")
	bodyFile, _ := cmd.Flags().GetString("body-file")

	if body != "" && bodyFile != "" {
		return "", fmt.Errorf("use either --body or --body-file, not both")
	}

	if bodyFile != "" {
		var data []byte
		var err error
		if bodyFile == "-" {
			data, err = io.ReadAll(os.Stdin)
		} else {
			data, err = os.ReadFile(bodyFile)
		}
		if err != nil {
			return "", fmt.Errorf("failed to read body file: %w", err)
		}
		body = string(data)
	}

	return body, nil
}

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

	body, err := readBody(cmd)
	if err != nil {
		return err
	}

	if body == "" {
		return fmt.Errorf("comment body is required (use --body or --body-file)")
	}

	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("Adding comment...")
	comment, _, err := client.CreateIssueComment(owner, name, prNumber, gitea.CreateIssueCommentOption{
		Body: body,
	})
	ios.StopSpinner()
	if err != nil {
		return fmt.Errorf("failed to create comment: %w", err)
	}

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

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

	return nil
}

func runPRReview(cmd *cobra.Command, args []string) error {
	repo, _ := cmd.Flags().GetString("repo")
	approve, _ := cmd.Flags().GetBool("approve")
	requestChanges, _ := cmd.Flags().GetBool("request-changes")
	commentReview, _ := cmd.Flags().GetBool("comment")

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

	// Validate exactly one review type is specified
	count := 0
	if approve {
		count++
	}
	if requestChanges {
		count++
	}
	if commentReview {
		count++
	}
	if count != 1 {
		return fmt.Errorf("exactly one of --approve, --request-changes, or --comment must be specified")
	}

	body, err := readBody(cmd)
	if err != nil {
		return err
	}

	if requestChanges && body == "" {
		return fmt.Errorf("body is required when requesting changes (use --body or --body-file)")
	}

	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 state gitea.ReviewStateType
	var action string
	switch {
	case approve:
		state = gitea.ReviewStateApproved
		action = "approved"
	case requestChanges:
		state = gitea.ReviewStateRequestChanges
		action = "reviewed with requested changes"
	case commentReview:
		state = gitea.ReviewStateComment
		action = "reviewed with comment"
	}

	ios.StartSpinner("Submitting review...")
	review, _, err := client.CreatePullReview(owner, name, prNumber, gitea.CreatePullReviewOptions{
		State: state,
		Body:  body,
	})
	ios.StopSpinner()
	if err != nil {
		return fmt.Errorf("failed to create review: %w", err)
	}

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

	cs := ios.ColorScheme()
	fmt.Fprintf(ios.Out, "%s PR #%d %s\n", cs.SuccessIcon(), prNumber, action)
	if review.HTMLURL != "" {
		fmt.Fprintf(ios.Out, "View at: %s\n", review.HTMLURL)
	}

	return nil
}
