package mcp_test

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"
	"github.com/yan1h/agent-coding-workflow/internal/mcp"
	"github.com/yan1h/agent-coding-workflow/internal/user"
)

type fakeUserSvc struct{ users map[uuid.UUID]*user.User }

func (s *fakeUserSvc) Login(_ context.Context, _, _, _ string) (string, *user.User, error) {
	return "", nil, nil
}
func (s *fakeUserSvc) Logout(_ context.Context, _ string) error { return nil }
func (s *fakeUserSvc) ResolveSession(_ context.Context, _ string) (uuid.UUID, bool, error) {
	return uuid.Nil, false, nil
}
func (s *fakeUserSvc) Get(_ context.Context, id uuid.UUID) (*user.User, error) {
	if u, ok := s.users[id]; ok {
		return u, nil
	}
	return nil, errs.New(errs.CodeNotFound, "user")
}
func (s *fakeUserSvc) Bootstrap(_ context.Context, _, _ string) (*user.User, error) {
	return nil, nil
}
func (s *fakeUserSvc) ListAdmins(_ context.Context) ([]*user.User, error) { return nil, nil }

func (s *fakeUserSvc) ListUsers(_ context.Context) ([]*user.User, error) { return nil, nil }
func (s *fakeUserSvc) CreateUser(_ context.Context, _ user.CreateUserInput) (*user.User, error) {
	return nil, nil
}
func (s *fakeUserSvc) UpdateProfile(_ context.Context, _ uuid.UUID, _ string) (*user.User, error) {
	return nil, nil
}
func (s *fakeUserSvc) SetAdmin(_ context.Context, _, _ uuid.UUID, _ bool) (*user.User, error) {
	return nil, nil
}
func (s *fakeUserSvc) SetEnabled(_ context.Context, _, _ uuid.UUID, _ bool) (*user.User, error) {
	return nil, nil
}
func (s *fakeUserSvc) AdminResetPassword(_ context.Context, _ uuid.UUID) (string, error) {
	return "", nil
}
func (s *fakeUserSvc) ChangePassword(_ context.Context, _ uuid.UUID, _, _ string) error { return nil }
func (s *fakeUserSvc) DeleteUser(_ context.Context, _, _ uuid.UUID) error               { return nil }

func ctxWithSession(s *mcp.AuthSession) context.Context {
	return context.WithValue(context.Background(), mcp.AuthContextKey, s)
}

func TestCallerResolver_Admin(t *testing.T) {
	t.Parallel()
	uid := uuid.New()
	uSvc := &fakeUserSvc{users: map[uuid.UUID]*user.User{
		uid: {ID: uid, IsAdmin: true},
	}}
	r := mcp.NewCallerResolver(uSvc)

	c, err := r.Resolve(ctxWithSession(&mcp.AuthSession{
		UserID: uid.String(), TokenID: uuid.NewString(),
	}))
	require.NoError(t, err)
	assert.True(t, c.IsAdmin)
	assert.Equal(t, uid, c.UserID)
}

func TestCallerResolver_NonAdmin(t *testing.T) {
	t.Parallel()
	uid := uuid.New()
	uSvc := &fakeUserSvc{users: map[uuid.UUID]*user.User{
		uid: {ID: uid, IsAdmin: false},
	}}
	r := mcp.NewCallerResolver(uSvc)

	c, err := r.Resolve(ctxWithSession(&mcp.AuthSession{
		UserID: uid.String(), TokenID: uuid.NewString(),
	}))
	require.NoError(t, err)
	assert.False(t, c.IsAdmin)
}

func TestCallerResolver_NoSession(t *testing.T) {
	t.Parallel()
	r := mcp.NewCallerResolver(&fakeUserSvc{})

	_, err := r.Resolve(context.Background())
	ae, ok := errs.As(err)
	require.True(t, ok)
	assert.Equal(t, errs.CodeInternal, ae.Code)
}

func TestCallerResolver_UserDeleted(t *testing.T) {
	t.Parallel()
	uid := uuid.New()
	r := mcp.NewCallerResolver(&fakeUserSvc{users: map[uuid.UUID]*user.User{}})

	_, err := r.Resolve(ctxWithSession(&mcp.AuthSession{
		UserID: uid.String(), TokenID: uuid.NewString(),
	}))
	ae, ok := errs.As(err)
	require.True(t, ok)
	assert.Equal(t, errs.CodeNotFound, ae.Code)
}
