package provider

import (
	"context"
	"fmt"
	"time"

	"github.com/hashicorp/go-hclog"
)

type InstanceGroup interface {
	Init(ctx context.Context, logger hclog.Logger, settings Settings) (ProviderInfo, error)

	// Update updates instance data from the instance group, passing a function
	// to perform instance reconciliation.
	Update(ctx context.Context, fn func(instance string, state State)) error

	// Increase requests more instances to be created. It returns how many
	// instances were successfully requested.
	Increase(ctx context.Context, n int) (succeeded int, err error)

	// Decrease removes the specified instances from the instance group. It
	// returns instance IDs of successful requests for removal.
	Decrease(ctx context.Context, instances []string) (succeeded []string, err error)

	// ConnectInfo returns additional information about an instance,
	// useful for creating a connection.
	ConnectInfo(ctx context.Context, instance string) (ConnectInfo, error)

	// Shutdown performs any cleanup tasks required when the plugin is to shutdown.
	Shutdown(ctx context.Context) error
}

// State is the state of an instance.
type State string

const (
	StateCreating State = "creating"
	StateRunning  State = "running"
	StateDeleting State = "deleting"
	StateDeleted  State = "deleted"
	StateTimeout  State = "timeout"
)

// ProviderInfo is information about the provider returned from Init().
type ProviderInfo struct {
	ID        string
	MaxSize   int
	Version   string
	BuildInfo string
}

type Settings struct {
	ConnectorConfig
}

// ConnectorConfig is used to describe how an instance is setup for
// connecting to.
type ConnectorConfig struct {
	OS   string `json:"os"`   // the OS of the instance
	Arch string `json:"arch"` // The CPU architecture of the instance

	Protocol             Protocol      `json:"protocol"`
	Username             string        `json:"username"`
	Password             string        `json:"password"`
	Key                  []byte        `json:"key"`
	UseStaticCredentials bool          `json:"use_static_credentials"`
	Keepalive            time.Duration `json:"keepalive"`
	Timeout              time.Duration `json:"timeout"`
}

// Protocol is the protocol supported by an instance: ssh, winrm
type Protocol string

// Valid checks that the protocol is a supported type.
func (p Protocol) Valid() error {
	switch p {
	case ProtocolSSH, ProtocolWinRM:
		return nil
	}
	return fmt.Errorf("invalid protocol: must be winrm, ssh")
}

const (
	ProtocolSSH   Protocol = "ssh"
	ProtocolWinRM Protocol = "winrm"
)

// ConnectInfo provides useful information for connecting to an instance.
type ConnectInfo struct {
	ConnectorConfig
	ID           string // id of the instance
	ExternalAddr string // external address of the instance
	InternalAddr string // internal address of the instance

	// Expires is an optional time in the future when details regarding the
	// connection (such as authentication) have expired and need refreshing.
	Expires *time.Time `json:"expires,omitempty"`
}
