package main

import (
	"encoding/json"
	"fmt"
	"os"

	"github.com/spf13/cobra"
	"github.com/steveyegge/beads/internal/rpc"
	"github.com/steveyegge/beads/internal/types"
	"github.com/steveyegge/beads/internal/validation"
)

// LintResult holds the validation result for a single issue.
type LintResult struct {
	ID       string   `json:"id"`
	Title    string   `json:"title"`
	Type     string   `json:"type"`
	Missing  []string `json:"missing,omitempty"`
	Warnings int      `json:"warnings"`
}

var lintCmd = &cobra.Command{
	Use:     "lint [issue-id...]",
	GroupID: "views",
	Short:   "Check issues for missing template sections",
	Long: `Check issues for missing recommended sections based on issue type.

By default, lints all open issues. Specify issue IDs to lint specific issues.

Section requirements by type:
  bug:      Steps to Reproduce, Acceptance Criteria
  task:     Acceptance Criteria
  feature:  Acceptance Criteria
  epic:     Success Criteria
  chore:    (none)

Examples:
  bd lint                    # Lint all open issues
  bd lint bd-abc             # Lint specific issue
  bd lint bd-abc bd-def      # Lint multiple issues
  bd lint --type bug         # Lint only bugs
  bd lint --status all       # Lint all issues (including closed)
`,
	Run: func(cmd *cobra.Command, args []string) {
		ctx := rootCtx

		typeFilter, _ := cmd.Flags().GetString("type")
		statusFilter, _ := cmd.Flags().GetString("status")

		var issues []*types.Issue

		// Use daemon if available, otherwise direct mode
		if daemonClient != nil {
			if len(args) > 0 {
				// Get specific issues via show
				for _, id := range args {
					showArgs := &rpc.ShowArgs{ID: id}
					resp, err := daemonClient.Show(showArgs)
					if err != nil {
						fmt.Fprintf(os.Stderr, "Error getting %s: %v\n", id, err)
						continue
					}
					var details types.IssueDetails
					if err := json.Unmarshal(resp.Data, &details); err != nil {
						fmt.Fprintf(os.Stderr, "Error parsing %s: %v\n", id, err)
						continue
					}
					issues = append(issues, &details.Issue)
				}
			} else {
				// List issues via daemon
				listArgs := &rpc.ListArgs{
					Limit: 1000, // reasonable limit
				}

				// Default to open issues unless --status specified
				if statusFilter == "" || statusFilter == "open" {
					listArgs.Status = "open"
				} else if statusFilter != "all" {
					listArgs.Status = statusFilter
				}

				if typeFilter != "" {
					listArgs.IssueType = typeFilter
				}

				resp, err := daemonClient.List(listArgs)
				if err != nil {
					fmt.Fprintf(os.Stderr, "Error: %v\n", err)
					os.Exit(1)
				}

				var issuesWithCounts []*types.IssueWithCounts
				if err := json.Unmarshal(resp.Data, &issuesWithCounts); err != nil {
					fmt.Fprintf(os.Stderr, "Error parsing response: %v\n", err)
					os.Exit(1)
				}

				for _, iwc := range issuesWithCounts {
					issues = append(issues, iwc.Issue)
				}
			}
		} else {
			// Direct mode
			if err := ensureDatabaseFresh(ctx); err != nil {
				fmt.Fprintf(os.Stderr, "Error: %v\n", err)
				os.Exit(1)
			}

			if store == nil {
				fmt.Fprintln(os.Stderr, "Error: database not initialized")
				os.Exit(1)
			}

			if len(args) > 0 {
				// Lint specific issues
				for _, id := range args {
					issue, err := store.GetIssue(ctx, id)
					if err != nil {
						fmt.Fprintf(os.Stderr, "Error getting %s: %v\n", id, err)
						continue
					}
					if issue == nil {
						fmt.Fprintf(os.Stderr, "Issue not found: %s\n", id)
						continue
					}
					issues = append(issues, issue)
				}
			} else {
				// Lint all matching issues
				filter := types.IssueFilter{}

				// Default to open issues unless --status specified
				if statusFilter == "" || statusFilter == "open" {
					s := types.StatusOpen
					filter.Status = &s
				} else if statusFilter != "all" {
					s := types.Status(statusFilter)
					filter.Status = &s
				}

				if typeFilter != "" {
					t := types.IssueType(typeFilter)
					filter.IssueType = &t
				}

				var err error
				issues, err = store.SearchIssues(ctx, "", filter)
				if err != nil {
					fmt.Fprintf(os.Stderr, "Error: %v\n", err)
					os.Exit(1)
				}
			}
		}

		var results []LintResult
		totalWarnings := 0

		for _, issue := range issues {
			err := validation.LintIssue(issue)
			if err == nil {
				continue // No warnings for this issue
			}

			templateErr, ok := err.(*validation.TemplateError)
			if !ok {
				continue
			}

			missing := make([]string, len(templateErr.Missing))
			for i, m := range templateErr.Missing {
				missing[i] = m.Heading
			}

			result := LintResult{
				ID:       issue.ID,
				Title:    issue.Title,
				Type:     string(issue.IssueType),
				Missing:  missing,
				Warnings: len(missing),
			}
			results = append(results, result)
			totalWarnings += len(missing)
		}

		if jsonOutput {
			output := struct {
				Total   int          `json:"total"`
				Issues  int          `json:"issues"`
				Results []LintResult `json:"results"`
			}{
				Total:   totalWarnings,
				Issues:  len(results),
				Results: results,
			}
			data, _ := json.MarshalIndent(output, "", "  ")
			fmt.Println(string(data))
			return
		}

		// Human-readable output
		if len(results) == 0 {
			fmt.Printf("✓ No template warnings found (%d issues checked)\n", len(issues))
			return
		}

		fmt.Printf("Template warnings (%d issues, %d warnings):\n\n", len(results), totalWarnings)
		for _, r := range results {
			fmt.Printf("%s [%s]: %s\n", r.ID, r.Type, r.Title)
			for _, m := range r.Missing {
				fmt.Printf("  ⚠ Missing: %s\n", m)
			}
			fmt.Println()
		}

		// Exit with error code if warnings found (useful for CI)
		os.Exit(1)
	},
}

func init() {
	lintCmd.Flags().StringP("type", "t", "", "Filter by issue type (bug, task, feature, epic)")
	lintCmd.Flags().StringP("status", "s", "", "Filter by status (default: open, use 'all' for all)")

	rootCmd.AddCommand(lintCmd)
}
