package main

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

	"github.com/denisovdennis/autohero/internal/model"
	"github.com/denisovdennis/autohero/internal/storage"
)

func loadGearCatalog(ctx context.Context, cs *storage.ContentStore, base string) ([]model.GearFamily, error) {
	switch strings.ToLower(strings.TrimSpace(base)) {
	case "", "db":
		return cs.LoadGearFamilies(ctx)
	case "code":
		return model.DefaultGearFamilies(), nil
	default:
		return nil, fmt.Errorf("gear-base must be db or code, got %q", base)
	}
}

// gearFamilyPatchJSON holds optional overrides merged onto catalog families after DB/code load.
type gearFamilyPatchJSON struct {
	BasePrimary   *int     `json:"basePrimary,omitempty"`
	SpeedModifier *float64 `json:"speedModifier,omitempty"`
	BaseCrit      *float64 `json:"baseCrit,omitempty"`
	AgilityBonus  *int     `json:"agilityBonus,omitempty"`
	StatType      *string  `json:"statType,omitempty"`
}

// listOverlayKeys returns top-level JSON object keys (patch targets).
func listOverlayKeys(path string) ([]string, error) {
	b, err := os.ReadFile(path)
	if err != nil {
		return nil, err
	}
	var raw map[string]json.RawMessage
	if err := json.Unmarshal(b, &raw); err != nil {
		return nil, fmt.Errorf("gear overlay JSON: %w", err)
	}
	keys := make([]string, 0, len(raw))
	for k := range raw {
		keys = append(keys, strings.TrimSpace(k))
	}
	return keys, nil
}

// applyGearOverlayJSON clones families, applies patches keyed by "name" or "slot:name" (e.g. "main_hand:Soul Reaver").
func applyGearOverlayJSON(path string, families []model.GearFamily) ([]model.GearFamily, error) {
	b, err := os.ReadFile(path)
	if err != nil {
		return nil, err
	}
	var raw map[string]json.RawMessage
	if err := json.Unmarshal(b, &raw); err != nil {
		return nil, fmt.Errorf("gear overlay JSON: %w", err)
	}
	patches := make(map[string]gearFamilyPatchJSON, len(raw))
	for k, v := range raw {
		var p gearFamilyPatchJSON
		if err := json.Unmarshal(v, &p); err != nil {
			return nil, fmt.Errorf("gear overlay key %q: %w", k, err)
		}
		patches[strings.TrimSpace(k)] = p
	}
	out := cloneGearFamilies(families)
	for i := range out {
		key := overlayKey(out[i])
		if p, ok := patches[key]; ok {
			mergePatch(&out[i], p)
			continue
		}
		if p, ok := patches[out[i].Name]; ok {
			mergePatch(&out[i], p)
		}
	}
	return out, nil
}

func overlayKey(g model.GearFamily) string {
	return fmt.Sprintf("%s:%s", g.Slot, g.Name)
}

func cloneGearFamilies(f []model.GearFamily) []model.GearFamily {
	out := make([]model.GearFamily, len(f))
	copy(out, f)
	return out
}

func mergePatch(g *model.GearFamily, p gearFamilyPatchJSON) {
	if p.BasePrimary != nil {
		g.BasePrimary = *p.BasePrimary
	}
	if p.SpeedModifier != nil {
		g.SpeedModifier = *p.SpeedModifier
	}
	if p.BaseCrit != nil {
		g.BaseCrit = *p.BaseCrit
	}
	if p.AgilityBonus != nil {
		g.AgilityBonus = *p.AgilityBonus
	}
	if p.StatType != nil {
		g.StatType = *p.StatType
	}
}

// printGearOverlayMigrationSQL prints UPDATEs for gear (and equipment_items for extended slots) for each overlay key.
func printGearOverlayMigrationSQL(families []model.GearFamily, overlayKeys []string) {
	bySlotName := make(map[string]model.GearFamily, len(families))
	byName := make(map[string]model.GearFamily, len(families))
	for _, g := range families {
		bySlotName[overlayKey(g)] = g
		byName[g.Name] = g
	}
	fmt.Printf("-- balanceall -gear-print-sql (pairs with -gear-overlay)\n\n")
	for _, k := range overlayKeys {
		g, ok := bySlotName[k]
		if !ok {
			g, ok = byName[k]
		}
		if !ok {
			fmt.Printf("-- WARNING: no catalog family for overlay key %q\n", k)
			continue
		}
		r := catalogRarityForName(g.Name)
		ps := model.ScalePrimary(g.BasePrimary, 1, r)
		fmt.Printf(`UPDATE public.gear SET base_primary = %d, primary_stat = %d WHERE slot = '%s' AND name = %s;
`, g.BasePrimary, ps, string(g.Slot), sqlQuote(g.Name))
		switch g.Slot {
		case model.SlotMainHand, model.SlotChest:
			// Catalog is fully in public.gear above.
		default:
			if g.FormID != "" {
				fmt.Printf(`UPDATE public.equipment_items SET base_primary = %d, primary_stat = %d WHERE form_id = %s;
`, g.BasePrimary, ps, sqlQuote(g.FormID))
			}
		}
		fmt.Println()
	}
}

func catalogRarityForName(name string) model.Rarity {
	switch name {
	case "Rusty Dagger", "Iron Sword", "Rusty Axe",
		"Leather Armor", "Chainmail", "Iron Plate":
		return model.RarityCommon
	case "Iron Dagger", "Steel Sword", "Battle Axe",
		"Ranger's Vest", "Reinforced Mail", "Steel Plate":
		return model.RarityUncommon
	case "Assassin's Blade", "Longsword", "War Axe",
		"Shadow Cloak", "Battle Armor", "Fortress Armor", "Guardian's Plate":
		return model.RarityRare
	case "Phantom Edge", "Excalibur", "Infernal Axe",
		"Phantom Garb", "Royal Guard", "Dragon Scale", "Guardian's Bastion":
		return model.RarityEpic
	case "Fang of the Void", "Soul Reaver", "Godslayer's Edge",
		"Whisper of the Void", "Crown of Eternity", "Dragon Slayer", "Ancient Guardian's Aegis":
		return model.RarityLegendary
	default:
		return model.RarityCommon
	}
}

func sqlQuote(s string) string {
	return "'" + strings.ReplaceAll(s, "'", "''") + "'"
}
