package plugin

import (
	"fmt"
)

// Phase 12 E1b — Supply procurement, daily burn, and depletion effects.
// Spec: gogobee_expedition_system.md §4.

// Pack pricing/yield. §4.2.
const (
	SupplyPackStandardSU    = 10
	SupplyPackStandardCoins = 50
	SupplyPackStandardMax   = 3 // per expedition

	SupplyPackDeluxeSU    = 20
	SupplyPackDeluxeCoins = 90
	SupplyPackDeluxeMax   = 1 // per expedition

	SupplyForageMaxSU = 4 // 1d4 cap (Ranger, WIS DC 12) — §4.2
)

// supplyDailyBurn returns the base SU/day for a zone tier (§4.1).
// Tier 1: 1, Tier 2: 1.5, Tier 3: 2, Tier 4: 3, Tier 5: 4.
func supplyDailyBurn(tier ZoneTier) float32 {
	switch tier {
	case ZoneTierBeginner:
		return 1.0
	case ZoneTierApprentice:
		return 1.5
	case ZoneTierJourneyman:
		return 2.0
	case ZoneTierVeteran:
		return 3.0
	case ZoneTierLegendary:
		return 4.0
	}
	return 1.0
}

// supplyHarshMultiplier returns the harsh-conditions multiplier (§4.1).
// Tier 1×1, Tier 2×1.5, Tier 3×2, Tier 4×2.5, Tier 5×3.
func supplyHarshMultiplier(tier ZoneTier) float32 {
	switch tier {
	case ZoneTierBeginner:
		return 1.0
	case ZoneTierApprentice:
		return 1.5
	case ZoneTierJourneyman:
		return 2.0
	case ZoneTierVeteran:
		return 2.5
	case ZoneTierLegendary:
		return 3.0
	}
	return 1.0
}

// SupplyDepletionState classifies remaining supply ratio (§4.3).
type SupplyDepletionState int

const (
	SupplyNormal SupplyDepletionState = iota
	SupplyRationing
	SupplySevereRationing
	SupplyStarvation
)

// supplyDepletion returns the depletion state for a supplies snapshot.
// Brackets: >25% normal, 10–25% rationing, 1–9% severe, 0 starvation.
func supplyDepletion(s ExpeditionSupplies) SupplyDepletionState {
	if s.Max <= 0 {
		return SupplyNormal
	}
	if s.Current <= 0 {
		return SupplyStarvation
	}
	pct := (s.Current / s.Max) * 100
	switch {
	case pct >= 25:
		return SupplyNormal
	case pct >= 10:
		return SupplyRationing
	default:
		return SupplySevereRationing
	}
}

// supplyRollModifier maps depletion state to the universal roll penalty
// applied to attack/skill rolls (§4.3). Starvation has its own CON bleed
// handled elsewhere; the roll mod for starvation matches severe rationing
// floor so combat math doesn't divide by zero.
func supplyRollModifier(state SupplyDepletionState) int {
	switch state {
	case SupplyRationing:
		return -1
	case SupplySevereRationing, SupplyStarvation:
		return -2
	}
	return 0
}

// supplyAllowsLongRest returns false when severe rationing (§4.3) blocks
// long rests. Starvation also blocks (forced extraction is the next step).
func supplyAllowsLongRest(state SupplyDepletionState) bool {
	return state == SupplyNormal || state == SupplyRationing
}

// SupplyPurchase represents an expedition outfitting choice.
type SupplyPurchase struct {
	StandardPacks int
	DeluxePacks   int
}

// Total SU yield from a purchase.
func (p SupplyPurchase) Total() float32 {
	return float32(p.StandardPacks*SupplyPackStandardSU + p.DeluxePacks*SupplyPackDeluxeSU)
}

// Total cost in coins.
func (p SupplyPurchase) Cost() int {
	return p.StandardPacks*SupplyPackStandardCoins + p.DeluxePacks*SupplyPackDeluxeCoins
}

// Validate enforces §4.2 caps (max 3 standard, max 1 deluxe, no negatives,
// at least one pack purchased — an expedition without supplies is not a
// legal start).
func (p SupplyPurchase) Validate() error {
	if p.StandardPacks < 0 || p.DeluxePacks < 0 {
		return fmt.Errorf("supply pack counts must be non-negative")
	}
	if p.StandardPacks > SupplyPackStandardMax {
		return fmt.Errorf("standard packs capped at %d (got %d)", SupplyPackStandardMax, p.StandardPacks)
	}
	if p.DeluxePacks > SupplyPackDeluxeMax {
		return fmt.Errorf("deluxe packs capped at %d (got %d)", SupplyPackDeluxeMax, p.DeluxePacks)
	}
	if p.StandardPacks == 0 && p.DeluxePacks == 0 {
		return fmt.Errorf("expedition requires at least one supply pack")
	}
	return nil
}

// makeSupplies builds an ExpeditionSupplies from a purchase, sized for the
// given zone tier. Harsh-conditions multiplier defaults to 1 (off);
// upgraded by E2/E3 events.
func makeSupplies(tier ZoneTier, p SupplyPurchase) ExpeditionSupplies {
	total := p.Total()
	return ExpeditionSupplies{
		Current:       total,
		Max:           total,
		DailyBurn:     supplyDailyBurn(tier),
		HarshMod:      1.0,
		PacksStandard: p.StandardPacks,
		PacksDeluxe:   p.DeluxePacks,
	}
}

// applyDailyBurn deducts one day's supplies from the snapshot (caller
// persists). Returns the new snapshot and the SU consumed.
//
// Multiplier precedence (§4.1, §8.3):
//   - siege overrides everything with a hard 2× floor (even for tier 1
//     where HarshMod is 1×) — the dungeon is actively starving you out.
//   - otherwise, harshActive applies HarshMod (zone-tier scaled).
func applyDailyBurn(s ExpeditionSupplies, harshActive, siege bool) (ExpeditionSupplies, float32) {
	burn := s.DailyBurn
	switch {
	case siege:
		mult := s.HarshMod
		if mult < 2 {
			mult = 2
		}
		burn *= mult
	case harshActive:
		mult := s.HarshMod
		if mult <= 0 {
			mult = 1
		}
		burn *= mult
	}
	s.Current -= burn
	if s.Current < 0 {
		s.Current = 0
	}
	// Reset per-day forage flag.
	s.ForagedToday = false
	return s, burn
}
