package mocks

import (
	"context"
	caasmmodel "security-center-backend/internal/modules/caasm/model"
	"security-center-backend/internal/modules/caasm/queue"
	"security-center-backend/internal/modules/caasm/repository"
	"time"

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

// MockJobQueue is a mock implementation of queue.JobQueueInterface
type MockJobQueue struct {
	mock.Mock
}

func (m *MockJobQueue) Enqueue(ctx context.Context, job *queue.SyncJobPayload) error {
	args := m.Called(ctx, job)
	return args.Error(0)
}

func (m *MockJobQueue) Dequeue(ctx context.Context, consumerName string) (*queue.SyncJobPayload, string, error) {
	args := m.Called(ctx, consumerName)
	if args.Get(0) == nil {
		return nil, args.String(1), args.Error(2)
	}
	return args.Get(0).(*queue.SyncJobPayload), args.String(1), args.Error(2)
}

func (m *MockJobQueue) Acknowledge(ctx context.Context, streamID string) error {
	args := m.Called(ctx, streamID)
	return args.Error(0)
}

func (m *MockJobQueue) Fail(ctx context.Context, job *queue.SyncJobPayload, streamID string, err error) error {
	args := m.Called(ctx, job, streamID, err)
	return args.Error(0)
}

func (m *MockJobQueue) GetQueueDepth(ctx context.Context) (int64, error) {
	args := m.Called(ctx)
	return args.Get(0).(int64), args.Error(1)
}

func (m *MockJobQueue) GetDLQDepth(ctx context.Context) (int64, error) {
	args := m.Called(ctx)
	return args.Get(0).(int64), args.Error(1)
}

func (m *MockJobQueue) CleanupStuckJobs(ctx context.Context) error {
	args := m.Called(ctx)
	return args.Error(0)
}

func (m *MockJobQueue) ExistsActiveAccount(ctx context.Context, accountID uint64) bool {
	args := m.Called(ctx, accountID)
	return args.Bool(0)
}

// MockCloudAccountRepository is a mock implementation of repository.CloudAccountRepository
type MockCloudAccountRepository struct {
	mock.Mock
}

func (m *MockCloudAccountRepository) Create(ctx context.Context, account *caasmmodel.CloudAccount) error {
	args := m.Called(ctx, account)
	return args.Error(0)
}

func (m *MockCloudAccountRepository) Update(ctx context.Context, account *caasmmodel.CloudAccount) error {
	args := m.Called(ctx, account)
	return args.Error(0)
}

func (m *MockCloudAccountRepository) Delete(ctx context.Context, id uint64) error {
	args := m.Called(ctx, id)
	return args.Error(0)
}

func (m *MockCloudAccountRepository) FindByID(ctx context.Context, id uint64) (*caasmmodel.CloudAccount, error) {
	args := m.Called(ctx, id)
	if args.Get(0) == nil {
		return nil, args.Error(1)
	}
	return args.Get(0).(*caasmmodel.CloudAccount), args.Error(1)
}

func (m *MockCloudAccountRepository) FindByExternalID(ctx context.Context, provider caasmmodel.CloudProvider, externalID string) (*caasmmodel.CloudAccount, error) {
	args := m.Called(ctx, provider, externalID)
	if args.Get(0) == nil {
		return nil, args.Error(1)
	}
	return args.Get(0).(*caasmmodel.CloudAccount), args.Error(1)
}

func (m *MockCloudAccountRepository) List(ctx context.Context, filter repository.CloudAccountFilter, page, pageSize int) ([]caasmmodel.CloudAccount, int64, error) {
	args := m.Called(ctx, filter, page, pageSize)
	return args.Get(0).([]caasmmodel.CloudAccount), args.Get(1).(int64), args.Error(2)
}

func (m *MockCloudAccountRepository) FindDueForSync(ctx context.Context) ([]caasmmodel.CloudAccount, error) {
	args := m.Called(ctx)
	return args.Get(0).([]caasmmodel.CloudAccount), args.Error(1)
}

func (m *MockCloudAccountRepository) FindActiveByID(ctx context.Context, id uint64) (*caasmmodel.CloudAccount, error) {
	args := m.Called(ctx, id)
	if args.Get(0) == nil {
		return nil, args.Error(1)
	}
	return args.Get(0).(*caasmmodel.CloudAccount), args.Error(1)
}

func (m *MockCloudAccountRepository) ListIDs(ctx context.Context) ([]uint64, error) {
	args := m.Called(ctx)
	if args.Get(0) == nil {
		return nil, args.Error(1)
	}
	return args.Get(0).([]uint64), args.Error(1)
}

// MockSyncJobRepository is a mock implementation of repository.SyncJobRepository
type MockSyncJobRepository struct {
	mock.Mock
}

func (m *MockSyncJobRepository) Create(ctx context.Context, job *caasmmodel.SyncJob) error {
	args := m.Called(ctx, job)
	// Simulate setting ID
	if job.ID == 0 {
		job.ID = 123
	}
	return args.Error(0)
}

func (m *MockSyncJobRepository) Update(ctx context.Context, job *caasmmodel.SyncJob) error {
	args := m.Called(ctx, job)
	return args.Error(0)
}

func (m *MockSyncJobRepository) FindByID(ctx context.Context, id uint64) (*caasmmodel.SyncJob, error) {
	args := m.Called(ctx, id)
	if args.Get(0) == nil {
		return nil, args.Error(1)
	}
	return args.Get(0).(*caasmmodel.SyncJob), args.Error(1)
}

func (m *MockSyncJobRepository) List(ctx context.Context, filter repository.SyncJobFilter, page, pageSize int) ([]caasmmodel.SyncJob, int64, error) {
	args := m.Called(ctx, filter, page, pageSize)
	return args.Get(0).([]caasmmodel.SyncJob), args.Get(1).(int64), args.Error(2)
}

func (m *MockSyncJobRepository) UpdateFields(ctx context.Context, id uint64, fields map[string]interface{}) error {
	args := m.Called(ctx, id, fields)
	return args.Error(0)
}

func (m *MockSyncJobRepository) FindStaleJobs(ctx context.Context, status caasmmodel.SyncJobStatus, olderThan time.Time) ([]caasmmodel.SyncJob, error) {
	args := m.Called(ctx, status, olderThan)
	if args.Get(0) == nil {
		return nil, args.Error(1)
	}
	return args.Get(0).([]caasmmodel.SyncJob), args.Error(1)
}

func (m *MockSyncJobRepository) CountActiveJobs(ctx context.Context) (int64, error) {
	args := m.Called(ctx)
	return args.Get(0).(int64), args.Error(1)
}

func (m *MockSyncJobRepository) CountRunningJobs(ctx context.Context) (int64, error) {
	args := m.Called(ctx)
	return args.Get(0).(int64), args.Error(1)
}

func (m *MockSyncJobRepository) HasActiveJobForAccount(ctx context.Context, cloudAccountID uint64) (bool, error) {
	args := m.Called(ctx, cloudAccountID)
	return args.Bool(0), args.Error(1)
}

func (m *MockSyncJobRepository) FindActiveJobForAccount(ctx context.Context, cloudAccountID uint64) (*caasmmodel.SyncJob, error) {
	args := m.Called(ctx, cloudAccountID)
	if args.Get(0) == nil {
		return nil, args.Error(1)
	}
	return args.Get(0).(*caasmmodel.SyncJob), args.Error(1)
}

func (m *MockSyncJobRepository) FindLatestResumableJobForAccount(ctx context.Context, cloudAccountID uint64, excludeJobID uint64, jobType string) (*caasmmodel.SyncJob, error) {
	args := m.Called(ctx, cloudAccountID, excludeJobID, jobType)
	if args.Get(0) == nil {
		return nil, args.Error(1)
	}
	return args.Get(0).(*caasmmodel.SyncJob), args.Error(1)
}

// MockAssetRepository is a mock implementation of repository.AssetRepository
type MockAssetRepository struct {
	mock.Mock
}

func (m *MockAssetRepository) List(ctx context.Context, filter repository.AssetFilter, page, pageSize int) ([]caasmmodel.Asset, int64, error) {
	args := m.Called(ctx, filter, page, pageSize)
	return args.Get(0).([]caasmmodel.Asset), args.Get(1).(int64), args.Error(2)
}

func (m *MockAssetRepository) Create(ctx context.Context, asset *caasmmodel.Asset) error {
	args := m.Called(ctx, asset)
	return args.Error(0)
}

func (m *MockAssetRepository) Delete(ctx context.Context, id uint64) error {
	args := m.Called(ctx, id)
	return args.Error(0)
}

func (m *MockAssetRepository) FindByID(ctx context.Context, id uint64) (*caasmmodel.Asset, error) {
	args := m.Called(ctx, id)
	if args.Get(0) == nil {
		return nil, args.Error(1)
	}
	return args.Get(0).(*caasmmodel.Asset), args.Error(1)
}

func (m *MockAssetRepository) FindByNativeID(ctx context.Context, cloudAcctID uint64, nativeID string) (*caasmmodel.Asset, error) {
	if !hasExpectedMockCall(&m.Mock, "FindByNativeID") {
		return nil, gorm.ErrRecordNotFound
	}
	args := m.Called(ctx, cloudAcctID, nativeID)
	if args.Get(0) == nil {
		return nil, args.Error(1)
	}
	return args.Get(0).(*caasmmodel.Asset), args.Error(1)
}

func (m *MockAssetRepository) FindMinimalByNativeIDs(ctx context.Context, cloudAcctID uint64, nativeIDs []string) ([]repository.AssetMinimal, error) {
	if !hasExpectedMockCall(&m.Mock, "FindMinimalByNativeIDs") {
		return []repository.AssetMinimal{}, nil
	}
	args := m.Called(ctx, cloudAcctID, nativeIDs)
	if args.Get(0) == nil {
		return nil, args.Error(1)
	}
	return args.Get(0).([]repository.AssetMinimal), args.Error(1)
}

func (m *MockAssetRepository) ListIPAssets(ctx context.Context, filter repository.IPAssetFilter, page, pageSize int) ([]caasmmodel.IPAsset, int64, error) {
	args := m.Called(ctx, filter, page, pageSize)
	if args.Get(0) == nil {
		return nil, args.Get(1).(int64), args.Error(2)
	}
	return args.Get(0).([]caasmmodel.IPAsset), args.Get(1).(int64), args.Error(2)
}

func hasExpectedMockCall(m *mock.Mock, method string) bool {
	if m == nil {
		return false
	}
	for _, call := range m.ExpectedCalls {
		if call.Method == method {
			return true
		}
	}
	return false
}

func (m *MockAssetRepository) Detail(ctx context.Context, id uint64) (*repository.AssetDetail, error) {
	args := m.Called(ctx, id)
	if args.Get(0) == nil {
		return nil, args.Error(1)
	}
	return args.Get(0).(*repository.AssetDetail), args.Error(1)
}

func (m *MockAssetRepository) Update(ctx context.Context, asset *caasmmodel.Asset) error {
	args := m.Called(ctx, asset)
	return args.Error(0)
}

func (m *MockAssetRepository) UpdateAssetOwner(ctx context.Context, id uint64, ownerEmployeeID *uint64, departmentID *string) error {
	args := m.Called(ctx, id, ownerEmployeeID, departmentID)
	return args.Error(0)
}

func (m *MockAssetRepository) UpdateDomainOwner(ctx context.Context, id uint64, ownerEmployeeID *uint64, departmentID *string) error {
	args := m.Called(ctx, id, ownerEmployeeID, departmentID)
	return args.Error(0)
}

func (m *MockAssetRepository) Save(ctx context.Context, asset *caasmmodel.Asset) error {
	args := m.Called(ctx, asset)
	return args.Error(0)
}

func (m *MockAssetRepository) SaveAssetSnapshot(ctx context.Context, asset *caasmmodel.Asset) error {
	args := m.Called(ctx, asset)
	return args.Error(0)
}

func (m *MockAssetRepository) FindIPAsset(ctx context.Context, assetID uint64, ip string) (*caasmmodel.IPAsset, error) {
	args := m.Called(ctx, assetID, ip)
	if args.Get(0) == nil {
		return nil, args.Error(1)
	}
	return args.Get(0).(*caasmmodel.IPAsset), args.Error(1)
}

func (m *MockAssetRepository) FindDomainAsset(ctx context.Context, assetID uint64, domain string) (*caasmmodel.DomainAsset, error) {
	args := m.Called(ctx, assetID, domain)
	if args.Get(0) == nil {
		return nil, args.Error(1)
	}
	return args.Get(0).(*caasmmodel.DomainAsset), args.Error(1)
}

func (m *MockAssetRepository) SaveIPAsset(ctx context.Context, ipAsset *caasmmodel.IPAsset) error {
	args := m.Called(ctx, ipAsset)
	return args.Error(0)
}

func (m *MockAssetRepository) SaveDomainAsset(ctx context.Context, domainAsset *caasmmodel.DomainAsset) error {
	args := m.Called(ctx, domainAsset)
	return args.Error(0)
}

func (m *MockAssetRepository) SaveDomainAssetSnapshot(ctx context.Context, domainAsset *caasmmodel.DomainAsset) error {
	args := m.Called(ctx, domainAsset)
	return args.Error(0)
}

func (m *MockAssetRepository) DeletePrivateIPAssetsForCloudAccount(ctx context.Context, cloudAcctID uint64) error {
	args := m.Called(ctx, cloudAcctID)
	return args.Error(0)
}

func (m *MockAssetRepository) DeletePrivateDomainAssetsForCloudAccount(ctx context.Context, cloudAcctID uint64) error {
	args := m.Called(ctx, cloudAcctID)
	return args.Error(0)
}

func (m *MockAssetRepository) SaveRelationship(ctx context.Context, rel *caasmmodel.AssetRelationship) error {
	args := m.Called(ctx, rel)
	return args.Error(0)
}

func (m *MockAssetRepository) ListByIDs(ctx context.Context, ids []uint64) ([]caasmmodel.Asset, error) {
	args := m.Called(ctx, ids)
	if args.Get(0) == nil {
		return nil, args.Error(1)
	}
	return args.Get(0).([]caasmmodel.Asset), args.Error(1)
}

func (m *MockAssetRepository) ListRelationships(ctx context.Context, filter repository.RelationshipFilter) ([]caasmmodel.AssetRelationship, error) {
	args := m.Called(ctx, filter)
	if args.Get(0) == nil {
		return nil, args.Error(1)
	}
	return args.Get(0).([]caasmmodel.AssetRelationship), args.Error(1)
}

func (m *MockAssetRepository) SoftDeleteStale(ctx context.Context, cloudAcctID uint64, cutoff time.Time) error {
	args := m.Called(ctx, cloudAcctID, cutoff)
	return args.Error(0)
}

func (m *MockAssetRepository) GetDistinctAssetTypes(ctx context.Context) ([]string, error) {
	args := m.Called(ctx)
	if args.Get(0) == nil {
		return nil, args.Error(1)
	}
	return args.Get(0).([]string), args.Error(1)
}

func (m *MockAssetRepository) GetDistinctServices(ctx context.Context) ([]string, error) {
	args := m.Called(ctx)
	if args.Get(0) == nil {
		return nil, args.Error(1)
	}
	return args.Get(0).([]string), args.Error(1)
}

func (m *MockAssetRepository) GetDistinctEnvironments(ctx context.Context) ([]string, error) {
	args := m.Called(ctx)
	if args.Get(0) == nil {
		return nil, args.Error(1)
	}
	return args.Get(0).([]string), args.Error(1)
}

// MockIPPortIndexRepository is a mock implementation of repository.IPPortIndexRepository
type MockIPPortIndexRepository struct {
	mock.Mock
}

func (m *MockIPPortIndexRepository) Upsert(ctx context.Context, entry *caasmmodel.IPPortIndex) error {
	args := m.Called(ctx, entry)
	return args.Error(0)
}

func (m *MockIPPortIndexRepository) DeleteNonPublicForCloudAccount(ctx context.Context, cloudAcctID uint64) error {
	args := m.Called(ctx, cloudAcctID)
	return args.Error(0)
}

func (m *MockIPPortIndexRepository) Query(ctx context.Context, filter repository.PortQueryFilter, page, pageSize int) ([]caasmmodel.IPPortIndex, int64, error) {
	args := m.Called(ctx, filter, page, pageSize)
	return args.Get(0).([]caasmmodel.IPPortIndex), args.Get(1).(int64), args.Error(2)
}

func (m *MockIPPortIndexRepository) Detail(ctx context.Context, id uint64) (*repository.PortDetail, error) {
	args := m.Called(ctx, id)
	if args.Get(0) == nil {
		return nil, args.Error(1)
	}
	return args.Get(0).(*repository.PortDetail), args.Error(1)
}

func (m *MockIPPortIndexRepository) Export(ctx context.Context) ([]repository.PortExportEntry, error) {
	args := m.Called(ctx)
	if args.Get(0) == nil {
		return nil, args.Error(1)
	}
	return args.Get(0).([]repository.PortExportEntry), args.Error(1)
}

func (m *MockIPPortIndexRepository) QueryByPort(ctx context.Context, port uint32) ([]caasmmodel.IPPortIndex, error) {
	args := m.Called(ctx, port)
	return args.Get(0).([]caasmmodel.IPPortIndex), args.Error(1)
}

func (m *MockIPPortIndexRepository) HighRiskPorts(ctx context.Context, ports []uint32) ([]caasmmodel.IPPortIndex, error) {
	args := m.Called(ctx, ports)
	return args.Get(0).([]caasmmodel.IPPortIndex), args.Error(1)
}

func (m *MockIPPortIndexRepository) Stats(ctx context.Context, topN int) (*repository.PortStats, error) {
	args := m.Called(ctx, topN)
	if args.Get(0) == nil {
		return nil, args.Error(1)
	}
	return args.Get(0).(*repository.PortStats), args.Error(1)
}
