package auth

import (
	"context"
	"errors"
	"testing"
	"time"

	"github.com/stretchr/testify/require"
	"gorm.io/gorm"

	"security-center-backend/internal/config"
	"security-center-backend/internal/model"
	"security-center-backend/internal/repository"
	"security-center-backend/internal/service/jwt"
)

type fakeUserRepository struct {
	users map[string]*model.User
}

func (f *fakeUserRepository) FindByID(ctx context.Context, id uint64) (*model.User, error) {
	for _, user := range f.users {
		if user != nil && user.ID == id {
			copyUser := *user
			return &copyUser, nil
		}
	}
	return nil, errors.New("not found")
}

func (f *fakeUserRepository) FindByAuth0ID(ctx context.Context, auth0UserID string) (*model.User, error) {
	return nil, errors.New("not implemented")
}

func (f *fakeUserRepository) FindByExternalID(ctx context.Context, externalID string) (*model.User, error) {
	return nil, errors.New("not implemented")
}

func (f *fakeUserRepository) FindByEmail(ctx context.Context, email string) (*model.User, error) {
	if f == nil {
		return nil, errors.New("not found")
	}
	if user, ok := f.users[email]; ok && user != nil {
		copyUser := *user
		return &copyUser, nil
	}
	return nil, gorm.ErrRecordNotFound
}

func (f *fakeUserRepository) FindByEmails(ctx context.Context, emails []string) ([]model.User, error) {
	return nil, errors.New("not implemented")
}

func (f *fakeUserRepository) CreateOrUpdate(ctx context.Context, user *model.User) error {
	if f.users == nil {
		f.users = map[string]*model.User{}
	}
	copyUser := *user
	if copyUser.ID == 0 {
		copyUser.ID = 1
	}
	f.users[copyUser.Email] = &copyUser
	user.ID = copyUser.ID
	return nil
}

func (f *fakeUserRepository) List(ctx context.Context, filter repository.UserFilter, page, pageSize int) ([]model.User, int64, error) {
	return nil, 0, errors.New("not implemented")
}

func (f *fakeUserRepository) AssignRoles(ctx context.Context, userID uint64, roleIDs []uint64) error {
	return errors.New("not implemented")
}

func (f *fakeUserRepository) RemoveRole(ctx context.Context, userID uint64, roleID uint64) error {
	return errors.New("not implemented")
}

func (f *fakeUserRepository) GetUserRoles(ctx context.Context, userID uint64) ([]model.Role, error) {
	return nil, errors.New("not implemented")
}

func (f *fakeUserRepository) GetUserPermissions(ctx context.Context, userID uint64) ([]model.Permission, error) {
	return nil, errors.New("not implemented")
}

func (f *fakeUserRepository) UpdateDefaultRole(ctx context.Context, userID uint64, roleID *uint64) error {
	return errors.New("not implemented")
}

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

	svc := &Service{
		cfg: config.AuthConfig{
			DevUser: config.DevUserConfig{
				ID:    "dev-user",
				Email: "dev.user@example.com",
				Name:  "Development User",
				Roles: []string{"admin"},
			},
		},
		jwtManager: jwt.NewManager(config.JWTConfig{
			AccessSecret:       "access-secret",
			RefreshSecret:      "refresh-secret",
			AccessTokenTTL:     time.Hour,
			RefreshTokenTTL:    time.Hour,
			SigningAlgorithm:   "HS256",
			Issuer:             "security-center",
			ClockSkewInSeconds: 0,
		}),
		userRepo: &fakeUserRepository{
			users: map[string]*model.User{
				"dev.user@example.com": {
					Base:             model.Base{ID: 1},
					ExternalID:       "dev-user",
					Email:            "dev.user@example.com",
					Name:             "Development User",
					Status:           model.UserStatusActive,
					IdentityProvider: "dev",
				},
			},
		},
	}

	session, err := svc.GenerateDevSession(context.Background())
	require.NoError(t, err)
	require.NotNil(t, session)
	require.Equal(t, "1", session.User.ID)
	require.Equal(t, "dev.user@example.com", session.User.Email)
	require.Contains(t, session.User.Permissions, "caasm:export_ports")

	claims, err := svc.jwtManager.Validate(session.AccessToken)
	require.NoError(t, err)
	require.Equal(t, uint64(1), claims.UserID)
	require.Equal(t, "dev.user@example.com", claims.Email)
}
