package chat

import (
	"context"
	"testing"

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

	"github.com/yan1h/agent-coding-workflow/internal/infra/errs"
)

// tplFakeRepo embeds fakeRepo and overrides prompt template methods.
type tplFakeRepo struct {
	*fakeRepo
	templates map[uuid.UUID]*PromptTemplate
}

func newTplFakeRepo() *tplFakeRepo {
	return &tplFakeRepo{fakeRepo: newFakeRepo(), templates: map[uuid.UUID]*PromptTemplate{}}
}

func (r *tplFakeRepo) GetPromptTemplate(_ context.Context, id uuid.UUID) (*PromptTemplate, error) {
	return r.templates[id], nil // 未命中返回 (nil, nil)，与真实 repo 语义一致
}
func (r *tplFakeRepo) UpdatePromptTemplate(_ context.Context, id uuid.UUID, name, content string) error {
	t, ok := r.templates[id]
	if !ok {
		return errs.New(errs.CodeChatTemplateNotFound, "template not found")
	}
	t.Name, t.Content = name, content
	return nil
}
func (r *tplFakeRepo) DeletePromptTemplate(_ context.Context, id uuid.UUID) error {
	delete(r.templates, id)
	return nil
}

func newTplSvc(repo *tplFakeRepo) TemplateService {
	return NewTemplateService(repo, noopAuditor{}, discardLogger())
}

func addTplFixture(repo *tplFakeRepo, scope TemplateScope, owner *uuid.UUID) *PromptTemplate {
	t := &PromptTemplate{ID: uuid.New(), Name: "tpl", Content: "c", Scope: scope, OwnerID: owner}
	repo.templates[t.ID] = t
	return t
}

func requireForbidden(t *testing.T, err error) {
	t.Helper()
	ae, ok := errs.As(err)
	require.True(t, ok)
	assert.Equal(t, errs.CodeForbidden, ae.Code)
}

func TestTemplateUpdate_SystemScopeRequiresAdmin(t *testing.T) {
	repo := newTplFakeRepo()
	svc := newTplSvc(repo)
	sys := addTplFixture(repo, TemplateScopeSystem, nil)
	uid := uuid.New()

	// 非 admin → 拒绝
	err := svc.Update(context.Background(), uid, false, sys.ID, "x", "y")
	requireForbidden(t, err)
	assert.Equal(t, "tpl", repo.templates[sys.ID].Name)

	// admin → 允许
	require.NoError(t, svc.Update(context.Background(), uid, true, sys.ID, "x", "y"))
	assert.Equal(t, "x", repo.templates[sys.ID].Name)
}

func TestTemplateDelete_SystemScopeRequiresAdmin(t *testing.T) {
	repo := newTplFakeRepo()
	svc := newTplSvc(repo)
	sys := addTplFixture(repo, TemplateScopeSystem, nil)
	uid := uuid.New()

	err := svc.Delete(context.Background(), uid, false, sys.ID)
	requireForbidden(t, err)
	assert.Contains(t, repo.templates, sys.ID)

	require.NoError(t, svc.Delete(context.Background(), uid, true, sys.ID))
	assert.NotContains(t, repo.templates, sys.ID)
}

func TestTemplateUpdateDelete_UserScopeOwnerOnly(t *testing.T) {
	repo := newTplFakeRepo()
	svc := newTplSvc(repo)
	owner := uuid.New()
	tpl := addTplFixture(repo, TemplateScopeUser, &owner)
	stranger := uuid.New()

	// 非 owner（即使 admin）→ 拒绝，保持既有 owner-only 语义
	requireForbidden(t, svc.Update(context.Background(), stranger, true, tpl.ID, "x", "y"))
	requireForbidden(t, svc.Delete(context.Background(), stranger, true, tpl.ID))

	// owner → 允许
	require.NoError(t, svc.Update(context.Background(), owner, false, tpl.ID, "x", "y"))
	require.NoError(t, svc.Delete(context.Background(), owner, false, tpl.ID))
	assert.NotContains(t, repo.templates, tpl.ID)
}
