package provision

import (
	"context"
	"time"
)

// ReplaceStep enumerates the resumable steps of a Replace orchestration.
// Persisted in replacements.step so a crashed process can continue without
// repeating a boot or a destroy.
type ReplaceStep string

const (
	StepOpenNew    ReplaceStep = "open_new"    // create the replacement node
	StepProbing    ReplaceStep = "probing"     // wait for self-register + probe
	StepNewUp      ReplaceStep = "new_up"      // promote new node, bump version
	StepDraining   ReplaceStep = "draining"    // drain the old node
	StepDestroyOld ReplaceStep = "destroy_old" // destroy old node + release IP
	StepDone       ReplaceStep = "done"
)

// ReplaceStatus is the terminal/running state of a replacement record.
type ReplaceStatus string

const (
	ReplaceRunning ReplaceStatus = "running"
	ReplaceDone    ReplaceStatus = "done"
	ReplaceFailed  ReplaceStatus = "failed"
)

// Replacement mirrors a replacements row (crash-recoverable orchestration).
type Replacement struct {
	UUID      string
	OldNodeID int64
	NewNodeID int64 // 0 until the new node is created
	Pool      Pool
	Status    ReplaceStatus
	Step      ReplaceStep
	CreatedAt time.Time
	UpdatedAt time.Time
}

// Store is the persistence boundary for the provision package. Both the MySQL
// implementation (mysqlStore) and the in-memory test fake satisfy it, keeping
// the service / orchestration logic database-agnostic and unit-testable.
type Store interface {
	// --- nodes ---
	InsertNode(ctx context.Context, n *Node) (int64, error)
	GetNode(ctx context.Context, id int64) (*Node, error)
	GetNodeByUUID(ctx context.Context, uuid string) (*Node, error)
	UpdateNodeStatus(ctx context.Context, id int64, status Status) error
	UpdateNodeEndpoint(ctx context.Context, id int64, endpoint string) error
	// SetNodeInstance records the vendor instance ID and IP-derived endpoint.
	SetNodeInstance(ctx context.Context, id int64, instanceID, endpoint string) error
	SetNodeWeight(ctx context.Context, id int64, weight int) error
	// ListNodesByPool returns nodes whose provider belongs to pool, optionally
	// filtered to a single status (empty = all statuses).
	ListNodesByPool(ctx context.Context, pool Pool, status Status) ([]*Node, error)

	// --- providers ---
	ListProviders(ctx context.Context, pool Pool) ([]*Provider, error)
	GetProvider(ctx context.Context, id int64) (*Provider, error)

	// --- events / audit / directory ---
	WriteNodeEvent(ctx context.Context, nodeID int64, event Event, detailJSON string) error
	WriteAuditLog(ctx context.Context, actor, action, target, metaJSON string) error
	BumpDirectoryVersion(ctx context.Context) (int64, error)

	// --- idempotency ---
	// LookupIdempotency returns the node UUID previously bound to key, or
	// (\"\", false, nil) if unseen.
	LookupIdempotency(ctx context.Context, key string) (string, bool, error)
	// SaveIdempotency binds key→nodeUUID. It is a no-op if the key already
	// exists (first write wins).
	SaveIdempotency(ctx context.Context, key, nodeUUID string) error

	// --- replacements (crash recovery) ---
	CreateReplacement(ctx context.Context, r *Replacement) error
	GetReplacement(ctx context.Context, uuid string) (*Replacement, error)
	UpdateReplacement(ctx context.Context, r *Replacement) error
}
