package exec

import (
	"go/ast"
	"go/parser"
	"go/token"
	"os"
	"path/filepath"
	"reflect"
	"strconv"
	"strings"
	"testing"

	"github.com/stretchr/testify/require"
)

func TestExecProductionImportsOnlyOwnedAPIDTOs(t *testing.T) {
	for path, file := range parseExecProductionFiles(t) {
		for _, imported := range file.Imports {
			importPath, err := strconv.Unquote(imported.Path.Value)
			require.NoError(t, err)
			require.Falsef(
				t,
				isSiblingAPIHandlerImport(importPath),
				"%s imports sibling API implementation %s",
				path,
				importPath,
			)
			require.NotEqualf(t, "github.com/tidbcloud/AgentTeam/desktop/internal/profile", importPath, "%s imports global profile state", path)
			require.NotEqualf(t, "github.com/tidbcloud/AgentTeam/desktop/internal/api/dto", importPath, "%s imports retired Agent DTOs", path)
		}
	}
}

func TestAgentProjectionHasOneJSONTaggedOwner(t *testing.T) {
	var owners []string
	err := filepath.Walk("../..", func(path string, info os.FileInfo, err error) error {
		require.NoError(t, err)
		if info.IsDir() || filepath.Ext(path) != ".go" || strings.HasSuffix(path, "_test.go") {
			return nil
		}
		file, parseErr := parser.ParseFile(token.NewFileSet(), path, nil, 0)
		require.NoError(t, parseErr)
		ast.Inspect(file, func(node ast.Node) bool {
			value, ok := node.(*ast.StructType)
			if !ok || value.Fields == nil {
				return true
			}
			tags := make(map[string]struct{}, len(value.Fields.List))
			for _, field := range value.Fields.List {
				if field.Tag != nil {
					tag := strings.Trim(field.Tag.Value, "`")
					if value := reflect.StructTag(tag).Get("json"); value != "" {
						tags[strings.Split(value, ",")[0]] = struct{}{}
					}
				}
			}
			if containsAgentSnapshotTags(tags) {
				owners = append(owners, path)
			}
			return true
		})
		return nil
	})
	require.NoError(t, err)
	require.Equal(t, []string{"../../agentview/snapshot.go"}, owners)
}

func containsAgentSnapshotTags(tags map[string]struct{}) bool {
	for _, name := range []string{
		"id", "runtime", "headers", "runtimeType", "status",
		"backendRegistered", "source", "sourceType", "sourceName",
	} {
		if _, ok := tags[name]; !ok {
			return false
		}
	}
	return true
}

func TestRetiredAgentSourceFilesStayDeleted(t *testing.T) {
	for _, path := range []string{"../exec_agent_source.go", "../exec_agent_source_test.go"} {
		_, err := os.Stat(path)
		require.ErrorIs(t, err, os.ErrNotExist, path)
	}
}

func TestSiblingAPIHandlerImportClassification(t *testing.T) {
	for _, importPath := range []string{
		"github.com/tidbcloud/AgentTeam/desktop/internal/api/dashboard",
		"github.com/tidbcloud/AgentTeam/desktop/internal/api/mcp",
		"github.com/tidbcloud/AgentTeam/desktop/internal/api/dashboard/nested",
	} {
		require.True(t, isSiblingAPIHandlerImport(importPath), importPath)
	}
	require.False(t, isSiblingAPIHandlerImport("github.com/tidbcloud/AgentTeam/desktop/internal/api/dto"))
	require.False(t, isSiblingAPIHandlerImport("github.com/tidbcloud/AgentTeam/desktop/internal/api/exec"))
}

func isSiblingAPIHandlerImport(importPath string) bool {
	const apiPrefix = "github.com/tidbcloud/AgentTeam/desktop/internal/api/"
	if !strings.HasPrefix(importPath, apiPrefix) {
		return false
	}
	remainder := importPath[len(apiPrefix):]
	return remainder != "dto" && remainder != "exec"
}
