# Indexable Usage Guide

## Generic Implementation for Any Indexable Struct

The implementation is now **generic** and supports **any struct that embeds `model.Indexable`**!

- **Interface**: `api/pkg/db/indexable.go` - defines the contract
- **Implementation**: `api/pkg/db/internal/mongo/indexable/` - generic implementation
- **Factory**: `api/pkg/db/project_indexable.go` - convenient factory for projects

## Usage

### 1. Using the Generic Implementation Directly

```go
import "github.com/tech/sendico/pkg/db/internal/mongo/indexable"

// For any type that embeds model.Indexable, define helper functions:
createEmpty := func() *YourType {
    return &YourType{}
}

getIndexable := func(obj *YourType) *model.Indexable {
    return &obj.Indexable
}

// Create generic IndexableDB
indexableDB := indexable.NewIndexableDB(repo, logger, createEmpty, getIndexable)

// Use with single filter parameter
err := indexableDB.Reorder(ctx, objectID, newIndex, filter)
```

### 2. Using the Project Factory (Recommended for Projects)

```go
import "github.com/tech/sendico/pkg/db"

// Create project indexable DB (automatically applies org filter)
projectDB := db.NewProjectIndexableDB(repo, logger, organizationRef)

// Reorder project (org filter applied automatically)
err := projectDB.Reorder(ctx, projectID, newIndex, repository.Query())

// Reorder with additional filters (combined with org filter)
additionalFilter := repository.Query().Comparison(repository.Field("state"), builder.Eq, "active")
err := projectDB.Reorder(ctx, projectID, newIndex, additionalFilter)
```

## Examples for Different Types

### Project IndexableDB
```go
createEmpty := func() *model.Project {
    return &model.Project{}
}

getIndexable := func(p *model.Project) *model.Indexable {
    return &p.Indexable
}

projectDB := indexable.NewIndexableDB(repo, logger, createEmpty, getIndexable)
orgFilter := repository.OrgFilter(organizationRef)
projectDB.Reorder(ctx, projectID, 2, orgFilter)
```

### Status IndexableDB
```go
createEmpty := func() *model.Status {
    return &model.Status{}
}

getIndexable := func(s *model.Status) *model.Indexable {
    return &s.Indexable
}

statusDB := indexable.NewIndexableDB(repo, logger, createEmpty, getIndexable)
projectFilter := repository.Query().Comparison(repository.Field("projectRef"), builder.Eq, projectRef)
statusDB.Reorder(ctx, statusID, 1, projectFilter)
```

### Task IndexableDB
```go
createEmpty := func() *model.Task {
    return &model.Task{}
}

getIndexable := func(t *model.Task) *model.Indexable {
    return &t.Indexable
}

taskDB := indexable.NewIndexableDB(repo, logger, createEmpty, getIndexable)
statusFilter := repository.Query().Comparison(repository.Field("statusRef"), builder.Eq, statusRef)
taskDB.Reorder(ctx, taskID, 3, statusFilter)
```

### Priority IndexableDB
```go
createEmpty := func() *model.Priority {
    return &model.Priority{}
}

getIndexable := func(p *model.Priority) *model.Indexable {
    return &p.Indexable
}

priorityDB := indexable.NewIndexableDB(repo, logger, createEmpty, getIndexable)
orgFilter := repository.OrgFilter(organizationRef)
priorityDB.Reorder(ctx, priorityID, 0, orgFilter)
```

### Global Reordering (No Filter)
```go
createEmpty := func() *model.Project {
    return &model.Project{}
}

getIndexable := func(p *model.Project) *model.Indexable {
    return &p.Indexable
}

globalDB := indexable.NewIndexableDB(repo, logger, createEmpty, getIndexable)
// Reorders all items globally (empty filter)
globalDB.Reorder(ctx, objectID, 5, repository.Query())
```

## Key Features

### ✅ **Generic Support**
- Works with **any struct** that embeds `model.Indexable`
- Type-safe with compile-time checking
- No hardcoded types

### ✅ **Single Filter Parameter**
- **Simple**: Single `builder.Query` parameter instead of variadic `interface{}`
- **Flexible**: Can incorporate any combination of filters
- **Type-safe**: No runtime type assertions needed

### ✅ **Clean Architecture**
- Interface separated from implementation
- Generic implementation in internal package
- Easy-to-use factories for common types

## How It Works

### Generic Algorithm
1. **Get current index** using type-specific helper function
2. **If no change needed** → return early
3. **Apply filter** to scope affected items
4. **Shift affected items** using `PatchMany` with `$inc`
5. **Update target object** using `Patch` with `$set`

### Type-Safe Implementation
```go
type IndexableDB[T storable.Storable] struct {
    repo         repository.Repository
    logger       mlogger.Logger
    createEmpty  func() T
    getIndexable func(T) *model.Indexable
}

// Single filter parameter - clean and simple
func (db *IndexableDB[T]) Reorder(ctx context.Context, objectRef primitive.ObjectID, newIndex int, filter builder.Query) error
```

## Benefits

✅ **Generic** - Works with any Indexable struct  
✅ **Type Safe** - Compile-time type checking  
✅ **Simple** - Single filter parameter instead of variadic interface{}  
✅ **Efficient** - Uses patches, not full updates  
✅ **Clean** - Interface separated from implementation  

That's it! **Generic, type-safe, and simple** reordering for any Indexable struct with a single filter parameter. 