package registry

import (
	"fmt"
	"regexp"
	"sort"
	"strings"
)

// ImageEntry represents a stock image with parsed metadata
type ImageEntry struct {
	Filename   string   `json:"filename"`
	Type       string   `json:"type"`       // "background" or "gallery"
	Subject    string   `json:"subject"`    // descriptive name e.g. "sand-dunes"
	Category   string   `json:"category"`   // primary category e.g. "texture" (bg) or "food-bakery" (gallery)
	Categories []string `json:"categories"` // split categories e.g. ["food", "bakery"]
	Mood       string   `json:"mood"`       // backgrounds only: warm, cool, earthy, moody, soft, bold, etc.
	Tone       string   `json:"tone"`       // light, dark, warm, cool, neutral
	Contrast   string   `json:"contrast"`   // dark-text or light-text
}

// ParseBackgroundFilename parses bg_{subject}_{type}_{mood}_{tone}_{contrast}.jpeg
func ParseBackgroundFilename(filename string) ImageEntry {
	name := strings.TrimSuffix(filename, ".jpeg")
	parts := strings.Split(name, "_")

	entry := ImageEntry{
		Filename: filename,
		Type:     "background",
	}

	// Expected: bg, subject, type, mood, tone, contrast
	if len(parts) >= 6 {
		entry.Subject = parts[1]
		entry.Category = parts[2]
		entry.Categories = []string{parts[2]}
		entry.Mood = parts[3]
		entry.Tone = parts[4]
		entry.Contrast = parts[5]
	} else if len(parts) >= 2 {
		entry.Subject = parts[1]
	}

	return entry
}

// ParseGalleryFilename parses gal_{subject}_{categories}_{tone}_{contrast}.jpeg
func ParseGalleryFilename(filename string) ImageEntry {
	name := strings.TrimSuffix(filename, ".jpeg")
	parts := strings.Split(name, "_")

	entry := ImageEntry{
		Filename: filename,
		Type:     "gallery",
	}

	// Expected: gal, subject, categories, tone, contrast
	if len(parts) >= 5 {
		entry.Subject = parts[1]
		entry.Category = parts[2]
		entry.Categories = strings.Split(parts[2], "-")
		entry.Tone = parts[3]
		entry.Contrast = parts[4]
	} else if len(parts) >= 2 {
		entry.Subject = parts[1]
	}

	return entry
}

// ImageRegistry holds the stock image catalog.
// It can be populated from either hardcoded data (fallback) or the Laravel API.
type ImageRegistry struct {
	backgrounds []ImageEntry
	gallery     []ImageEntry
	byFilename  map[string]*ImageEntry
}

// NewImageRegistry creates an empty registry
func NewImageRegistry() *ImageRegistry {
	return &ImageRegistry{
		byFilename: make(map[string]*ImageEntry),
	}
}

// LoadFromEntries populates the registry from a slice of ImageEntry
func (r *ImageRegistry) LoadFromEntries(entries []ImageEntry) {
	r.backgrounds = nil
	r.gallery = nil
	r.byFilename = make(map[string]*ImageEntry)

	for i := range entries {
		entry := entries[i]
		if entry.Type == "background" {
			r.backgrounds = append(r.backgrounds, entry)
		} else {
			r.gallery = append(r.gallery, entry)
		}
	}

	// Build lookup map after slices are stable
	for i := range r.backgrounds {
		r.byFilename[r.backgrounds[i].Filename] = &r.backgrounds[i]
	}
	for i := range r.gallery {
		r.byFilename[r.gallery[i].Filename] = &r.gallery[i]
	}
}

// SearchImages finds images matching the given filters
func (r *ImageRegistry) SearchImages(imageType, category, mood, tone string, limit int) []ImageEntry {
	if limit <= 0 {
		limit = 10
	}

	var results []ImageEntry
	var pool []ImageEntry

	switch imageType {
	case "background":
		pool = r.backgrounds
	case "gallery":
		pool = r.gallery
	default:
		pool = append(append([]ImageEntry{}, r.backgrounds...), r.gallery...)
	}

	for _, img := range pool {
		if category != "" && !matchesCategory(img, category) {
			continue
		}
		if mood != "" && img.Mood != mood {
			continue
		}
		if tone != "" && img.Tone != tone {
			continue
		}
		results = append(results, img)
		if len(results) >= limit {
			break
		}
	}

	return results
}

// FindImageByFilename looks up an image by exact filename
func (r *ImageRegistry) FindImageByFilename(filename string) *ImageEntry {
	if img, ok := r.byFilename[filename]; ok {
		return img
	}
	return nil
}

// FormatImageList formats a slice of images for display to the AI
func (r *ImageRegistry) FormatImageList(images []ImageEntry, laravelURL string) string {
	if len(images) == 0 {
		return "No matching images found."
	}

	var sb strings.Builder
	fmt.Fprintf(&sb, "Found %d images:\n\n", len(images))

	for i, img := range images {
		fmt.Fprintf(&sb, "%d. **%s** (%s)\n", i+1, img.Subject, img.Type)
		fmt.Fprintf(&sb, "   Category: %s | Tone: %s | Contrast: %s",
			img.Category, img.Tone, img.Contrast)
		if img.Mood != "" {
			fmt.Fprintf(&sb, " | Mood: %s", img.Mood)
		}
		sb.WriteString("\n")
		fmt.Fprintf(&sb, "   URL: %s\n\n", FormatImageURL(img, laravelURL))
	}

	sb.WriteString("IMPORTANT: Use the exact URLs above. Do not modify or reconstruct filenames.\n")

	return sb.String()
}

// FormatImageUsage returns usage guidelines for a specific image
func (r *ImageRegistry) FormatImageUsage(img ImageEntry, laravelURL string) string {
	url := FormatImageURL(img, laravelURL)
	var sb strings.Builder

	fmt.Fprintf(&sb, "## %s (%s)\n\n", img.Subject, img.Type)
	fmt.Fprintf(&sb, "URL: `%s`\n\n", url)

	if img.Type == "background" {
		sb.WriteString("### Recommended Usage\n")
		sb.WriteString("Use as a **section background** with gradient overlay for text readability:\n\n")
		sb.WriteString("```tsx\n")
		fmt.Fprintf(&sb, `<section className="relative min-h-[60vh] bg-cover bg-center" style={{ backgroundImage: 'url(%s)' }}>`, url)
		sb.WriteString("\n")
		sb.WriteString(`  <div className="absolute inset-0 bg-black/40" />`)
		sb.WriteString("\n")
		sb.WriteString(`  <div className="relative z-10 container mx-auto px-6 py-20">`)
		sb.WriteString("\n")
		fmt.Fprintf(&sb, `    <h1 className="%s">Your Heading</h1>`, textColorForContrast(img.Contrast))
		sb.WriteString("\n")
		sb.WriteString("  </div>\n</section>\n```\n\n")
	} else {
		sb.WriteString("### Recommended Usage\n")
		sb.WriteString("Use as a **card image** or **section image**:\n\n")
		sb.WriteString("```tsx\n")
		fmt.Fprintf(&sb, `<img src="%s" alt="%s" className="w-full h-64 object-cover rounded-lg" />`, url, humanizeSubject(img.Subject))
		sb.WriteString("\n```\n\n")
	}

	sb.WriteString("### Metadata\n")
	fmt.Fprintf(&sb, "- **Alt text suggestion**: \"%s\"\n", humanizeSubject(img.Subject))
	fmt.Fprintf(&sb, "- **Tone**: %s (pair with %s backgrounds)\n", img.Tone, img.Tone)
	fmt.Fprintf(&sb, "- **Text overlay**: Use %s text color\n", textColorForContrast(img.Contrast))
	if img.Mood != "" {
		fmt.Fprintf(&sb, "- **Mood**: %s\n", img.Mood)
	}

	return sb.String()
}

// FixImageURLs scans content for stock image filenames and replaces any
// hallucinated filenames with the correct ones from the registry.
func (r *ImageRegistry) FixImageURLs(content string) (string, int) {
	fixes := 0
	result := imageURLPattern.ReplaceAllStringFunc(content, func(match string) string {
		if r.FindImageByFilename(match) != nil {
			return match
		}
		if best := r.findBestMatch(match); best != nil {
			fixes++
			return best.Filename
		}
		return match
	})
	return result, fixes
}

func (r *ImageRegistry) findBestMatch(filename string) *ImageEntry {
	name := strings.TrimSuffix(strings.TrimSuffix(filename, ".jpeg"), ".png")
	parts := strings.Split(name, "_")
	if len(parts) < 3 {
		return nil
	}

	imgType := parts[0]
	subject := parts[1]

	var pool []ImageEntry
	if imgType == "bg" {
		pool = r.backgrounds
	} else {
		pool = r.gallery
	}

	for i := range pool {
		if pool[i].Subject == subject {
			return &pool[i]
		}
	}
	return nil
}

// UniqueCategories returns sorted unique category values for backgrounds and gallery images.
func (r *ImageRegistry) UniqueCategories() (bgCategories, galCategories []string) {
	bgSet := make(map[string]bool)
	galSet := make(map[string]bool)

	for _, img := range r.backgrounds {
		if img.Category != "" {
			bgSet[img.Category] = true
		}
	}
	for _, img := range r.gallery {
		if img.Category != "" {
			galSet[img.Category] = true
		}
	}

	for k := range bgSet {
		bgCategories = append(bgCategories, k)
	}
	for k := range galSet {
		galCategories = append(galCategories, k)
	}

	// Sort for deterministic output
	sort.Strings(bgCategories)
	sort.Strings(galCategories)
	return
}

// --- Static helpers (not registry-dependent) ---

func matchesCategory(img ImageEntry, category string) bool {
	if img.Category == category {
		return true
	}
	for _, cat := range img.Categories {
		if cat == category {
			return true
		}
	}
	return false
}

// FormatImageURL constructs the full URL for an image served from Laravel storage
func FormatImageURL(img ImageEntry, laravelURL string) string {
	subdir := "backgrounds"
	if img.Type == "gallery" {
		subdir = "gallery"
	}
	return fmt.Sprintf("%s/storage/image-library/%s/%s", strings.TrimRight(laravelURL, "/"), subdir, img.Filename)
}

func textColorForContrast(contrast string) string {
	if contrast == "light-text" {
		return "text-white"
	}
	return "text-gray-900"
}

func humanizeSubject(subject string) string {
	return strings.ReplaceAll(subject, "-", " ")
}

var imageURLPattern = regexp.MustCompile(`((?:bg|gal)_[a-z0-9-]+(?:_[a-z0-9-]+)*\.(?:jpeg|png))`)

// --- DefaultRegistry: fallback using hardcoded data ---

var DefaultRegistry *ImageRegistry

// Legacy package-level vars for backward compatibility
var AllBackgroundImages []ImageEntry
var AllGalleryImages []ImageEntry

func init() {
	DefaultRegistry = NewImageRegistry()
	var entries []ImageEntry
	for _, fn := range backgroundFilenames {
		entries = append(entries, ParseBackgroundFilename(fn))
	}
	for _, fn := range galleryFilenames {
		entries = append(entries, ParseGalleryFilename(fn))
	}
	DefaultRegistry.LoadFromEntries(entries)

	AllBackgroundImages = DefaultRegistry.backgrounds
	AllGalleryImages = DefaultRegistry.gallery
}

// --- Package-level wrappers delegating to DefaultRegistry ---

func SearchImages(imageType, category, mood, tone string, limit int) []ImageEntry {
	return DefaultRegistry.SearchImages(imageType, category, mood, tone, limit)
}

func FindImageByFilename(filename string) *ImageEntry {
	return DefaultRegistry.FindImageByFilename(filename)
}

func FormatImageList(images []ImageEntry, laravelURL string) string {
	return DefaultRegistry.FormatImageList(images, laravelURL)
}

func FormatImageUsage(img ImageEntry, laravelURL string) string {
	return DefaultRegistry.FormatImageUsage(img, laravelURL)
}

func FixImageURLs(content string) (string, int) {
	return DefaultRegistry.FixImageURLs(content)
}
