/*
Copyright 2021 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v2

import (
	"context"
	"strings"

	apiv2 "k8s.io/autoscaler/cluster-autoscaler/cloudprovider/exoscale/internal/github.com/exoscale/egoscale/v2/api"
	"k8s.io/autoscaler/cluster-autoscaler/cloudprovider/exoscale/internal/github.com/exoscale/egoscale/v2/oapi"
)

// InstanceType represents a Compute instance type.
type InstanceType struct {
	Authorized *bool
	CPUs       *int64
	Family     *string
	GPUs       *int64
	ID         *string
	Memory     *int64
	Size       *string
}

func instanceTypeFromAPI(t *oapi.InstanceType) *InstanceType {
	return &InstanceType{
		Authorized: t.Authorized,
		CPUs:       t.Cpus,
		Family:     (*string)(t.Family),
		GPUs:       t.Gpus,
		ID:         t.Id,
		Memory:     t.Memory,
		Size:       (*string)(t.Size),
	}
}

// ListInstanceTypes returns the list of existing Instance types.
func (c *Client) ListInstanceTypes(ctx context.Context, zone string) ([]*InstanceType, error) {
	list := make([]*InstanceType, 0)

	resp, err := c.ListInstanceTypesWithResponse(apiv2.WithZone(ctx, zone))
	if err != nil {
		return nil, err
	}

	if resp.JSON200.InstanceTypes != nil {
		for i := range *resp.JSON200.InstanceTypes {
			list = append(list, instanceTypeFromAPI(&(*resp.JSON200.InstanceTypes)[i]))
		}
	}

	return list, nil
}

// GetInstanceType returns the Instance type corresponding to the specified ID.
func (c *Client) GetInstanceType(ctx context.Context, zone, id string) (*InstanceType, error) {
	resp, err := c.GetInstanceTypeWithResponse(apiv2.WithZone(ctx, zone), id)
	if err != nil {
		return nil, err
	}

	return instanceTypeFromAPI(resp.JSON200), nil
}

// FindInstanceType attempts to find an Instance type by family+size or ID.
// To search by family+size, the expected format for v is "[FAMILY.]SIZE" (e.g. "large", "gpu.medium"),
// with family defaulting to "standard" if not specified.
func (c *Client) FindInstanceType(ctx context.Context, zone, x string) (*InstanceType, error) {
	var typeFamily, typeSize string

	parts := strings.SplitN(x, ".", 2)
	if l := len(parts); l > 0 {
		if l == 1 {
			typeFamily, typeSize = "standard", strings.ToLower(parts[0])
		} else {
			typeFamily, typeSize = strings.ToLower(parts[0]), strings.ToLower(parts[1])
		}
	}

	res, err := c.ListInstanceTypes(ctx, zone)
	if err != nil {
		return nil, err
	}

	for _, r := range res {
		if *r.ID == x || (*r.Family == typeFamily && *r.Size == typeSize) {
			return c.GetInstanceType(ctx, zone, *r.ID)
		}
	}

	return nil, apiv2.ErrNotFound
}
