package main

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"github.com/spf13/cobra"
)

// isFreshCloneError checks if the error is due to a fresh clone scenario
// where the database exists but is missing required config (like issue_prefix).
// This happens when someone clones a repo with beads but needs to initialize.
func isFreshCloneError(err error) bool {
	if err == nil {
		return false
	}
	errStr := err.Error()
	// Check for the specific migration invariant error pattern
	return strings.Contains(errStr, "post-migration validation failed") &&
		strings.Contains(errStr, "required config key missing: issue_prefix")
}

// handleFreshCloneError displays a helpful message when a fresh clone is detected
// and returns true if the error was handled (so caller should exit).
// If not a fresh clone error, returns false and does nothing.
func handleFreshCloneError(err error, beadsDir string) bool {
	if !isFreshCloneError(err) {
		return false
	}

	// Look for JSONL file in the .beads directory
	jsonlPath := ""
	issueCount := 0

	if beadsDir != "" {
		// Check for issues.jsonl (canonical) first, then beads.jsonl (legacy)
		for _, name := range []string{"issues.jsonl", "beads.jsonl"} {
			candidate := filepath.Join(beadsDir, name)
			if info, statErr := os.Stat(candidate); statErr == nil && !info.IsDir() {
				jsonlPath = candidate
				// Count lines (approximately = issue count)
				// #nosec G304 -- candidate is constructed from beadsDir which is .beads/
				if data, readErr := os.ReadFile(candidate); readErr == nil {
					for _, line := range strings.Split(string(data), "\n") {
						if strings.TrimSpace(line) != "" {
							issueCount++
						}
					}
				}
				break
			}
		}
	}

	fmt.Fprintf(os.Stderr, "Error: Database not initialized\n\n")
	fmt.Fprintf(os.Stderr, "This appears to be a fresh clone or the database needs initialization.\n")

	if jsonlPath != "" && issueCount > 0 {
		fmt.Fprintf(os.Stderr, "Found: %s (%d issues)\n\n", jsonlPath, issueCount)
		fmt.Fprintf(os.Stderr, "To initialize from the JSONL file, run:\n")
		fmt.Fprintf(os.Stderr, "  bd import -i %s\n\n", jsonlPath)
	} else {
		fmt.Fprintf(os.Stderr, "\nTo initialize a new database, run:\n")
		fmt.Fprintf(os.Stderr, "  bd init --prefix <your-prefix>\n\n")
	}

	fmt.Fprintf(os.Stderr, "For more information: bd init --help\n")
	return true
}

// isWispOperation returns true if the command operates on ephemeral wisps.
// Wisp operations auto-bypass the daemon because wisps are local-only
// (Ephemeral=true issues are never exported to JSONL).
// Detects:
//   - mol wisp subcommands (create, list, gc, or direct proto invocation)
//   - mol burn (only operates on wisps)
//   - mol squash (condenses wisps to digests)
//   - Commands with ephemeral issue IDs in args (bd-*-wisp-*, wisp-*, or legacy eph-*)
func isWispOperation(cmd *cobra.Command, args []string) bool {
	cmdName := cmd.Name()

	// Check command hierarchy for wisp subcommands
	// bd mol wisp → parent is "mol", cmd is "wisp"
	// bd mol wisp create → parent is "wisp", cmd is "create"
	if cmd.Parent() != nil {
		parentName := cmd.Parent().Name()
		// Direct wisp command or subcommands under wisp
		if parentName == "wisp" || cmdName == "wisp" {
			return true
		}
		// mol burn and mol squash are wisp-only operations
		if parentName == "mol" && (cmdName == "burn" || cmdName == "squash") {
			return true
		}
	}

	// Check for ephemeral issue IDs in arguments
	// Ephemeral IDs have "wisp" segment: bd-wisp-xxx, gt-wisp-xxx, wisp-xxx
	// Also detect legacy "eph" prefix for backwards compatibility
	for _, arg := range args {
		// Skip flags
		if strings.HasPrefix(arg, "-") {
			continue
		}
		// Check for ephemeral prefix patterns (wisp-* or legacy eph-*)
		if strings.Contains(arg, "-wisp-") || strings.HasPrefix(arg, "wisp-") ||
			strings.Contains(arg, "-eph-") || strings.HasPrefix(arg, "eph-") {
			return true
		}
	}

	return false
}
