package cloud

import (
	"context"
	"fmt"
	"sync"
	"time"

	"git.linuxforward.com/byop/byop-engine/models"
)

// InstanceSize represents the size configuration for a VM instance
type InstanceSize struct {
	ID       string  `json:"id"`
	Name     string  `json:"name"`
	CPUCores int     `json:"cpu_cores"`
	MemoryGB int     `json:"memory_gb"`
	DiskGB   int     `json:"disk_gb"`
	Price    float64 `json:"price"` // Hourly price
}

// Region represents a geographic region
type Region struct {
	ID   string `json:"id"`
	Name string `json:"name"`
	Zone string `json:"zone"`
}

// Instance represents a virtual machine instance
type Instance struct {
	ID             string            `json:"id"`
	Name           string            `json:"name"`
	Region         string            `json:"region"`
	Size           string            `json:"size"`
	ImageID        string            `json:"image_id"`
	IPAddress      string            `json:"ip_address"`
	PrivateIP      string            `json:"private_ip,omitempty"`
	Status         string            `json:"status"` // Creating, Running, Stopping, Stopped, Restarting, Terminated
	CreatedAt      time.Time         `json:"created_at"`
	Tags           map[string]string `json:"tags,omitempty"`
	SecurityGroups []string          `json:"security_groups,omitempty"`
}

// InstanceCreateOpts are options to configure a new instance
type InstanceCreateOpts struct {
	Name           string             `json:"name"`
	Region         string             `json:"region"`
	Size           string             `json:"size"`
	ImageID        string             `json:"image_id"`
	SSHKeyIDs      []string           `json:"ssh_key_ids,omitempty"`
	UserData       string             `json:"user_data,omitempty"`
	Tags           map[string]string  `json:"tags,omitempty"`
	SecurityGroups []string           `json:"security_groups,omitempty"`
	Components     []models.Component `json:"components,omitempty"`
}

// SSHKey represents an SSH key
type SSHKey struct {
	ID          string    `json:"id"`
	Name        string    `json:"name"`
	Fingerprint string    `json:"fingerprint"`
	PublicKey   string    `json:"public_key"`
	CreatedAt   time.Time `json:"created_at"`
}

// Image represents an operating system image
type Image struct {
	ID          string    `json:"id"`
	Name        string    `json:"name"`
	Description string    `json:"description"`
	Type        string    `json:"type"` // base, snapshot, backup
	Status      string    `json:"status"`
	CreatedAt   time.Time `json:"created_at"`
	MinDiskGB   int       `json:"min_disk_gb,omitempty"`
	SizeGB      int       `json:"size_gb,omitempty"`
}

// Provider defines the interface that all cloud providers must implement
type Provider interface {
	// Initialize sets up the provider with credentials and configuration
	Initialize(config map[string]string) error

	// ListRegions lists all available regions
	ListRegions(ctx context.Context) ([]Region, error)

	// ListInstanceSizes lists available VM sizes
	ListInstanceSizes(ctx context.Context, region string) ([]InstanceSize, error)

	// ListInstances lists all instances
	ListInstances(ctx context.Context) ([]Instance, error)

	// GetInstance gets a specific instance by ID
	GetFirstFreeInstance(ctx context.Context) (*Instance, error)

	// GetPreviewInstance gets a specific instance by ID
	GetPreviewInstance(ctx context.Context) (*Instance, error)

	// DeleteInstance deletes an instance
	ResetInstance(ctx context.Context, id string) error

	// StartInstance starts an instance
	StartInstance(ctx context.Context, id string) error

	// StopInstance stops an instance
	StopInstance(ctx context.Context, id string) error

	// RestartInstance restarts an instance
	RestartInstance(ctx context.Context, id string) error

	// WaitForInstanceStatus waits for an instance to reach a specific status
	WaitForInstanceStatus(ctx context.Context, id, status string, timeout time.Duration) error
}

// // ProviderFactory is a function that creates a new provider instance
type ProviderFactory func() Provider

// providerRegistry manages provider factories and initialized instances
type providerRegistry struct {
	factories map[string]ProviderFactory
	instances map[string]Provider
	mu        sync.RWMutex
}

// global registry instance
var registry = &providerRegistry{
	factories: make(map[string]ProviderFactory),
	instances: make(map[string]Provider),
}

// RegisterProvider registers a new provider factory
func RegisterProvider(name string, factory ProviderFactory) {
	registry.mu.Lock()
	defer registry.mu.Unlock()
	registry.factories[name] = factory
}

// InitializeProvider initializes a provider with config and stores the instance
func InitializeProvider(name string, config map[string]string) error {
	registry.mu.Lock()
	defer registry.mu.Unlock()

	factory, ok := registry.factories[name]
	if !ok {
		return fmt.Errorf("provider %s not found", name)
	}

	provider := factory()
	err := provider.Initialize(config)
	if err != nil {
		return err
	}

	// Store the initialized provider instance
	registry.instances[name] = provider
	return nil
}

// GetProvider returns an initialized provider by name
func GetProvider(name string) (Provider, bool) {
	registry.mu.RLock()
	defer registry.mu.RUnlock()

	// Return the initialized instance if it exists
	if provider, ok := registry.instances[name]; ok {
		return provider, true
	}

	// If there's no initialized instance but there's a factory,
	// return false to indicate it needs initialization
	_, ok := registry.factories[name]
	return nil, ok
}

// GetSupportedProviders returns a list of supported provider names
func GetSupportedProviders() []string {
	registry.mu.RLock()
	defer registry.mu.RUnlock()

	var names []string
	for name := range registry.factories {
		names = append(names, name)
	}
	return names
}
