package service

import (
	"context"
	"fmt"
	"os/exec"

	"github.com/Azure/azure-sdk-for-go/sdk/azcore"
	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute"
	"github.com/aliyun/alibaba-cloud-sdk-go/services/ecs"
	"github.com/aws/aws-sdk-go-v2/service/ec2"
	"google.golang.org/api/compute/v1"
)

// NodeRestartProvider is an interface to restart a pod's node
type NodeRestartProvider interface {
	Restart(context.Context, string) error
}

type awsNodeRestarter struct {
	ec2Client *ec2.Client
}

func NewAWSNodeRestarter(client *ec2.Client) NodeRestartProvider {
	return &awsNodeRestarter{ec2Client: client}
}

func (r *awsNodeRestarter) Restart(ctx context.Context, providerId string) error {
	_, instanceId, err := parseEKSNodeProviderID(providerId)
	if err != nil {
		return err
	}
	_, err = r.ec2Client.RebootInstances(ctx, &ec2.RebootInstancesInput{InstanceIds: []string{instanceId}})
	return err
}

type gcpNodeRestarter struct {
	computeSvc *compute.Service
}

func NewGCPNodeRestarter(computeSvc *compute.Service) NodeRestartProvider {
	return &gcpNodeRestarter{computeSvc: computeSvc}
}

func (r *gcpNodeRestarter) Restart(ctx context.Context, providerID string) error {
	project, zone, instanceName, err := parseGKENodeProviderID(providerID)
	if err != nil {
		return err
	}
	if _, err = r.computeSvc.Instances.Reset(project, zone, instanceName).Context(ctx).Do(); err != nil {
		return fmt.Errorf("failed to reset instance %s: %w", instanceName, err)
	}
	return nil
}

type azureNodeRestarter struct {
	cred azcore.TokenCredential
}

func NewAzureNodeRestarter(cred azcore.TokenCredential) NodeRestartProvider {
	return &azureNodeRestarter{cred: cred}
}

func (r *azureNodeRestarter) Restart(ctx context.Context, providerID string) error {
	subscription, resourceGroup, vmss, instanceID, err := parseAKSNodeProviderID(providerID)
	if err != nil {
		return err
	}

	vm, err := armcompute.NewVirtualMachineScaleSetVMsClient(subscription, r.cred, nil)
	if err != nil {
		return err
	}
	if _, err = vm.BeginRestart(ctx, resourceGroup, vmss, instanceID, nil); err != nil {
		return fmt.Errorf("failed to restart VM instance %s: %w", instanceID, err)
	}
	return nil
}

// To use kindNodeRestarter, the cluster-agent should be run on host machine
type kindNodeRestarter struct{}

func NewKindNodeRestarter() NodeRestartProvider {
	return &kindNodeRestarter{}
}

func (r *kindNodeRestarter) Restart(ctx context.Context, providerID string) error {
	_, node, err := parseAlicloudNodeProviderID(providerID)
	if err != nil {
		return err
	}
	//nolint:gosec // Docker command with node name from validated provider ID
	return exec.Command("docker", "restart", node).Run()
}

type alicloudNodeRestarter struct {
	ecsClient *ecs.Client
}

func NewAlicloudNodeRestarter(ecsClient *ecs.Client) NodeRestartProvider {
	return &alicloudNodeRestarter{
		ecsClient: ecsClient,
	}
}

func (r *alicloudNodeRestarter) Restart(ctx context.Context, providerID string) error {

	if r.ecsClient == nil {
		return fmt.Errorf("alicloud ECS client is not initialized")
	}

	request := ecs.CreateRebootInstanceRequest()
	request.InstanceId = providerID
	request.Scheme = "https"

	response, err := r.ecsClient.RebootInstance(request)

	if !response.IsSuccess() {
		return fmt.Errorf("reboot instance failed: %s", response.GetHttpContentString())
	}
	return err
}
