package model

import "time"

// ExcursionPhase tracks where the hero is within a mini-adventure session.
// The lifecycle is: Out → Wild → Return → (back to road, phase cleared).
// For KindTown, Phase is usually ExcursionWild while using attractor movement during wander/rest.
type ExcursionPhase string

const (
	ExcursionNone   ExcursionPhase = ""
	ExcursionOut    ExcursionPhase = "out"    // moving off-road into the forest
	ExcursionWild   ExcursionPhase = "wild"   // in the wilderness (encounters happen here)
	ExcursionReturn ExcursionPhase = "return" // returning to the road (encounters still possible)
)

// ExcursionKind distinguishes roadside rest vs walking adventure vs in-town tour.
type ExcursionKind string

const (
	ExcursionKindNone      ExcursionKind = ""
	ExcursionKindRoadside  ExcursionKind = "roadside"
	ExcursionKindAdventure ExcursionKind = "adventure"
	ExcursionKindTown      ExcursionKind = "town"
	ExcursionKindHeroMeet  ExcursionKind = "hero_meet"
)

// ExcursionPhaseHeroMeet: heroes stand still during a paired meet session.
const ExcursionPhaseHeroMeet ExcursionPhase = "meet"

// HeroMeetSubPrompt: online — wait for player text or HeroMeetPromptWindowMs then auto lines.
const HeroMeetSubPrompt = "prompt"

// HeroMeetSubAuto: alternating scripted lines every HeroMeetAutoLineInterval (tuning).
const HeroMeetSubAuto = "auto"

// TownTourPhase is the sub-state machine while ExcursionKind == town (StateInTown).
type TownTourPhase string

const (
	TownTourPhaseWander       TownTourPhase = "wander"
	TownTourPhaseNpcApproach  TownTourPhase = "npc_approach"
	TownTourPhaseNpcWelcome   TownTourPhase = "npc_welcome"
	TownTourPhaseNpcService   TownTourPhase = "npc_service"
	TownTourPhaseRest         TownTourPhase = "rest"
)

// ExcursionSession holds the live state of an active mini-adventure (off-road excursion) or town tour.
// When Phase == ExcursionNone the session is inactive and all other fields are zero-valued (except Kind for town cleared on leave).
type ExcursionSession struct {
	Kind  ExcursionKind
	Phase ExcursionPhase
	StartedAt time.Time

	// OutUntil / WildUntil / ReturnUntil: legacy time-based FSM (ignored when Kind is set).
	OutUntil time.Time
	WildUntil time.Time
	ReturnUntil time.Time

	// DepthWorldUnits is used to place forest attractors (perpendicular distance from road spine).
	DepthWorldUnits float64

	// RoadFreezeWaypoint / RoadFreezeFraction capture road progress at the moment the hero
	// left the road, so it can be restored exactly when the excursion ends.
	RoadFreezeWaypoint int
	RoadFreezeFraction float64

	// Attractor-based movement (Kind != ""): hero walks in world space toward AttractorX/Y.
	StartX, StartY         float64
	AttractorX, AttractorY float64
	AttractorSet           bool

	// Adventure-only: wall-time when wandering should end (then return to road).
	AdventureEndsAt time.Time
	// Adventure / town wander: next time to pick a new wander attractor (wild phase).
	WanderNextAt time.Time
	// PendingReturnAfterCombat: adventure timer elapsed; wait for combat end then enter return phase.
	PendingReturnAfterCombat bool

	// --- Town tour (Kind == ExcursionKindTown) ---
	TownTourPhase string
	// TownTourEndsAt: wall-time when the hero should leave the town (may defer until idle).
	TownTourEndsAt time.Time
	TownTourNpcID  int64
	// Stand point near NPC during approach / welcome / service.
	TownTourStandX float64
	TownTourStandY float64
	// TownWelcomeUntil: npc_welcome phase deadline (30s, shifted while dialog open).
	TownWelcomeUntil time.Time
	// TownServiceUntil: npc_service phase max wall time (4 min, shifted while UI open).
	TownServiceUntil time.Time
	// TownRestUntil: in-town rest phase end.
	TownRestUntil time.Time
	TownExitPending bool
	// Client has NPCDialog (welcome or service) open — shifts welcome/service deadlines.
	TownTourDialogOpen bool
	// Client has NPCInteraction panel open — shifts service deadline; with dialog shifts welcome too.
	TownTourInteractionOpen bool

	// --- Hero meet (Kind == ExcursionKindHeroMeet) ---
	HeroMeetPartnerID int64
	// SubPhase: HeroMeetSubPrompt | HeroMeetSubAuto
	HeroMeetSubPhase string
	HeroMeetPromptUntil time.Time
	HeroMeetNextAutoAt time.Time
	HeroMeetTurnHeroID int64
	HeroMeetHadPlayerMessage bool
	HeroMeetOfflineDeadline   time.Time
	HeroMeetOfflineTimerRunning bool
	HeroMeetOfflineRemainingMs  int64
	HeroMeetAutoLineIdx int
	// MeetAnchorX/Y: center; heroes placed at anchor ± small offset.
	HeroMeetAnchorX float64
	HeroMeetAnchorY float64
}

// Active reports whether an excursion session is in progress.
func (s *ExcursionSession) Active() bool {
	if s == nil {
		return false
	}
	if s.Kind == ExcursionKindTown {
		return s.TownTourPhase != ""
	}
	if s.Kind == ExcursionKindHeroMeet {
		if s.HeroMeetPartnerID == 0 {
			return false
		}
		switch s.Phase {
		case ExcursionOut, ExcursionPhaseHeroMeet, ExcursionReturn:
			return true
		default:
			return false
		}
	}
	return s.Phase != ExcursionNone
}

// ExcursionPersisted is the JSON-serialisable subset of ExcursionSession stored in the
// heroes.town_pause JSONB column so that reconnect / offline catch-up can resume mid-adventure.
type ExcursionPersisted struct {
	Kind                     string     `json:"kind,omitempty"`
	Phase                    string     `json:"phase,omitempty"`
	StartedAt                *time.Time `json:"startedAt,omitempty"`
	OutUntil                 *time.Time `json:"outUntil,omitempty"`
	WildUntil                *time.Time `json:"wildUntil,omitempty"`
	ReturnUntil              *time.Time `json:"returnUntil,omitempty"`
	DepthWorldUnits          float64    `json:"depthWorldUnits,omitempty"`
	RoadFreezeWaypoint       int        `json:"roadFreezeWaypoint,omitempty"`
	RoadFreezeFraction       float64    `json:"roadFreezeFraction,omitempty"`
	StartX                   float64    `json:"startX,omitempty"`
	StartY                   float64    `json:"startY,omitempty"`
	AttractorX               float64    `json:"attractorX,omitempty"`
	AttractorY               float64    `json:"attractorY,omitempty"`
	AttractorSet             bool       `json:"attractorSet,omitempty"`
	AdventureEndsAt          *time.Time `json:"adventureEndsAt,omitempty"`
	WanderNextAt             *time.Time `json:"wanderNextAt,omitempty"`
	PendingReturnAfterCombat bool       `json:"pendingReturnAfterCombat,omitempty"`

	TownTourPhase           string     `json:"townTourPhase,omitempty"`
	TownTourEndsAt          *time.Time `json:"townTourEndsAt,omitempty"`
	TownTourNpcID           int64      `json:"townTourNpcId,omitempty"`
	TownTourStandX          float64    `json:"townTourStandX,omitempty"`
	TownTourStandY          float64    `json:"townTourStandY,omitempty"`
	TownWelcomeUntil        *time.Time `json:"townWelcomeUntil,omitempty"`
	TownServiceUntil        *time.Time `json:"townServiceUntil,omitempty"`
	TownRestUntil           *time.Time `json:"townRestUntil,omitempty"`
	TownExitPending         bool       `json:"townExitPending,omitempty"`
	TownTourDialogOpen      bool       `json:"townTourDialogOpen,omitempty"`
	TownTourInteractionOpen bool       `json:"townTourInteractionOpen,omitempty"`

	// Hero meet (kind hero_meet)
	HeroMeetPartnerID           int64      `json:"heroMeetPartnerId,omitempty"`
	HeroMeetSubPhase            string     `json:"heroMeetSubPhase,omitempty"`
	HeroMeetPromptUntil         *time.Time `json:"heroMeetPromptUntil,omitempty"`
	HeroMeetNextAutoAt          *time.Time `json:"heroMeetNextAutoAt,omitempty"`
	HeroMeetTurnHeroID          int64      `json:"heroMeetTurnHeroId,omitempty"`
	HeroMeetHadPlayerMessage    bool       `json:"heroMeetHadPlayerMessage,omitempty"`
	HeroMeetOfflineDeadline     *time.Time `json:"heroMeetOfflineDeadline,omitempty"`
	HeroMeetOfflineTimerRunning bool       `json:"heroMeetOfflineTimerRunning,omitempty"`
	HeroMeetOfflineRemainingMs  int64      `json:"heroMeetOfflineRemainingMs,omitempty"`
	HeroMeetAutoLineIdx         int        `json:"heroMeetAutoLineIdx,omitempty"`
	HeroMeetAnchorX             float64    `json:"heroMeetAnchorX,omitempty"`
	HeroMeetAnchorY             float64    `json:"heroMeetAnchorY,omitempty"`
}
