package main

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

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

// enemyPartial mirrors model.Enemy with pointer fields so JSON omits mean "keep DB value".
type enemyPartial struct {
	ID               *int64               `json:"id"`
	Type             *string              `json:"type"`
	Name             *string              `json:"name"`
	HP               *int                 `json:"hp"`
	MaxHP            *int                 `json:"maxHp"`
	Attack           *int                 `json:"attack"`
	Defense          *int                 `json:"defense"`
	Speed            *float64             `json:"speed"`
	CritChance       *float64             `json:"critChance"`
	MinLevel         *int                 `json:"minLevel"`
	MaxLevel         *int                 `json:"maxLevel"`
	BaseLevel        *int                 `json:"baseLevel"`
	LevelVariance    *float64             `json:"levelVariance"`
	MaxHeroLevelDiff *int                 `json:"maxHeroLevelDiff"`
	HPPerLevel       *float64             `json:"hpPerLevel"`
	AttackPerLevel   *float64             `json:"attackPerLevel"`
	DefensePerLevel  *float64             `json:"defensePerLevel"`
	XPPerLevel       *float64             `json:"xpPerLevel"`
	GoldPerLevel     *float64             `json:"goldPerLevel"`
	Level            *int                 `json:"level"`
	XPReward         *int64               `json:"xpReward"`
	GoldReward       *int64               `json:"goldReward"`
	SpecialAbilities *[]model.SpecialAbility `json:"specialAbilities"`
	IsElite          *bool                `json:"isElite"`
}

// applyEnemyOverlayJSON reads a JSON object keyed by enemy type (string), merges each partial onto templates.
// Unknown keys log a warning and are skipped. Keys for types not present in templates log a warning.
func applyEnemyOverlayJSON(path string, templates map[string]model.Enemy) (map[string]model.Enemy, error) {
	data, err := os.ReadFile(path)
	if err != nil {
		return nil, fmt.Errorf("read overlay %q: %w", path, err)
	}
	var raw map[string]json.RawMessage
	if err := json.Unmarshal(data, &raw); err != nil {
		return nil, fmt.Errorf("parse overlay JSON: %w", err)
	}
	out := make(map[string]model.Enemy, len(templates))
	for k, v := range templates {
		out[k] = v
	}
	for typeKey, rawMsg := range raw {
		base, ok := out[typeKey]
		if !ok {
			fmt.Fprintf(os.Stderr, "balanceall overlay: skip unknown type %q (not in loaded templates)\n", typeKey)
			continue
		}
		var p enemyPartial
		if err := json.Unmarshal(rawMsg, &p); err != nil {
			return nil, fmt.Errorf("overlay %q: %w", typeKey, err)
		}
		mergeEnemyPartial(&base, &p)
		out[typeKey] = base
	}
	return out, nil
}

func mergeEnemyPartial(dst *model.Enemy, p *enemyPartial) {
	if p.ID != nil {
		dst.ID = *p.ID
	}
	if p.Type != nil {
		dst.Slug = *p.Type
	}
	if p.Name != nil {
		dst.Name = *p.Name
	}
	if p.HP != nil {
		dst.HP = *p.HP
	}
	if p.MaxHP != nil {
		dst.MaxHP = *p.MaxHP
	}
	if p.Attack != nil {
		dst.Attack = *p.Attack
	}
	if p.Defense != nil {
		dst.Defense = *p.Defense
	}
	if p.Speed != nil {
		dst.Speed = *p.Speed
	}
	if p.CritChance != nil {
		dst.CritChance = *p.CritChance
	}
	if p.MinLevel != nil {
		dst.MinLevel = *p.MinLevel
	}
	if p.MaxLevel != nil {
		dst.MaxLevel = *p.MaxLevel
	}
	if p.BaseLevel != nil {
		dst.BaseLevel = *p.BaseLevel
	}
	if p.LevelVariance != nil {
		dst.LevelVariance = *p.LevelVariance
	}
	if p.MaxHeroLevelDiff != nil {
		dst.MaxHeroLevelDiff = *p.MaxHeroLevelDiff
	}
	if p.HPPerLevel != nil {
		dst.HPPerLevel = *p.HPPerLevel
	}
	if p.AttackPerLevel != nil {
		dst.AttackPerLevel = *p.AttackPerLevel
	}
	if p.DefensePerLevel != nil {
		dst.DefensePerLevel = *p.DefensePerLevel
	}
	if p.XPPerLevel != nil {
		dst.XPPerLevel = *p.XPPerLevel
	}
	if p.GoldPerLevel != nil {
		dst.GoldPerLevel = *p.GoldPerLevel
	}
	if p.Level != nil {
		dst.Level = *p.Level
	}
	if p.XPReward != nil {
		dst.XPReward = *p.XPReward
	}
	if p.GoldReward != nil {
		dst.GoldReward = *p.GoldReward
	}
	if p.SpecialAbilities != nil {
		dst.SpecialAbilities = *p.SpecialAbilities
	}
	if p.IsElite != nil {
		dst.IsElite = *p.IsElite
	}
	// If only one of hp/maxHp was overridden, keep them aligned for template rows.
	if p.MaxHP != nil && p.HP == nil {
		dst.HP = dst.MaxHP
	}
	if p.HP != nil && p.MaxHP == nil {
		dst.MaxHP = dst.HP
	}
}
