package mcp

import (
	"encoding/json"
	"testing"

	"github.com/google/uuid"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

// Scope 的 JSON round-trip 必须保留 nil-vs-empty 语义：
//   - nil Tools/ProjectIDs = "允许全部"
//   - empty slice          = "全部禁止"
// repository.go 的 scopeToJSON / scopeFromJSON 是 encoding/json 的薄封装，
// 这里直接测公开类型的 marshal/unmarshal 路径，不耦合具体 helper 实现。

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

	raw, err := json.Marshal(Scope{})
	require.NoError(t, err)
	assert.JSONEq(t, "{}", string(raw), "empty Scope should marshal to {} via omitempty")

	var got Scope
	require.NoError(t, json.Unmarshal(raw, &got))
	assert.Nil(t, got.Tools, "Tools must remain nil — meaning 'allow all'")
	assert.Nil(t, got.ProjectIDs, "ProjectIDs must remain nil — meaning 'allow all'")
}

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

	pid := uuid.New()
	in := Scope{
		Tools:      []string{"list_issues", "create_issue"},
		ProjectIDs: []uuid.UUID{pid},
	}
	raw, err := json.Marshal(in)
	require.NoError(t, err)

	var out Scope
	require.NoError(t, json.Unmarshal(raw, &out))
	assert.Equal(t, in.Tools, out.Tools)
	assert.Equal(t, in.ProjectIDs, out.ProjectIDs)
}

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

	var nilScope *Scope
	assert.True(t, nilScope.AllowsTool("anything"), "nil receiver allows all")

	zero := &Scope{} // Tools/ProjectIDs both nil
	assert.True(t, zero.AllowsTool("anything"), "nil Tools allows all")

	empty := &Scope{Tools: []string{}}
	assert.False(t, empty.AllowsTool("anything"), "empty Tools forbids all")

	allow := &Scope{Tools: []string{"list_issues"}}
	assert.True(t, allow.AllowsTool("list_issues"))
	assert.False(t, allow.AllowsTool("create_issue"))
}

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

	pidA := uuid.New()
	pidB := uuid.New()

	var nilScope *Scope
	assert.True(t, nilScope.AllowsProject(pidA))

	zero := &Scope{}
	assert.True(t, zero.AllowsProject(pidA), "nil ProjectIDs allows all")

	empty := &Scope{ProjectIDs: []uuid.UUID{}}
	assert.False(t, empty.AllowsProject(pidA), "empty ProjectIDs forbids all")

	allow := &Scope{ProjectIDs: []uuid.UUID{pidA}}
	assert.True(t, allow.AllowsProject(pidA))
	assert.False(t, allow.AllowsProject(pidB))
}
