//go:build !llgo
// +build !llgo

package llvm

import (
	"os/exec"
	"strings"
	"testing"
)

func TestGetTargetTriple(t *testing.T) {
	// Get the list of supported architectures from clang
	cmd := exec.Command("clang", "--print-targets")
	output, err := cmd.Output()
	if err != nil {
		t.Fatalf("Failed to run clang --print-targets: %v", err)
	}

	// Parse the output to get the list of supported architectures
	supportedArchs := make(map[string]bool)
	lines := strings.Split(string(output), "\n")
	for _, line := range lines {
		line = strings.TrimSpace(line)
		if line != "" && !strings.HasPrefix(line, "Registered Targets:") {
			// Extract the architecture from the line
			parts := strings.SplitN(line, " - ", 2)
			if len(parts) > 0 {
				arch := strings.TrimSpace(parts[0])
				supportedArchs[arch] = true
			}
		}
	}

	if len(supportedArchs) == 0 {
		t.Fatal("No supported architectures found from clang --print-targets")
	}

	t.Logf("Found %d supported architectures from clang", len(supportedArchs))

	// Map our architecture names to clang's architecture names
	clangArchMap := map[string][]string{
		"x86_64":  {"x86-64", "x86_64"},
		"i386":    {"x86", "i386"},
		"aarch64": {"aarch64", "arm64"},
		"arm64":   {"arm64", "aarch64"},
		"armv7":   {"arm", "thumb"},
		"wasm32":  {"wasm32"},
	}

	// Define a function to check if the architecture is supported by clang
	isArchSupported := func(archPart string) (bool, string) {
		if mappedArchs, ok := clangArchMap[archPart]; ok {
			for _, mappedArch := range mappedArchs {
				if supportedArchs[mappedArch] {
					return true, mappedArch
				}
			}
		} else if supportedArchs[archPart] {
			// Direct match
			return true, archPart
		}
		return false, ""
	}

	// Define a function to verify OS name
	isOSValid := func(os, goos string) bool {
		validOSMap := map[string][]string{
			"linux":   {"linux", "linux-gnu"},
			"darwin":  {"macosx", "darwin"},
			"windows": {"windows", "win32"},
			"wasip1":  {"wasip1", "wasi"},
			"js":      {"js", "javascript"},
		}

		if validVariants, ok := validOSMap[goos]; ok {
			for _, validVariant := range validVariants {
				if strings.HasPrefix(os, validVariant) {
					return true
				}
			}
		}
		return false
	}

	// Define a function to check if vendor is valid
	isVendorValid := func(vendor string) bool {
		validVendors := map[string]bool{
			"unknown": true,
			"apple":   true,
			"pc":      true,
			"ibm":     true,
		}
		return validVendors[vendor]
	}

	// Define the test function
	checkTriple := func(t *testing.T, testName, goos, goarch, expected string) {
		t.Helper()
		got := GetTargetTriple(goos, goarch)

		// Check if the generated triple matches the expected value
		if got != expected {
			t.Errorf("getTargetTriple(%q, %q) = %q, want %q",
				goos, goarch, got, expected)
		}

		// Extract the architecture part from the triple (first component)
		parts := strings.Split(got, "-")
		if len(parts) < 3 {
			t.Errorf("Invalid target triple format: %s, should have at least 3 components", got)
			return
		}

		archPart := parts[0]
		vendor := parts[1]
		os := parts[2]

		// Check if the architecture is supported by clang
		supported, mappedArch := isArchSupported(archPart)
		if supported {
			t.Logf("Architecture %s (mapped to %s) is supported by clang", archPart, mappedArch)
		} else {
			t.Logf("WARNING: Architecture %s from triple %q for %s/%s not found in clang's supported architectures",
				archPart, got, goos, goarch)
		}

		// Verify vendor
		if !isVendorValid(vendor) {
			t.Errorf("Invalid vendor in triple: %s", vendor)
		}

		// Verify OS
		if !isOSValid(os, goos) {
			t.Errorf("OS in triple %q doesn't match expected OS %q", os, goos)
		}
	}

	// Run tests for different OS/arch combinations
	checkTriple(t, "wasip1/wasm", "wasip1", "wasm", "wasm32-unknown-wasip1")
	checkTriple(t, "linux/amd64", "linux", "amd64", "x86_64-unknown-linux")
	checkTriple(t, "linux/386", "linux", "386", "i386-unknown-linux")
	checkTriple(t, "linux/arm64", "linux", "arm64", "aarch64-unknown-linux")
	checkTriple(t, "linux/arm", "linux", "arm", "armv7-unknown-linux")
	checkTriple(t, "darwin/amd64", "darwin", "amd64", "x86_64-apple-macosx")
	checkTriple(t, "darwin/arm64", "darwin", "arm64", "arm64-apple-macosx")
	checkTriple(t, "windows/amd64", "windows", "amd64", "x86_64-unknown-windows")
	checkTriple(t, "windows/386", "windows", "386", "i386-unknown-windows")
	checkTriple(t, "js/wasm", "js", "wasm", "wasm32-unknown-js")
}
