// Copyright 2022 schukai GmbH
// SPDX-License-Identifier: AGPL-3.0

package configuration

import (
	"github.com/stretchr/testify/assert"
	"os"
	"path"
	"path/filepath"
	"testing"
)

type Issue7Routing struct {
	P PathValue `json:"p" yaml:"p"`
	X string    `json:"x" yaml:"x"`
}

type Issue7Server struct {
	Routing []Issue7Routing `json:"routing" yaml:"routing"`
}

type Issue7Config struct {
	Server Issue7Server `json:"server" yaml:"server"`
}

func createIssue7TempFile(content string) (string, error) {
	file, err := os.CreateTemp("", "tempfile")
	if err != nil {
		return "", err
	}
	defer func() {
		_ = file.Close()
	}()

	_, err = file.WriteString(content)
	if err != nil {
		return "", err
	}

	return file.Name(), nil
}

func TestPathRewrite(t *testing.T) {

	c := New(Issue7Config{})

	n, err := createIssue7TempFile(`{
		"server": {
			"routing": [
				{
					"p": "./test",
                    "x": "testX"
				}
			]
		}	
	}`)

	if err != nil {
		t.Fatal(err)
	}

	c.SetMnemonic("my-app")
	c.AddFile(n)
	c.Import()

	_ = os.Remove(n)

	//fmt.Println(c.Config().Host)
	expected := path.Join(filepath.Dir(n), "test")

	assert.Equal(t, expected, c.Config().Server.Routing[0].P.String())
	assert.Equal(t, "testX", c.Config().Server.Routing[0].X)

}

// Test data structs
type Issue7TestStruct1 struct {
	A PathValue
	B string
	C int
}

type Issue7TestStruct2 struct {
	A PathValue
	B Issue7TestStruct1
	C []PathValue
	D map[string]PathValue
	E string
}

func TestIssue7ReplacePath(t *testing.T) {
	basePath := "/basepath"

	// Test case 1
	ts1 := Issue7TestStruct1{
		A: "relative/path",
		B: "justastring",
		C: 42,
	}

	err := replacePath[*Issue7TestStruct1](basePath, &ts1)
	assert.Nil(t, err)

	if ts1.A != PathValue(path.Join(basePath, "relative/path")) {
		t.Errorf("Expected '%s', got '%s'", path.Join(basePath, "relative/path"), ts1.A)
	}

	// Test case 2
	ts2 := Issue7TestStruct2{
		A: "another/relative/path",
		B: ts1,
		C: []PathValue{"rel1", "rel2"},
		D: map[string]PathValue{
			"key1": "relkey1",
			"key2": "relkey2",
		},
		E: "justastring",
	}

	err = replacePath[*Issue7TestStruct2](basePath, &ts2)
	assert.Nil(t, err)

	if ts2.A != PathValue(path.Join(basePath, "another/relative/path")) {
		t.Errorf("Expected '%s', got '%s'", path.Join(basePath, "another/relative/path"), ts2.A)
	}
	if ts2.B.A != PathValue(path.Join(basePath, "relative/path")) {
		t.Errorf("Expected '%s', got '%s'", path.Join(basePath, "relative/path"), ts2.B.A)
	}
	if ts2.C[0] != PathValue(path.Join(basePath, "rel1")) {
		t.Errorf("Expected '%s', got '%s'", path.Join(basePath, "rel1"), ts2.C[0])
	}
	if ts2.C[1] != PathValue(path.Join(basePath, "rel2")) {
		t.Errorf("Expected '%s', got '%s'", path.Join(basePath, "rel2"), ts2.C[1])
	}
	if ts2.D["key1"] != PathValue(path.Join(basePath, "relkey1")) {
		t.Errorf("Expected '%s', got '%s'", path.Join(basePath, "relkey1"), ts2.D["key1"])
	}
	if ts2.D["key2"] != PathValue(path.Join(basePath, "relkey2")) {
		t.Errorf("Expected '%s', got '%s'", path.Join(basePath, "relkey2"), ts2.D["key2"])
	}
	if ts2.E != "justastring" {
		t.Errorf("Expected '%s', got '%s'", "justastring", ts2.E)
	}
}

type DeepNestedStruct struct {
	A PathValue
	B string
	C *PathValue
	D []PathValue
	E map[string]PathValue
	F interface{}
	G *DeepNestedStruct
	H []DeepNestedStruct
	I map[string]DeepNestedStruct
}

type MyStruct struct {
	A PathValue
	B string
	C *PathValue
	D []PathValue
	E map[string]PathValue
	F interface{}
	G DeepNestedStruct
	H []DeepNestedStruct
	I map[string]DeepNestedStruct
}

func TestReplacePath(t *testing.T) {
	// Sample seed data to guide the fuzzer
	tempPathValueC := PathValue("rel/pathC")
	tempPathValueF := PathValue("rel/pathF")
	tempPathValueGC := PathValue("rel/pathGC")

	s := &MyStruct{
		A: "rel/pathA",
		B: "normalStringB",
		C: &tempPathValueC,
		D: []PathValue{"rel1D", "rel2D"},
		E: map[string]PathValue{
			"key1E": "relkey1E",
			"key2E": "relkey2E",
		},
		F: tempPathValueF,
		G: DeepNestedStruct{
			A: "rel/pathGA",
			B: "normalStringGB",
			C: &tempPathValueGC,
			D: []PathValue{"rel1GD", "rel2GD"},
			E: map[string]PathValue{
				"key1GE": "relkey1GE",
				"key2GE": "relkey2GE",
			},
			F: tempPathValueF,
			H: []DeepNestedStruct{
				{
					A: "rel/pathGHA",
				},
			},
			I: map[string]DeepNestedStruct{
				"key1GI": {
					A: "rel/pathGIA",
				},
			},
		},
		H: []DeepNestedStruct{
			{
				A: "rel/pathHA",
			},
		},
		I: map[string]DeepNestedStruct{
			"key1I": {
				A: "rel/pathIA",
			},
		},
	}

	basePath := "/basepath"
	// Copy the struct to compare later
	original := *s

	err := replacePath[*MyStruct](basePath, s)
	assert.Nil(t, err)

	// Checking each field to ensure replacePath works as expected
	if s.A != original.A && !path.IsAbs(s.A.String()) {
		t.Errorf("Path not replaced correctly for field A")
	}
	if s.C != nil && *s.C != *original.C && !path.IsAbs(s.C.String()) {
		t.Errorf("Path not replaced correctly for field C")
	}
	for idx, val := range s.D {
		if val != original.D[idx] && !path.IsAbs(val.String()) {
			t.Errorf("Path not replaced correctly for field D at index %d", idx)
		}
	}
	for key, val := range s.E {
		if val != original.E[key] && !path.IsAbs(val.String()) {
			t.Errorf("Path not replaced correctly for field E with key %s", key)
		}
	}
	if s.F != original.F && !path.IsAbs(s.F.(PathValue).String()) {
		t.Errorf("Path not replaced correctly for field F")
	}
	// Check fields of the nested struct (G) similarly...
	// Continue this for all fields and nested fields of MyStruct
}