package mock

import (
	"context"
	"sync"

	"github.com/google/uuid"
	"github.com/ocdp/cluster-service/internal/domain/entity"
	"github.com/ocdp/cluster-service/internal/domain/repository"
)

type WorkspaceRepositoryMock struct {
	mu         sync.RWMutex
	workspaces map[string]*entity.Workspace
}

func NewWorkspaceRepositoryMock() repository.WorkspaceRepository {
	repo := &WorkspaceRepositoryMock{workspaces: make(map[string]*entity.Workspace)}
	defaultWorkspace := entity.NewWorkspace(entity.DefaultWorkspaceName, "")
	defaultWorkspace.ID = entity.DefaultWorkspaceID
	repo.workspaces[defaultWorkspace.ID] = defaultWorkspace
	return repo
}

func (r *WorkspaceRepositoryMock) Create(ctx context.Context, workspace *entity.Workspace) error {
	r.mu.Lock()
	defer r.mu.Unlock()
	if workspace.ID == "" {
		workspace.ID = uuid.New().String()
	}
	for _, existing := range r.workspaces {
		if existing.Name == workspace.Name {
			return entity.ErrWorkspaceExists
		}
	}
	copy := *workspace
	r.workspaces[workspace.ID] = &copy
	return nil
}

func (r *WorkspaceRepositoryMock) GetByID(ctx context.Context, id string) (*entity.Workspace, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()
	workspace, ok := r.workspaces[id]
	if !ok {
		return nil, entity.ErrWorkspaceNotFound
	}
	copy := *workspace
	return &copy, nil
}

func (r *WorkspaceRepositoryMock) GetByName(ctx context.Context, name string) (*entity.Workspace, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()
	for _, workspace := range r.workspaces {
		if workspace.Name == name {
			copy := *workspace
			return &copy, nil
		}
	}
	return nil, entity.ErrWorkspaceNotFound
}

func (r *WorkspaceRepositoryMock) Update(ctx context.Context, workspace *entity.Workspace) error {
	r.mu.Lock()
	defer r.mu.Unlock()
	if _, ok := r.workspaces[workspace.ID]; !ok {
		return entity.ErrWorkspaceNotFound
	}
	copy := *workspace
	r.workspaces[workspace.ID] = &copy
	return nil
}

func (r *WorkspaceRepositoryMock) Delete(ctx context.Context, id string) error {
	r.mu.Lock()
	defer r.mu.Unlock()
	if _, ok := r.workspaces[id]; !ok {
		return entity.ErrWorkspaceNotFound
	}
	delete(r.workspaces, id)
	return nil
}

func (r *WorkspaceRepositoryMock) List(ctx context.Context) ([]*entity.Workspace, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()
	result := make([]*entity.Workspace, 0, len(r.workspaces))
	for _, workspace := range r.workspaces {
		copy := *workspace
		result = append(result, &copy)
	}
	return result, nil
}

type WorkspaceClusterBindingRepositoryMock struct {
	mu       sync.RWMutex
	bindings map[string]*entity.WorkspaceClusterBinding
}

func NewWorkspaceClusterBindingRepositoryMock() repository.WorkspaceClusterBindingRepository {
	return &WorkspaceClusterBindingRepositoryMock{bindings: make(map[string]*entity.WorkspaceClusterBinding)}
}

func bindingKey(workspaceID, clusterID string) string {
	return workspaceID + "/" + clusterID
}

func (r *WorkspaceClusterBindingRepositoryMock) Upsert(ctx context.Context, binding *entity.WorkspaceClusterBinding) error {
	r.mu.Lock()
	defer r.mu.Unlock()
	if binding.ID == "" {
		binding.ID = uuid.New().String()
	}
	copy := *binding
	r.bindings[bindingKey(binding.WorkspaceID, binding.ClusterID)] = &copy
	return nil
}

func (r *WorkspaceClusterBindingRepositoryMock) Get(ctx context.Context, workspaceID, clusterID string) (*entity.WorkspaceClusterBinding, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()
	binding, ok := r.bindings[bindingKey(workspaceID, clusterID)]
	if !ok {
		return nil, entity.ErrWorkspaceNotFound
	}
	copy := *binding
	return &copy, nil
}

func (r *WorkspaceClusterBindingRepositoryMock) ListByWorkspace(ctx context.Context, workspaceID string) ([]*entity.WorkspaceClusterBinding, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()
	result := make([]*entity.WorkspaceClusterBinding, 0)
	for _, binding := range r.bindings {
		if binding.WorkspaceID != workspaceID {
			continue
		}
		copy := *binding
		result = append(result, &copy)
	}
	return result, nil
}

func (r *WorkspaceClusterBindingRepositoryMock) Delete(ctx context.Context, workspaceID, clusterID string) error {
	r.mu.Lock()
	defer r.mu.Unlock()
	delete(r.bindings, bindingKey(workspaceID, clusterID))
	return nil
}

type AuditLogRepositoryMock struct {
	mu   sync.RWMutex
	logs []*entity.AuditLog
}

func NewAuditLogRepositoryMock() repository.AuditLogRepository {
	return &AuditLogRepositoryMock{logs: make([]*entity.AuditLog, 0)}
}

func (r *AuditLogRepositoryMock) Create(ctx context.Context, logEntry *entity.AuditLog) error {
	r.mu.Lock()
	defer r.mu.Unlock()
	if logEntry.ID == "" {
		logEntry.ID = uuid.New().String()
	}
	copy := *logEntry
	r.logs = append(r.logs, &copy)
	return nil
}

func (r *AuditLogRepositoryMock) ListByWorkspace(ctx context.Context, workspaceID string, limit int) ([]*entity.AuditLog, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()
	result := make([]*entity.AuditLog, 0)
	for i := len(r.logs) - 1; i >= 0; i-- {
		if r.logs[i].WorkspaceID == workspaceID {
			copy := *r.logs[i]
			result = append(result, &copy)
			if limit > 0 && len(result) >= limit {
				break
			}
		}
	}
	return result, nil
}
