package cloud

import (
	"context"
	"errors"
	"fmt"
	"strconv"
	"strings"
	"time"

	"git.linuxforward.com/byop/byop-engine/models"
	"github.com/ovh/go-ovh/ovh"
	"github.com/sirupsen/logrus"
)

// OVHProvider implements the Provider interface for OVH Cloud
type OVHProvider struct {
	entry      *logrus.Entry
	client     *ovh.Client
	region     string
	configured bool
}

// NewOVHProvider creates a new OVH provider
func NewOVHProvider() Provider {
	return &OVHProvider{
		entry: logrus.WithField("provider", "ovh"),
	}
}

func init() {
	RegisterProvider("ovh", NewOVHProvider)
}

// Initialize sets up the OVH provider with credentials and configuration
func (p *OVHProvider) Initialize(config map[string]string) error {
	// Check required configuration
	requiredKeys := []string{"client_id", "client_secret", "endpoint"}
	for _, key := range requiredKeys {
		if _, ok := config[key]; !ok {
			return fmt.Errorf("missing required configuration key: %s", key)
		}
	}

	// Create OVH client
	client, err := ovh.NewOAuth2Client(
		config["endpoint"],
		config["client_id"],
		config["client_secret"],
	)
	if err != nil {
		return fmt.Errorf("failed to create OVH client: %w", err)
	}

	// Test the client by trying to list VPS IDs
	var vpsIDs []string
	if err = client.Get("/vps", &vpsIDs); err != nil {
		return fmt.Errorf("failed to connect to OVH API: %w", err)
	}

	// Set client before testing VPS connectivity
	p.client = client
	p.configured = true
	// Set region if provided
	if region, ok := config["region"]; ok {
		p.region = region
	}

	p.entry.Info("OVH provider initialized")

	return nil
}

// ListRegions lists all available OVH regions
func (p *OVHProvider) ListRegions(ctx context.Context) ([]Region, error) {
	if !p.configured {
		return nil, errors.New("provider not configured")
	}

	type OVHRegion struct {
		AvailabilityZones []string `json:"availabilityZones"`
		CardinalPoint     string   `json:"cardinalPoint"`
		CityCode          string   `json:"cityCode"`
		CityLatitude      float64  `json:"cityLatitude"`
		CityLongitude     float64  `json:"cityLongitude"`
		CityName          string   `json:"cityName"`
		Code              string   `json:"code"`
		CountryCode       string   `json:"countryCode"`
		CountryName       string   `json:"countryName"`
		GeographyCode     string   `json:"geographyCode"`
		GeographyName     string   `json:"geographyName"`
		Location          string   `json:"location"`
		Name              string   `json:"name"`
		OpeningYear       int      `json:"openingYear"`
		SpecificType      string   `json:"specificType"`
		Type              string   `json:"type"`
	}

	// Get the list of regions from OVH
	path := "/v2/location"
	var regions []Region
	var ovhRegions []OVHRegion
	err := p.client.Get(path, &ovhRegions)
	if err != nil {
		return nil, fmt.Errorf("failed to list regions: %w", err)
	}

	fmt.Printf("OVH regions: %v\n", ovhRegions)

	// Convert OVHRegion to Region
	for _, ovhRegion := range ovhRegions {
		regions = append(regions, Region{
			ID:   ovhRegion.Name,
			Zone: strings.Join(ovhRegion.AvailabilityZones, ","),
			// Add other fields as needed
		})
	}
	return regions, nil
}

// ListInstanceSizes lists available VM sizes (flavors) in OVH
func (p *OVHProvider) ListInstanceSizes(ctx context.Context, region string) ([]InstanceSize, error) {
	// TODO: Implement this method
	return nil, errors.New("not implemented")
}

// ListInstances lists all instances in OVH
func (p *OVHProvider) ListInstances(ctx context.Context) ([]Instance, error) {
	if !p.configured {
		return nil, errors.New("provider not configured")
	}

	path := "/vps"
	var vpsIDs []string
	var vpsList []*models.OVHVPS
	err := p.client.Get(path, &vpsIDs)
	if err != nil {
		return nil, fmt.Errorf("failed to list instances: %w", err)
	}

	// Get details for each VPS ID
	for _, vpsID := range vpsIDs {
		path := fmt.Sprintf("/vps/%s", vpsID)
		vps := &models.OVHVPS{}
		err := p.client.Get(path, vps)
		if err != nil {
			return nil, fmt.Errorf("failed to get instance %s: %w", vpsID, err)
		}
		vpsList = append(vpsList, vps)
	}

	instances := make([]Instance, len(vpsList))
	for i, vps := range vpsList {
		// convert size
		instances[i] = Instance{
			ID:        vps.Name,
			Name:      vps.DisplayName,
			Region:    vps.Zone,
			Size:      strconv.Itoa(vps.VCore),
			Status:    vps.State,
			IPAddress: vps.Name, // Assuming Name is the IP address
		}
	}

	return instances, nil
}

// ResetInstance resets an instance in OVH
func (p *OVHProvider) ResetInstance(ctx context.Context, id string) error {
	// TODO: Implement this method
	return errors.New("not implemented")
}

// StartInstance starts an instance in OVH
func (p *OVHProvider) StartInstance(ctx context.Context, id string) error {
	// TODO: Implement this method
	return errors.New("not implemented")
}

// StopInstance stops an instance in OVH
func (p *OVHProvider) StopInstance(ctx context.Context, id string) error {
	// TODO: Implement this method
	return errors.New("not implemented")
}

// RestartInstance restarts an instance in OVH
func (p *OVHProvider) RestartInstance(ctx context.Context, id string) error {
	// TODO: Implement this method
	return errors.New("not implemented")
}

// WaitForInstanceStatus waits for an instance to reach a specific status
func (p *OVHProvider) WaitForInstanceStatus(ctx context.Context, id, status string, timeout time.Duration) error {
	// TODO: Implement this method
	return errors.New("not implemented")
}

// GetFirstFreeInstance retrieves the first available instance
func (p *OVHProvider) GetFirstFreeInstance(ctx context.Context) (*Instance, error) {
	// List all instances
	instances, err := p.ListInstances(ctx)
	if err != nil {
		return nil, fmt.Errorf("failed to list instances: %w", err)
	}

	// Iterate through instances to find the first free one
	for _, instance := range instances {
		// This will be final optioons using tags
		// Check if instance has tags and if the instance is marked as available
		// if instance.DisplayName != nil && instance.Tags["byop-state"] == "available" {
		// 	// Get full details of the instance
		// 	vpsPath := fmt.Sprintf("/vps/%s", instance.ID)
		// 	vps := &models.OVHVPS{}
		// 	err := p.client.Get(vpsPath, vps)
		// 	if err != nil {
		// 		// Log the error but continue with next instance
		// 		fmt.Printf("Error fetching details for VPS %s: %v\n", instance.ID, err)
		// 		continue
		// 	}

		// 	return vps, nil
		// }

		// Check if display name contains byom.fr or byop.fr
		if instance.Name != "" && (strings.Contains(instance.Name, "byom.fr") || strings.Contains(instance.Name, "byop.fr")) {
			// Get full details of the instance
			vpsPath := fmt.Sprintf("/vps/%s", instance.ID)
			vps := &models.OVHVPS{}
			err := p.client.Get(vpsPath, vps)
			if err != nil {
				// Log the error but continue with next instance
				fmt.Printf("Error fetching details for VPS %s: %v\n", instance.ID, err)
				continue
			}

			// Mark this instance as in use
			if instance.Tags != nil {
				instance.Tags["byop-state"] = "in-use"
			}

			return &Instance{
				ID:        vps.Name,
				Name:      vps.DisplayName,
				IPAddress: vps.Name,
				Region:    vps.Zone,
				Size:      strconv.Itoa(vps.VCore),
				Status:    vps.State,
			}, nil
		}
	}

	// If no instance with "available" tag is found, just return the first running instance
	// if len(instances) > 0 {
	// 	for _, instance := range instances {
	// 		if instance.Status == "Running" {
	// 			vpsPath := fmt.Sprintf("/vps/%s", instance.ID)
	// 			vps := &models.OVHVPS{}
	// 			err := p.client.Get(vpsPath, vps)
	// 			if err != nil {
	// 				// Log the error but continue
	// 				fmt.Printf("Error fetching details for VPS %s: %v\n", instance.ID, err)
	// 				continue
	// 			}

	// 			// Mark this instance as in use
	// 			if instance.Tags != nil {
	// 				instance.Tags["byop-state"] = "in-use"
	// 			}

	// 			return vps, nil
	// 		}
	// 	}
	// }

	// If no instances are found or none are available, return an error
	return nil, fmt.Errorf("no free instances found in OVH infrastructure")
}

// GetPreviewInstance retrieves the preview instance
// preview instance has a specific display name format
// displayname format: "preview.byop.fr"
func (p *OVHProvider) GetPreviewInstance(ctx context.Context) (*Instance, error) {
	if !p.configured {
		return nil, errors.New("provider not configured")
	}

	// List all instances
	instances, err := p.ListInstances(ctx)
	if err != nil {
		return nil, fmt.Errorf("failed to list instances: %w", err)
	}

	// Iterate through instances to find the first preview instance
	for _, instance := range instances {
		// Check if display name matches the preview format
		if strings.Contains(instance.Name, "preview.byop.fr") {
			fmt.Printf("Found preview instance: %s\n", instance.Name)
			// print IP
			fmt.Printf("Preview instance IP: %s\n", instance.IPAddress)

			return &instance, nil
		}
	}

	return nil, fmt.Errorf("no preview instance found in OVH infrastructure")
}
