package mcp_test

import (
	"go/parser"
	"go/token"
	"io/fs"
	"os"
	"path/filepath"
	"strconv"
	"testing"

	"github.com/stretchr/testify/require"
	"golang.org/x/mod/modfile"
)

const sdkImportPath = "github.com/modelcontextprotocol/go-sdk"

func TestSDKImportIsConfinedToMCPRuntime(t *testing.T) {
	t.Parallel()

	root := repositoryRoot(t)
	allowedRoot := filepath.Join(
		root,
		"internal",
		"extension",
		"runner",
		"mcp",
	)
	skippedDirectories := map[string]bool{
		".git":         true,
		".worktrees":   true,
		"desktop":      true,
		"node_modules": true,
		"vendor":       true,
		"web":          true,
		"x":            true,
	}
	var violations []string
	require.NoError(t, filepath.WalkDir(
		root,
		func(path string, entry fs.DirEntry, walkErr error) error {
			if walkErr != nil {
				return walkErr
			}
			if entry.IsDir() {
				if path != root && skippedDirectories[entry.Name()] {
					return filepath.SkipDir
				}
				return nil
			}
			if filepath.Ext(path) != ".go" ||
				pathWithin(allowedRoot, path) {
				return nil
			}
			parsed, err := parser.ParseFile(
				token.NewFileSet(),
				path,
				nil,
				parser.ImportsOnly,
			)
			if err != nil {
				return err
			}
			for _, imported := range parsed.Imports {
				value, err := strconv.Unquote(imported.Path.Value)
				if err != nil {
					return err
				}
				if value == sdkImportPath ||
					hasPathPrefix(value, sdkImportPath) {
					relative, _ := filepath.Rel(root, path)
					violations = append(violations, filepath.ToSlash(relative))
				}
			}
			return nil
		},
	))
	require.Empty(t, violations,
		"official MCP SDK imports must stay inside runner/mcp")
}

func pathWithin(root, path string) bool {
	relative, err := filepath.Rel(root, path)
	if err != nil || relative == ".." {
		return false
	}
	parentPrefix := ".." + string(filepath.Separator)
	return len(relative) < len(parentPrefix) ||
		relative[:len(parentPrefix)] != parentPrefix
}

func hasPathPrefix(value, prefix string) bool {
	return len(value) > len(prefix) &&
		value[:len(prefix)] == prefix &&
		value[len(prefix)] == '/'
}

func TestSDKDependencyIsDirectAndPinned(t *testing.T) {
	t.Parallel()

	root := repositoryRoot(t)
	data, err := os.ReadFile(filepath.Join(root, "go.mod"))
	require.NoError(t, err)
	module, err := modfile.Parse("go.mod", data, nil)
	require.NoError(t, err)
	for _, replacement := range module.Replace {
		require.NotEqual(t, sdkImportPath, replacement.Old.Path,
			"official MCP SDK pin must not be replaced by a fork or commit")
	}
	for _, required := range module.Require {
		if required.Mod.Path != sdkImportPath {
			continue
		}
		require.Equal(t, "v1.7.0", required.Mod.Version)
		require.False(t, required.Indirect)
		return
	}
	t.Fatalf("%s is not required by the root module", sdkImportPath)
}

func repositoryRoot(t *testing.T) string {
	t.Helper()
	current, err := os.Getwd()
	require.NoError(t, err)
	for {
		if _, err := os.Stat(filepath.Join(current, "go.mod")); err == nil {
			return current
		}
		parent := filepath.Dir(current)
		if parent == current {
			t.Fatal("repository root was not found")
		}
		current = parent
	}
}
