package integration_tests

import (
	"bytes"
	"encoding/json"
	"fmt"

	"io"
	"net/http"
	"os"
	"testing"

	"code.fbi.h-da.de/danet/quant/goKMS/config"
	kmstls "code.fbi.h-da.de/danet/quant/goKMS/kms/tls"
	utils "code.fbi.h-da.de/danet/quant/integration-tests/code/integrationTestUtils"
	"github.com/google/uuid"
	"github.com/stretchr/testify/assert"
)

// For log file.
type LogFile struct {
	Source string            `json:"source"`
	Body   PushKSAKeyRequest `json:"body"`
}
type PushKSAKeyRequest struct {
	RequestID string   `json:"request_ID"`
	ProcessID string   `json:"process_ID"`
	KSAKeys   []KSAKey `json:"ksa_keys"`
}

type KSAKey struct {
	KeyID string `json:"key_ID"`
	Key   string `json:"key"`
}

// For request.
type KeyProperties struct {
	Number    int `json:"number"`
	KeyLength int `json:"key_length"`
	Timeout   int `json:"timeout"`
	TTL       int `json:"TTL"`
}

type RequestData struct {
	ReceivingCKMSID string        `json:"receiving_CKMS_ID"`
	RequestID       string        `json:"request_ID"`
	KeyProperties   KeyProperties `json:"key_properties"`
}

func TestGetKSAKey(t *testing.T) { //nolint:gocyclo
	gosdncScript := "../../config/gosdnc/add_devices.sh"
	gosdncScript_ENV := os.Getenv("INTEGRATION_TEST_GOSDNC_SCRIPT")
	if gosdncScript_ENV != "" {
		gosdncScript = gosdncScript_ENV
	}
	cleanupScript := "../../config/gosdnc/delete_devices.sh"
	cleanupScript_ENV := os.Getenv("INTEGRATION_TEST_CLEANUP_SCRIPT")
	if cleanupScript_ENV != "" {
		cleanupScript = cleanupScript_ENV
	}
	controllerURL := "127.0.0.1:55055"
	controllerURL_ENV := os.Getenv("INTEGRATION_TEST_CONTROLLER_URL")
	if controllerURL_ENV != "" {
		controllerURL = controllerURL_ENV
	}
	kms1AkmsURL := "127.0.0.1:9696"
	kms1AkmsURL_ENV := os.Getenv("INTEGRATION_TEST_KMS1_AKMS_URL")
	if kms1AkmsURL_ENV != "" {
		kms1AkmsURL = kms1AkmsURL_ENV
	}
	logFileURL := "127.0.0.1:4444"
	logFileURL_ENV := os.Getenv("INTEGRATION_TEST_LOG_FILE1_URL")
	if logFileURL_ENV != "" {
		logFileURL = logFileURL_ENV
	}
	logFileURL2 := "127.0.0.1:4445"
	logFileURL_ENV2 := os.Getenv("INTEGRATION_TEST_LOG_FILE2_URL")
	if logFileURL_ENV2 != "" {
		logFileURL2 = logFileURL_ENV2
	}

	// Tell the qkdn-controller what devices to use.
	_, err := utils.RunGosdncScript(gosdncScript, controllerURL)
	if err != nil {
		currentFolderPath := os.Getenv("PWD")
		t.Errorf("Error running gosdnc script. Current folder path: %s, Error: %s", currentFolderPath, err)
	}
	defer utils.RunGosdncScript(cleanupScript, controllerURL) //nolint:errcheck

	requestId := uuid.New().String()

	tlsConfig := config.TLSConfig{
		Active:   true,
		CAFile:   "../../../artifacts/integration-tests/ssl/ca.crt",
		CertFile: "../../../artifacts/integration-tests/ssl/kms/kms2-selfsigned.crt",
		KeyFile:  "../../../artifacts/integration-tests/ssl/kms/kms2-selfsigned.key",
	}

	url := fmt.Sprintf("https://%s/api/v1/keys/ksa_key_req", kms1AkmsURL)
	data := RequestData{
		ReceivingCKMSID: "5e41c291-6121-4335-84f6-41e04b8bdaa2",
		RequestID:       requestId,
		KeyProperties: KeyProperties{
			Number:    1,
			KeyLength: 256,
			Timeout:   20,
			TTL:       24,
		},
	}

	tlsConf, err := kmstls.GenerateTLSLibraryConfig(tlsConfig)
	if err != nil {
		t.Errorf("Error generating TLS config: %s", err)
	}
	transport := &http.Transport{
		TLSClientConfig: tlsConf,
	}
	client := &http.Client{Transport: transport}

	jsonData, err := json.Marshal(data)
	if err != nil {
		fmt.Println(err)
		return
	}

	resp, err := client.Post(url, "application/json", bytes.NewBuffer(jsonData))
	if err != nil {
		t.Errorf("Error making HTTP request: %s", err)
		return
	}
	defer resp.Body.Close() //nolint:errcheck

	if resp.StatusCode != http.StatusNoContent {
		t.Errorf("Expected status code 204 No Content, but got %d", resp.StatusCode)
	}

	// Get logfile of akms
	resp, err = client.Get("https://" + logFileURL + "/debug/get_log_file")
	if err != nil {
		t.Errorf("Error making HTTP request: %s", err)
		return
	}
	defer resp.Body.Close() //nolint:errcheck

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		t.Errorf("Error reading response body: %s", err)
		return
	}

	var logFile LogFile
	err = json.Unmarshal(body, &logFile)
	if err != nil {
		t.Errorf("Error parsing logFile answer from AKMS: %s. \n Most likely the AKMS never received a ksa key. It told us: %s", err, string(body))
		return
	}
	assert.NotNil(t, logFile.Source)
	assert.Equal(t, requestId, logFile.Body.RequestID)
	assert.NotNil(t, logFile.Body.ProcessID)
	assert.Equal(t, 1, len(logFile.Body.KSAKeys))
	assert.NotNil(t, logFile.Body.KSAKeys[0].KeyID)
	assert.NotNil(t, logFile.Body.KSAKeys[0].Key)

	tlsConfig = config.TLSConfig{
		Active:   true,
		CAFile:   "../../../artifacts/integration-tests/ssl/ca.crt",
		CertFile: "../../../artifacts/integration-tests/ssl/kms/kms1-selfsigned.crt",
		KeyFile:  "../../../artifacts/integration-tests/ssl/kms/kms1-selfsigned.key",
	}

	tlsConf, err = kmstls.GenerateTLSLibraryConfig(tlsConfig)
	if err != nil {
		t.Errorf("Error generating TLS config: %s", err)
	}
	transport = &http.Transport{
		TLSClientConfig: tlsConf,
	}
	client = &http.Client{Transport: transport}

	resp, err = client.Get("https://" + logFileURL2 + "/debug/get_log_file")
	if err != nil {
		t.Errorf("Error making HTTP request: %s", err)
		return
	}
	defer resp.Body.Close() //nolint:errcheck

	body, err = io.ReadAll(resp.Body)
	if err != nil {
		t.Errorf("Error reading response body: %s", err)
		return
	}
	var logFile2 LogFile

	err = json.Unmarshal(body, &logFile2)
	if err != nil {
		t.Errorf("Error parsing body into PushKSAKeyRequest: %s", err)
		return
	}
	assert.NotNil(t, logFile2.Source)
	assert.Equal(t, requestId, logFile2.Body.RequestID)
	assert.NotNil(t, logFile2.Body.ProcessID)
	assert.Equal(t, 1, len(logFile2.Body.KSAKeys))
	assert.NotNil(t, logFile2.Body.KSAKeys[0].KeyID)
	assert.NotNil(t, logFile2.Body.KSAKeys[0].Key)

	// Check that both log files are identical except for the source.
	assert.NotEqual(t, logFile.Source, logFile2.Source)
	assert.Equal(t, logFile.Body.RequestID, logFile2.Body.RequestID)
	assert.Equal(t, logFile.Body.ProcessID, logFile2.Body.ProcessID)
	assert.Equal(t, logFile.Body.KSAKeys[0].KeyID, logFile2.Body.KSAKeys[0].KeyID)
	assert.Equal(t, logFile.Body.KSAKeys[0].Key, logFile2.Body.KSAKeys[0].Key)
}