// Copyright 2018 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.

//go:build enablek8sconnector && windows

package k8s

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

	log "github.com/cihub/seelog"
)

const (
	// Represents default path for k8s connector binary in EKS Windows AMIs.
	defaultK8sConnectorBinaryPath = `C:\Program Files\Amazon\EKS\bin\aws-vpc-cni-k8s-connector.exe`

	// Represents Env for log level for k8s connector binary.
	envLogLevel = "VPC_CNI_LOG_LEVEL"
	// Represents default log level for k8s connector binary.
	defaultLogLevel = "info"
)

// GetPodIP retrieves pod IP address using k8s connector binary.
// Output from binary is received over named pipe.
// Create and read operations on named pipe are handled in separate go routine.
// Named pipe path will be passed as argument to k8s connector binary execution.
func GetPodIP(ctx context.Context, podNamespace, podName string) (string, error) {
	// Get new named pipe path.
	pipeName, err := newPipe()
	if err != nil {
		return "", fmt.Errorf("error creating new pipe: %w", err)
	}

	resultChan := make(chan pipeReadResult)

	// Read output from named pipe in separate go routine as accepting and reading connection is blocking operation.
	go readResultFromPipe(ctx, pipeName, resultChan)

	// Executing k8s connector binary in main routine. binary will write output to named pipe.
	err = executeK8sConnector(ctx, podNamespace, podName, pipeName)
	if err != nil {
		return "", fmt.Errorf("error executing k8s connector: %w", err)
	}

	// Get output from named pipe using result chan.
	var result pipeReadResult
	select {
	// Check if context timed out.
	case <-ctx.Done():
		return "", fmt.Errorf("error getting output from pipe: %w", ctx.Err())
	case result = <-resultChan:
	}

	if result.error != nil {
		return "", fmt.Errorf("error reading output from pipe: %w", result.error)
	}

	log.Debugf("Got pod IP address %s for pod %s in namespace %s", result.output, podName, podNamespace)
	return result.output, nil
}

// executeK8sConnector executes aws-vpc-cni-k8s-connector binary to get pod IP address.
// Output from binary is received over named pipe. Execution logs from binary are returned over stdout.
func executeK8sConnector(ctx context.Context, podNamespace string, podName string, pipe string) error {
	// Prepare command to execute binary with required args.
	cmd := exec.CommandContext(ctx, getK8sConnectorBinaryPath(),
		"-pod-name", podName, "-pod-namespace", podNamespace,
		"-pipe", pipe, "-log-level", getK8sConnectorLogLevel())
	log.Debugf("Executing cmd %s to get pod IP address", cmd.String())

	// Setting Stderr for command to receive complete error.
	var errBytes bytes.Buffer
	cmd.Stderr = &errBytes

	output, err := cmd.Output()

	log.Infof("Logs from k8s connector binary...\n")
	log.Infof("%s\n", string(output))
	log.Infof("End of k8s connector binary logs\n")

	if err != nil {
		return fmt.Errorf("error executing connector binary: %w with execution error: %s", err, errBytes.String())
	}

	return nil
}

// getK8sConnectorBinaryPath returns path to k8s connector binary.
func getK8sConnectorBinaryPath() string {
	return defaultK8sConnectorBinaryPath
}

// getK8sConnectorLogLevel returns the log level for k8s connector binary.
func getK8sConnectorLogLevel() string {
	logLevel := os.Getenv(envLogLevel)
	if logLevel == "" {
		logLevel = defaultLogLevel
	}
	return logLevel
}
