package main

import (
	"fmt"
	"os/exec"
	"strings"
	"time"

	"github.com/orchard9/rdev/internal/sdlc"
	"github.com/spf13/cobra"
)

var mergeStrategy string

var mergeCmd = &cobra.Command{
	Use:   "merge <slug>",
	Short: "Merge a feature branch after all gates pass",
	Args:  cobra.ExactArgs(1),
	RunE: func(_ *cobra.Command, args []string) error {
		root := mustResolveRoot()
		slug := args[0]

		// Check merge readiness
		checklist, err := sdlc.MergeChecklist(root, slug)
		if err != nil {
			return err
		}
		if len(checklist) > 0 {
			if jsonOutput {
				if err := printJSON(map[string]any{
					"error":     "merge not ready",
					"checklist": checklist,
				}); err != nil {
					return err
				}
			}
			return fmt.Errorf("%w: %s", sdlc.ErrMergeNotReady, strings.Join(checklist, ", "))
		}

		f, err := sdlc.LoadFeature(root, slug)
		if err != nil {
			return err
		}

		if f.Branch == "" {
			return fmt.Errorf("feature %s has no branch", slug)
		}

		manifest, err := sdlc.LoadBranch(root, f.Branch)
		if err != nil {
			return err
		}

		strategy := mergeStrategy
		if strategy == "" {
			strategy = "squash"
		}

		// Record original branch for rollback
		origBranchCmd := exec.Command("git", "rev-parse", "--abbrev-ref", "HEAD")
		origBranchCmd.Dir = root
		origBranchOut, err := origBranchCmd.Output()
		if err != nil {
			return fmt.Errorf("get current branch: %w", err)
		}
		originalBranch := strings.TrimSpace(string(origBranchOut))

		// Record HEAD for rollback
		headCmd := exec.Command("git", "rev-parse", "HEAD")
		headCmd.Dir = root
		headOut, err := headCmd.Output()
		if err != nil {
			return fmt.Errorf("get HEAD: %w", err)
		}
		originalHead := strings.TrimSpace(string(headOut))

		// rollback reverts git state on failure
		rollback := func() {
			// Reset to original HEAD
			resetCmd := exec.Command("git", "reset", "--hard", originalHead)
			resetCmd.Dir = root
			_ = resetCmd.Run()

			// Checkout original branch if different
			if originalBranch != manifest.BaseBranch {
				checkoutOrigCmd := exec.Command("git", "checkout", originalBranch)
				checkoutOrigCmd.Dir = root
				_ = checkoutOrigCmd.Run()
			}
		}

		// Checkout main branch
		checkoutCmd := exec.Command("git", "checkout", manifest.BaseBranch)
		checkoutCmd.Dir = root
		if out, err := checkoutCmd.CombinedOutput(); err != nil {
			return fmt.Errorf("git checkout %s: %s: %w", manifest.BaseBranch, string(out), err)
		}

		// Record main branch HEAD for rollback after checkout
		mainHeadCmd := exec.Command("git", "rev-parse", "HEAD")
		mainHeadCmd.Dir = root
		mainHeadOut, err := mainHeadCmd.Output()
		if err != nil {
			rollback()
			return fmt.Errorf("get main HEAD: %w", err)
		}
		mainHead := strings.TrimSpace(string(mainHeadOut))

		// Update rollback to use main branch HEAD
		rollbackAfterMerge := func() {
			resetCmd := exec.Command("git", "reset", "--hard", mainHead)
			resetCmd.Dir = root
			_ = resetCmd.Run()
		}

		// Determine merge ref: prefer local branch, fall back to origin/<branch>
		// when running in worker pods where resetToMain leaves only remote refs.
		mergeRef := f.Branch
		checkBranch := exec.Command("git", "rev-parse", "--verify", f.Branch)
		checkBranch.Dir = root
		if err := checkBranch.Run(); err != nil {
			mergeRef = "origin/" + f.Branch
		}

		// Merge with -X theirs to auto-resolve .sdlc/ state conflicts.
		// Both main and feature branch modify .sdlc/state.yaml; the feature
		// branch version is more current and post-merge updates will overwrite.
		mergeArgs := []string{"merge", "-X", "theirs"}
		if strategy == "squash" {
			mergeArgs = append(mergeArgs, "--squash")
		}
		mergeArgs = append(mergeArgs, mergeRef)

		gitMerge := exec.Command("git", mergeArgs...)
		gitMerge.Dir = root
		if out, err := gitMerge.CombinedOutput(); err != nil {
			rollbackAfterMerge()
			return fmt.Errorf("git merge %s: %s: %w", f.Branch, string(out), err)
		}

		// For squash merges, create the commit
		if strategy == "squash" {
			commitMsg := fmt.Sprintf("feat: %s\n\nMerged feature %s via SDLC orchestration.", f.Title, slug)
			commitCmd := exec.Command("git", "commit", "-m", commitMsg)
			commitCmd.Dir = root
			if out, err := commitCmd.CombinedOutput(); err != nil {
				rollbackAfterMerge()
				return fmt.Errorf("git commit: %s: %w", string(out), err)
			}
		}

		// Update branch manifest - rollback git on failure
		now := time.Now().UTC()
		manifest.MergedAt = &now
		manifest.MergeStrategy = strategy
		if err := sdlc.SaveBranch(root, manifest); err != nil {
			rollbackAfterMerge()
			return fmt.Errorf("save branch manifest (git rolled back): %w", err)
		}

		// Transition feature to released - rollback git on failure
		if err := f.Transition(sdlc.PhaseReleased); err != nil {
			rollbackAfterMerge()
			return fmt.Errorf("transition feature (git rolled back): %w", err)
		}
		if err := f.Save(root); err != nil {
			rollbackAfterMerge()
			return fmt.Errorf("save feature (git rolled back): %w", err)
		}

		// Record action
		state, err := sdlc.LoadState(root)
		if err != nil {
			return err
		}
		state.RecordAction("MERGE_FEATURE", slug, "cli", "success")
		if err := state.Save(root); err != nil {
			return err
		}

		if jsonOutput {
			return printJSON(map[string]string{
				"feature":  slug,
				"branch":   f.Branch,
				"strategy": strategy,
				"status":   "merged",
			})
		}

		fmt.Printf("Merged: %s -> %s (strategy: %s)\n", f.Branch, manifest.BaseBranch, strategy)
		fmt.Printf("Feature %s transitioned to released.\n", slug)
		return nil
	},
}

func init() {
	mergeCmd.Flags().StringVar(&mergeStrategy, "strategy", "squash", "merge strategy: squash or merge")
	rootCmd.AddCommand(mergeCmd)
}
