//go:build linux
// +build linux

// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may
// not use this file except in compliance with the License. A copy of the
// License is located at
//
//	http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file 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 appnet

import (
	"context"
	"net/http"
	"time"

	"github.com/aws/amazon-ecs-agent/ecs-agent/logger"
	"github.com/aws/amazon-ecs-agent/ecs-agent/logger/field"
	"github.com/aws/amazon-ecs-agent/ecs-agent/utils/retry"
	"github.com/pkg/errors"
	prometheus "github.com/prometheus/client_model/go"
)

var (
	// Injection point for UTs
	oneSecondBackoffNoJitter = retry.NewExponentialBackoff(time.Second, time.Second, 0, 1)
)

// GetStats invokes Appnet Agent's stats API to retrieve ServiceConnect stats in prometheus format. This function expects
// an Appnet-Agent-hosted HTTP server listening on the UDS path passed in config.
func (cl *AppNetAgentClient) GetStats(adminSocketPath string, statsRequest string) (map[string]*prometheus.MetricFamily, error) {
	resp, err := cl.performAppnetRequest(http.MethodGet, adminSocketPath, statsRequest)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return nil, errors.Wrapf(err, "received non-OK HTTP status %v from Service Connect stats endpoint", resp.StatusCode)
	}
	return parseServiceConnectStats(resp.Body)
}

// DrainInboundConnections invokes Appnet Agent's drain_listeners API which starts draining ServiceConnect inbound connections.
// This function expects an Appnet-agent-hosted HTTP server listening on the UDS path passed in config.
func (cl *AppNetAgentClient) DrainInboundConnections(adminSocketPath string, drainRequest string) error {
	return retry.RetryNWithBackoff(oneSecondBackoffNoJitter, 3, func() error {
		resp, err := cl.performAppnetRequest(http.MethodGet, adminSocketPath, drainRequest)
		if err != nil {
			logger.Warn("Error invoking Appnet's DrainInboundConnections", logger.Fields{
				"adminSocketPath": adminSocketPath,
				field.Error:       err,
			})
			return err
		}
		defer resp.Body.Close()
		return nil
	})
}

func (cl *AppNetAgentClient) performAppnetRequest(method, udsPath, url string) (*http.Response, error) {
	ctx := context.WithValue(context.Background(), udsAddressKey, udsPath)
	req, _ := http.NewRequestWithContext(ctx, method, url, nil)
	httpClient := cl.udsHttpClient
	return httpClient.Do(req)
}
