// Package main implements the bd CLI dependency repair command.
package main

import (
	"fmt"
	"os"

	"github.com/fatih/color"
	"github.com/spf13/cobra"
	"github.com/steveyegge/beads/internal/storage/sqlite"
	"github.com/steveyegge/beads/internal/types"
)

var repairDepsCmd = &cobra.Command{
	Use:   "repair-deps",
	Short: "Find and fix orphaned dependency references",
	Long: `Scans all issues for dependencies pointing to non-existent issues.

Reports orphaned dependencies and optionally removes them with --fix.
Interactive mode with --interactive prompts for each orphan.`,
	Run: func(cmd *cobra.Command, args []string) {
		CheckReadonly("repair-deps")
		fix, _ := cmd.Flags().GetBool("fix")
		interactive, _ := cmd.Flags().GetBool("interactive")

		// If daemon is running but doesn't support this command, use direct storage
		if daemonClient != nil && store == nil {
			var err error
			store, err = sqlite.New(rootCtx, dbPath)
			if err != nil {
				fmt.Fprintf(os.Stderr, "Error: failed to open database: %v\n", err)
				os.Exit(1)
			}
			defer func() { _ = store.Close() }()
		}

		ctx := rootCtx

		// Get all dependency records
		allDeps, err := store.GetAllDependencyRecords(ctx)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error: failed to get dependencies: %v\n", err)
			os.Exit(1)
		}

		// Get all issues to check existence
		issues, err := store.SearchIssues(ctx, "", types.IssueFilter{})
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error: failed to list issues: %v\n", err)
			os.Exit(1)
		}

		// Build set of valid issue IDs
		validIDs := make(map[string]bool)
		for _, issue := range issues {
			validIDs[issue.ID] = true
		}

		// Find orphaned dependencies
		type orphan struct {
			issueID     string
			dependsOnID string
			depType     types.DependencyType
		}
		var orphans []orphan

		for issueID, deps := range allDeps {
			if !validIDs[issueID] {
				// The issue itself doesn't exist, skip (will be cleaned up separately)
				continue
			}
			for _, dep := range deps {
				if !validIDs[dep.DependsOnID] {
					orphans = append(orphans, orphan{
						issueID:     dep.IssueID,
						dependsOnID: dep.DependsOnID,
						depType:     dep.Type,
					})
				}
			}
		}

		if jsonOutput {
			result := map[string]interface{}{
				"orphans_found": len(orphans),
				"orphans":       []map[string]string{},
			}
			if len(orphans) > 0 {
				orphanList := make([]map[string]string, len(orphans))
				for i, o := range orphans {
					orphanList[i] = map[string]string{
						"issue_id":      o.issueID,
						"depends_on_id": o.dependsOnID,
						"type":          string(o.depType),
					}
				}
				result["orphans"] = orphanList
			}
			if fix || interactive {
				result["fixed"] = len(orphans)
			}
			outputJSON(result)
			return
		}

		// Report results
		if len(orphans) == 0 {
			green := color.New(color.FgGreen).SprintFunc()
			fmt.Printf("\n%s No orphaned dependencies found\n\n", green("✓"))
			return
		}

		yellow := color.New(color.FgYellow).SprintFunc()
		fmt.Printf("\n%s Found %d orphaned dependencies:\n\n", yellow("⚠"), len(orphans))

		for i, o := range orphans {
			fmt.Printf("%d. %s → %s (%s) [%s does not exist]\n",
				i+1, o.issueID, o.dependsOnID, o.depType, o.dependsOnID)
		}
		fmt.Println()

		// Fix if requested
		if interactive {
			fixed := 0
			for _, o := range orphans {
				fmt.Printf("Remove dependency %s → %s (%s)? [y/N]: ", o.issueID, o.dependsOnID, o.depType)
				var response string
				_, _ = fmt.Scanln(&response)
				if response == "y" || response == "Y" {
					// Use direct SQL to remove orphaned dependencies
					// RemoveDependency tries to mark the depends_on issue as dirty, which fails for orphans
					db := store.UnderlyingDB()
					_, err := db.ExecContext(ctx, "DELETE FROM dependencies WHERE issue_id = ? AND depends_on_id = ?",
						o.issueID, o.dependsOnID)
					if err != nil {
						fmt.Fprintf(os.Stderr, "Error removing dependency: %v\n", err)
					} else {
						// Mark the issue as dirty
						_, _ = db.ExecContext(ctx, "INSERT OR IGNORE INTO dirty_issues (issue_id) VALUES (?)", o.issueID)
						fixed++
					}
				}
			}
			markDirtyAndScheduleFlush()
			green := color.New(color.FgGreen).SprintFunc()
			fmt.Printf("\n%s Fixed %d orphaned dependencies\n\n", green("✓"), fixed)
		} else if fix {
			db := store.UnderlyingDB()
			for _, o := range orphans {
				// Use direct SQL to remove orphaned dependencies
				_, err := db.ExecContext(ctx, "DELETE FROM dependencies WHERE issue_id = ? AND depends_on_id = ?",
					o.issueID, o.dependsOnID)
				if err != nil {
					fmt.Fprintf(os.Stderr, "Error removing dependency %s → %s: %v\n",
						o.issueID, o.dependsOnID, err)
				} else {
					// Mark the issue as dirty
					_, _ = db.ExecContext(ctx, "INSERT OR IGNORE INTO dirty_issues (issue_id) VALUES (?)", o.issueID)
				}
			}
			markDirtyAndScheduleFlush()
			green := color.New(color.FgGreen).SprintFunc()
			fmt.Printf("%s Fixed %d orphaned dependencies\n\n", green("✓"), len(orphans))
		} else {
			fmt.Printf("Run with --fix to automatically remove orphaned dependencies\n")
			fmt.Printf("Run with --interactive to review each dependency\n\n")
		}
	},
}

func init() {
	repairDepsCmd.Flags().Bool("fix", false, "Automatically remove orphaned dependencies")
	repairDepsCmd.Flags().Bool("interactive", false, "Interactively review each orphaned dependency")
	repairDepsCmd.Flags().BoolVar(&jsonOutput, "json", false, "Output repair results in JSON format")
	rootCmd.AddCommand(repairDepsCmd)
}
