// Package utils provides utility functions for issue ID parsing and resolution.
package utils

import (
	"context"
	"fmt"
	"strings"

	"github.com/steveyegge/beads/internal/storage"
	"github.com/steveyegge/beads/internal/types"
)

// ParseIssueID ensures an issue ID has the configured prefix.
// If the input already has the prefix (e.g., "bd-a3f8e9"), returns it as-is.
// If the input lacks the prefix (e.g., "a3f8e9"), adds the configured prefix.
// Works with hierarchical IDs too: "a3f8e9.1.2" → "bd-a3f8e9.1.2"
func ParseIssueID(input string, prefix string) string {
	if prefix == "" {
		prefix = "bd-"
	}
	
	if strings.HasPrefix(input, prefix) {
		return input
	}
	
	return prefix + input
}

// ResolvePartialID resolves a potentially partial issue ID to a full ID.
// Supports:
// - Full IDs: "bd-a3f8e9" or "a3f8e9" → "bd-a3f8e9"
// - Without hyphen: "bda3f8e9" or "wya3f8e9" → "bd-a3f8e9"
// - Partial IDs: "a3f8" → "bd-a3f8e9" (if unique match)
// - Hierarchical: "a3f8e9.1" → "bd-a3f8e9.1"
//
// Returns an error if:
// - No issue found matching the ID
// - Multiple issues match (ambiguous prefix)
func ResolvePartialID(ctx context.Context, store storage.Storage, input string) (string, error) {
	// Fast path: if the user typed an exact ID that exists, return it as-is.
	// This preserves behavior where issue IDs may not match the configured
	// issue_prefix (e.g. cross-repo IDs like "ao-izl"), while still allowing
	// prefix-based and hash-based resolution for other inputs.
	if issue, err := store.GetIssue(ctx, input); err == nil && issue != nil {
		return input, nil
	}
	
	// Get the configured prefix
	prefix, err := store.GetConfig(ctx, "issue_prefix")
	if err != nil || prefix == "" {
		prefix = "bd"
	}
	
	// Ensure prefix has hyphen for ID format
	prefixWithHyphen := prefix
	if !strings.HasSuffix(prefix, "-") {
		prefixWithHyphen = prefix + "-"
	}
	
	// Normalize input:
	// 1. If it has the full prefix with hyphen (bd-a3f8e9), use as-is
	// 2. Otherwise, add prefix with hyphen (handles both bare hashes and prefix-without-hyphen cases)
	
	var normalizedID string
	
	if strings.HasPrefix(input, prefixWithHyphen) {
		// Already has prefix with hyphen: "bd-a3f8e9"
		normalizedID = input
	} else {
		// Bare hash or prefix without hyphen: "a3f8e9", "07b8c8", "bda3f8e9" → all get prefix with hyphen added
		normalizedID = prefixWithHyphen + input
	}
	
	// First try exact match on normalized ID
	issue, err := store.GetIssue(ctx, normalizedID)
	if err == nil && issue != nil {
		return normalizedID, nil
	}
	
	// If exact match failed, try substring search
	filter := types.IssueFilter{}
	
	issues, err := store.SearchIssues(ctx, "", filter)
	if err != nil {
		return "", fmt.Errorf("failed to search issues: %w", err)
	}
	
	// Extract the hash part for substring matching
	hashPart := strings.TrimPrefix(normalizedID, prefixWithHyphen)

	var matches []string
	var exactMatch string
	
	for _, issue := range issues {
		// Check for exact full ID match first (case: user typed full ID with different prefix)
		if issue.ID == input {
			exactMatch = issue.ID
			break
		}
		
		// Extract hash from each issue, regardless of its prefix
		// This handles cross-prefix matching (e.g., "3d0" matching "offlinebrew-3d0")
		var issueHash string
		if idx := strings.Index(issue.ID, "-"); idx >= 0 {
			issueHash = issue.ID[idx+1:]
		} else {
			issueHash = issue.ID
		}
		
		// Check for exact hash match (excluding hierarchical children)
		if issueHash == hashPart {
			exactMatch = issue.ID
			// Don't break - keep searching in case there's a full ID match
		}
		
		// Check if the issue hash contains the input hash as substring
		if strings.Contains(issueHash, hashPart) {
			matches = append(matches, issue.ID)
		}
	}
	
	// Prefer exact match over substring matches
	if exactMatch != "" {
		return exactMatch, nil
	}
	
	if len(matches) == 0 {
		return "", fmt.Errorf("no issue found matching %q", input)
	}
	
	if len(matches) > 1 {
		return "", fmt.Errorf("ambiguous ID %q matches %d issues: %v\nUse more characters to disambiguate", input, len(matches), matches)
	}
	
	return matches[0], nil
}

// ResolvePartialIDs resolves multiple potentially partial issue IDs.
// Returns the resolved IDs and any errors encountered.
func ResolvePartialIDs(ctx context.Context, store storage.Storage, inputs []string) ([]string, error) {
	var resolved []string
	for _, input := range inputs {
		fullID, err := ResolvePartialID(ctx, store, input)
		if err != nil {
			return nil, err
		}
		resolved = append(resolved, fullID)
	}
	return resolved, nil
}
