/*
Copyright 2024 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 ibmcloud

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"strings"
	"time"

	"k8s.io/apimachinery/pkg/util/errors"
	klog "k8s.io/klog/v2"
)

const (
	userAgent = "kubernetes/cluster-autoscaler"
	ibmAPIBaseURLV2 = "https://containers.cloud.ibm.com/global/v2"
)

type ibmManagerRest struct {
	authToken  string
	baseURL    *url.URL
	nodePools  map[string]*ibmManagerNodePool
}

func createIbmManagerRest(configReader io.Reader) (*ibmManagerRest, error) {
	var manager ibmManagerRest
	manager.nodePools = make(map[string]*ibmManagerNodePool)

	ibmAuthToken := os.Getenv("IBM_AUTH_TOKEN")
	if len(ibmAuthToken) == 0 {
		klog.Fatalf("IBM_AUTH_TOKEN is required and missing")
	}

	manager.authToken = ibmAuthToken

	base, err := url.Parse(ibmAPIBaseURLV2)
	if err != nil {
		return nil, fmt.Errorf("invalid baseURL %s: %v", ibmAPIBaseURLV2, err)
	}
	manager.baseURL = base

	return &manager, nil
}

func (mgr *ibmManagerRest) request(ctx context.Context, method, pathUrl string, jsonData []byte) ([]byte, error) {
	reqUrl, err := url.Parse(fmt.Sprintf("%s%s", mgr.baseURL, pathUrl))
	if err != nil {
		return nil, fmt.Errorf("invalid request path %s: %v", pathUrl, err)
	}

	req, err := http.NewRequestWithContext(ctx, method, reqUrl.String(), bytes.NewBuffer(jsonData))
	if err != nil {
		return nil, fmt.Errorf("failed to create request: %w", err)
	}

	req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", mgr.authToken))
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("User-Agent", userAgent)
	dump, _ := httputil.DumpRequestOut(req, true)
	klog.V(2).Infof("%s", string(dump))

	client := &http.Client{}

	resp, err := client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("failed to perform request: %w", err)
	}

	defer func() {
		if err := resp.Body.Close(); err != nil {
			klog.Errorf("failed to close response body: %v", err)
		}
	}()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("failed to read response body: %w", err)
	}

	if resp.StatusCode >= 200 && resp.StatusCode < 300 {
		return body, nil
	}

	return nil, fmt.Errorf("API request failed with status code %d: %s", resp.StatusCode, body)
}

// Example function for listing servers using IBM Cloud Kubernetes Service v2 API
func (mgr *ibmManagerRest) listIbmServers(ctx context.Context, clusterID string) ([]Server, error) {
	req := fmt.Sprintf("/v2/clusters/%s/servers", clusterID)

	result, err := mgr.request(ctx, "GET", req, nil)
	if err != nil {
		return nil, err
	}

	var servers []Server
	if err := json.Unmarshal(result, &servers); err != nil {
		return nil, fmt.Errorf("failed to unmarshal response body: %w", err)
	}

	return servers, nil
}

// Additional refactored functions following similar pattern...
