package service

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

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

	coremodel "security-center-backend/internal/model"
	"security-center-backend/internal/modules/caasm/mocks"
	caasmmodel "security-center-backend/internal/modules/caasm/model"
	"security-center-backend/internal/modules/caasm/repository"
	corerepo "security-center-backend/internal/repository"
)

type stubDomainRepository struct {
	domain *caasmmodel.DomainAsset
	err    error
}

func (s *stubDomainRepository) List(ctx context.Context, filter repository.DomainFilter, page, pageSize int) ([]caasmmodel.DomainAsset, int64, error) {
	return nil, 0, errors.New("not implemented")
}

func (s *stubDomainRepository) Stats(ctx context.Context, window time.Duration) (*repository.DomainStats, error) {
	return nil, errors.New("not implemented")
}

func (s *stubDomainRepository) FindByID(ctx context.Context, id uint64) (*caasmmodel.DomainAsset, error) {
	if s.err != nil {
		return nil, s.err
	}
	if s.domain == nil || s.domain.ID != id {
		return nil, gorm.ErrRecordNotFound
	}
	return s.domain, nil
}

func (s *stubDomainRepository) GetRegistrars(ctx context.Context) ([]string, error) {
	return nil, errors.New("not implemented")
}

type stubUserRepository struct {
	user *coremodel.User
	err  error
}

func (s *stubUserRepository) FindByID(ctx context.Context, id uint64) (*coremodel.User, error) {
	if s.err != nil {
		return nil, s.err
	}
	if s.user == nil || s.user.ID != id {
		return nil, gorm.ErrRecordNotFound
	}
	return s.user, nil
}

func (s *stubUserRepository) FindByAuth0ID(ctx context.Context, auth0UserID string) (*coremodel.User, error) {
	return nil, errors.New("not implemented")
}

func (s *stubUserRepository) FindByExternalID(ctx context.Context, externalID string) (*coremodel.User, error) {
	return nil, errors.New("not implemented")
}

func (s *stubUserRepository) FindByEmail(ctx context.Context, email string) (*coremodel.User, error) {
	return nil, errors.New("not implemented")
}

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

func (s *stubUserRepository) CreateOrUpdate(ctx context.Context, user *coremodel.User) error {
	return errors.New("not implemented")
}

func (s *stubUserRepository) List(ctx context.Context, filter corerepo.UserFilter, page, pageSize int) ([]coremodel.User, int64, error) {
	return nil, 0, errors.New("not implemented")
}

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

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

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

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

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

func TestDomainServiceClaimUpdatesDomainOwnershipOnly(t *testing.T) {
	ctx := context.Background()
	actorID := uint64(1001)
	ownerID := uint64(2002)
	deptID := "dept-xyz"
	assetOwnerID := uint64(3003)
	assetDeptID := "asset-dept"

	domainRepo := &stubDomainRepository{
		domain: &caasmmodel.DomainAsset{
			Base:            coremodel.Base{ID: 77},
			AssetID:         88,
			DomainName:      "xxx.com",
			DomainClass:     caasmmodel.DomainClassAuthoritativeZone,
			OwnerEmployeeID: nil,
			DepartmentID:    nil,
			Asset: caasmmodel.Asset{
				Base:            coremodel.Base{ID: 88},
				OwnerEmployeeID: &assetOwnerID,
				DepartmentID:    &assetDeptID,
			},
		},
	}
	userRepo := &stubUserRepository{
		user: &coremodel.User{
			Base:  coremodel.Base{ID: actorID},
			Roles: []coremodel.Role{{Name: "admin"}},
		},
	}
	assetRepo := new(mocks.MockAssetRepository)
	svc := NewDomainService(domainRepo, assetRepo, userRepo)

	assetRepo.On("UpdateDomainOwner", ctx, uint64(77), &ownerID, &deptID).Return(nil).Once()

	err := svc.Claim(ctx, actorID, 77, &ownerID, &deptID)
	require.NoError(t, err)

	require.Nil(t, domainRepo.domain.OwnerEmployeeID)
	require.Nil(t, domainRepo.domain.DepartmentID)
	require.NotNil(t, domainRepo.domain.Asset.OwnerEmployeeID)
	require.Equal(t, assetOwnerID, *domainRepo.domain.Asset.OwnerEmployeeID)
	require.NotNil(t, domainRepo.domain.Asset.DepartmentID)
	require.Equal(t, assetDeptID, *domainRepo.domain.Asset.DepartmentID)

	assetRepo.AssertNotCalled(t, "SaveDomainAsset", mock.Anything, mock.Anything)
	assetRepo.AssertNotCalled(t, "Update", mock.Anything, mock.Anything)
	assetRepo.AssertExpectations(t)
}

func TestDomainServiceClaimRejectsNilOwner(t *testing.T) {
	ctx := context.Background()
	actorID := uint64(1001)

	domainRepo := &stubDomainRepository{
		domain: &caasmmodel.DomainAsset{
			Base:        coremodel.Base{ID: 77},
			AssetID:     88,
			DomainName:  "xxx.com",
			DomainClass: caasmmodel.DomainClassAuthoritativeZone,
			Asset: caasmmodel.Asset{
				Base: coremodel.Base{ID: 88},
			},
		},
	}
	userRepo := &stubUserRepository{
		user: &coremodel.User{
			Base:  coremodel.Base{ID: actorID},
			Roles: []coremodel.Role{{Name: "admin"}},
		},
	}
	assetRepo := new(mocks.MockAssetRepository)
	svc := NewDomainService(domainRepo, assetRepo, userRepo)

	err := svc.Claim(ctx, actorID, 77, nil, nil)
	require.Error(t, err)
	require.Contains(t, err.Error(), "domain owner is required")

	assetRepo.AssertNotCalled(t, "UpdateDomainOwner", mock.Anything, mock.Anything, mock.Anything, mock.Anything)
	assetRepo.AssertNotCalled(t, "SaveDomainAsset", mock.Anything, mock.Anything)
	assetRepo.AssertNotCalled(t, "Update", mock.Anything, mock.Anything)
}
