package lifecycle

import (
	"context"
	"fmt"
	"os"

	"github.com/tidbcloud/controlplane9/internal/cluster"
	"github.com/tidbcloud/controlplane9/internal/config"
	"github.com/tidbcloud/controlplane9/internal/dedicatedapi"
	"github.com/tidbcloud/controlplane9/internal/host"
	"github.com/tidbcloud/controlplane9/internal/host/remote/gcek3d"
)

// DedicatedGCPIdentityReadyStep extends the existing owner-identity stage with
// a read-only gcloud/project gate before either cloud substrate mutates.
type DedicatedGCPIdentityReadyStep struct {
	Inner   cluster.Step
	Session *config.SessionConfig
}

func (s *DedicatedGCPIdentityReadyStep) Name() cluster.StepName {
	return cluster.StepOwnerIdentityReady
}

func (s *DedicatedGCPIdentityReadyStep) Run(_ context.Context) error {
	return fmt.Errorf("owner_identity_ready requires lifecycle state")
}

func (s *DedicatedGCPIdentityReadyStep) RunWithState(ctx context.Context, state *cluster.State) error {
	if err := runLifecycleStep(ctx, state, s.Inner); err != nil {
		return err
	}
	if err := gcek3d.Preflight(ctx, s.Session); err != nil {
		return fmt.Errorf("owner_identity_ready: %w", err)
	}
	return nil
}

// DedicatedGCPHostReadyStep keeps the primary AWS host authoritative, then
// applies and prepares one independently persisted GCP regional host/API.
type DedicatedGCPHostReadyStep struct {
	Primary     cluster.Step
	PrimaryHost *host.Session
	GCPHost     *host.Session
	GCP         *gcek3d.Step
}

func (s *DedicatedGCPHostReadyStep) Name() cluster.StepName { return cluster.StepClusterCreate }

func (s *DedicatedGCPHostReadyStep) Run(_ context.Context) error {
	return fmt.Errorf("GCP host readiness requires lifecycle state")
}

func (s *DedicatedGCPHostReadyStep) RunWithState(ctx context.Context, state *cluster.State) error {
	if s == nil || s.PrimaryHost == nil || s.GCPHost == nil || s.GCP == nil {
		return fmt.Errorf("GCP host readiness requires primary and GCP host contracts")
	}
	if err := runLifecycleStep(ctx, state, s.Primary); err != nil {
		return err
	}
	primary, err := host.Load(s.PrimaryHost.StateDir)
	if err != nil {
		return fmt.Errorf("load primary host before GCP apply: %w", err)
	}
	s.GCP.Primary = primary
	s.GCP.Owner = state.Owner
	s.GCP.CreatedAt = state.CreatedAt
	s.GCP.Selected = s.GCPHost

	var reservation *dedicatedapi.PortReservation
	if _, err := os.Stat(host.StatePath(s.GCPHost.StateDir)); os.IsNotExist(err) {
		reservation, err = dedicatedapi.ReservePreparedPort(s.GCPHost.DedicatedAPI.HostPort)
		if err != nil {
			return err
		}
		defer func() { _ = reservation.Release() }()
	} else if err != nil {
		return fmt.Errorf("stat GCP host before Dedicated API port reservation: %w", err)
	}
	return (&DedicatedAPIHostReadyStep{
		Selected:        s.GCPHost,
		Inner:           s.GCP,
		PortReservation: reservation,
	}).Run(ctx)
}

func runLifecycleStep(ctx context.Context, state *cluster.State, step cluster.Step) error {
	if step == nil {
		return nil
	}
	if stateAware, ok := step.(cluster.StateAwareStep); ok {
		return stateAware.RunWithState(ctx, state)
	}
	return step.Run(ctx)
}
