# Project Merge & Conflict Resolution Feature

## Overview

pyPhotoAlbum v3.0 introduces comprehensive merge conflict resolution support, enabling multiple users to edit the same album and merge their changes intelligently. The system uses UUIDs, timestamps, and a project ID to track changes and resolve conflicts.

## Table of Contents

- [Key Features](#key-features)
- [How It Works](#how-it-works)
- [File Format Changes (v3.0)](#file-format-changes-v30)
- [User Guide](#user-guide)
- [Developer Guide](#developer-guide)
- [Testing](#testing)
- [Migration from v2.0](#migration-from-v20)

---

## Key Features

### 1. **Project ID-Based Merge Detection**
- Each project has a unique `project_id` (UUID)
- **Same project_id** → Merge with conflict resolution
- **Different project_id** → Concatenate (combine all pages)

### 2. **UUID-Based Element Tracking**
- Every page and element has a stable UUID
- Elements can be tracked even when page numbers or z-order changes
- Enables reliable conflict detection across versions

### 3. **Timestamp-Based Conflict Resolution**
- All changes tracked with `created` and `last_modified` timestamps (ISO 8601 UTC)
- Automatic "Latest Wins" strategy available
- Manual conflict resolution through visual dialog

### 4. **Soft Delete Support**
- Deleted items marked with `deleted` flag and `deleted_at` timestamp
- Prevents resurrection conflicts
- Tombstone pattern ensures deleted items stay deleted

### 5. **Visual Merge Dialog**
- Side-by-side comparison of conflicting changes
- Page previews and element details
- Multiple resolution strategies:
  - **Latest Wins**: Most recent change wins (automatic)
  - **Always Use Yours**: Keep all local changes
  - **Always Use Theirs**: Accept all remote changes
  - **Manual**: Choose per-conflict

---

## How It Works

### Merge Workflow

```
1. User clicks "Merge Projects" in File ribbon tab
   ↓
2. Select .ppz file to merge
   ↓
3. System compares project_ids
   ├─→ Same ID: Detect conflicts → Show merge dialog
   └─→ Different ID: Ask to concatenate
   ↓
4. User resolves conflicts (if any)
   ↓
5. Merged project becomes current project
   ↓
6. User saves merged project
```

### Conflict Detection

The system detects three types of conflicts:

#### 1. **Page-Level Conflicts**
- Page modified in both versions
- Page deleted in one, modified in other
- Page properties changed (size, type, etc.)

#### 2. **Element-Level Conflicts**
- Element modified in both versions (position, size, rotation, content)
- Element deleted in one, modified in other
- Element properties changed differently

#### 3. **Project-Level Conflicts**
- Settings changed in both (page size, DPI, cover settings, etc.)

### Automatic Conflict Resolution

**Non-conflicting changes** are automatically merged:
- Page 1 modified in version A, Page 2 modified in version B → Keep both
- New pages added at different positions → Merge both sets
- Different elements modified → Keep all modifications

**Conflicting changes** require resolution:
- Same element modified in both versions
- Element/page deleted in one but modified in other

---

## File Format Changes (v3.0)

### What's New in v3.0

#### Project Level
```json
{
  "data_version": "3.0",
  "project_id": "550e8400-e29b-41d4-a716-446655440000",
  "created": "2025-01-22T10:30:00.123456+00:00",
  "last_modified": "2025-01-22T14:45:12.789012+00:00",
  ...
}
```

#### Page Level
```json
{
  "page_number": 1,
  "uuid": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
  "created": "2025-01-22T10:30:00.123456+00:00",
  "last_modified": "2025-01-22T11:15:30.456789+00:00",
  "deleted": false,
  "deleted_at": null,
  ...
}
```

#### Element Level
```json
{
  "type": "image",
  "uuid": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "created": "2025-01-22T10:30:00.123456+00:00",
  "last_modified": "2025-01-22T13:20:45.123456+00:00",
  "deleted": false,
  "deleted_at": null,
  "position": [10, 10],
  "size": [100, 100],
  ...
}
```

### Backwards Compatibility

- **v3.0 can read v2.0 and v1.0 files** with automatic migration
- **v2.0/v1.0 cannot read v3.0 files** (breaking change)
- Migration automatically generates UUIDs and timestamps for old files

---

## User Guide

### How to Merge Two Album Versions

1. **Open your current album** in pyPhotoAlbum

2. **Click "Merge Projects"** in the File tab of the ribbon

3. **Select the other album file** (.ppz) to merge

4. **System analyzes the projects:**
   - If they're the same album (same project_id):
     - Shows conflicts requiring resolution
     - Auto-merges non-conflicting changes
   - If they're different albums:
     - Asks if you want to combine all pages

5. **Resolve conflicts** (if merging same album):
   - View side-by-side comparison
   - Choose "Use Your Version" or "Use Other Version" for each conflict
   - Or click "Auto-Resolve All" with a strategy:
     - **Latest Wins**: Keeps most recently modified version
     - **Always Use Yours**: Keeps all your changes
     - **Always Use Theirs**: Accepts all their changes

6. **Click "Apply Merge"** to complete the merge

7. **Save the merged album** when ready

### Best Practices

1. **Save before merging** - The system will prompt you, but it's good practice

2. **Use cloud sync carefully** - If using Dropbox/Google Drive:
   - Each person should have their own working copy
   - Merge explicitly rather than relying on cloud sync conflicts

3. **Communicate with collaborators** - Agree on who edits which pages to minimize conflicts

4. **Review the merge** - Check the merged result before saving

5. **Keep backups** - The autosave system creates checkpoints, but manual backups are recommended

### Common Scenarios

#### Scenario 1: You and a Friend Edit Different Pages
- **Result**: Auto-merge ✅
- No conflicts, both sets of changes preserved

#### Scenario 2: You Both Edit the Same Image Position
- **Result**: Conflict resolution needed ⚠️
- You choose which position to keep

#### Scenario 3: You Delete an Image, They Move It
- **Result**: Conflict resolution needed ⚠️
- You choose: keep it deleted or use their moved version

#### Scenario 4: Combining Two Different Albums
- **Result**: Concatenation
- All pages from both albums combined into one

---

## Developer Guide

### Architecture

```
pyPhotoAlbum/
├── models.py                 # BaseLayoutElement with UUID/timestamp support
├── project.py                # Project and Page with UUID/timestamp support
├── version_manager.py        # v3.0 migration logic
├── project_serializer.py     # Save/load with v3.0 support
├── merge_manager.py          # Core merge conflict detection & resolution
├── merge_dialog.py           # Qt UI for visual conflict resolution
└── mixins/operations/
    └── merge_ops.py          # Ribbon integration & workflow
```

### Key Classes

#### MergeManager
```python
from pyPhotoAlbum.merge_manager import MergeManager, MergeStrategy

manager = MergeManager()

# Check if projects should be merged or concatenated
should_merge = manager.should_merge_projects(project_a_data, project_b_data)

# Detect conflicts
conflicts = manager.detect_conflicts(our_data, their_data)

# Auto-resolve
resolutions = manager.auto_resolve_conflicts(MergeStrategy.LATEST_WINS)

# Apply merge
merged_data = manager.apply_resolutions(our_data, their_data, resolutions)
```

#### Data Model Updates

```python
from pyPhotoAlbum.models import ImageData
from pyPhotoAlbum.project import Page, Project

# All elements now have:
element = ImageData(...)
element.uuid  # Auto-generated UUID
element.created  # ISO 8601 timestamp
element.last_modified  # ISO 8601 timestamp
element.deleted  # Boolean flag
element.deleted_at  # Timestamp when deleted

# Mark as modified
element.mark_modified()  # Updates last_modified

# Mark as deleted
element.mark_deleted()  # Sets deleted=True, deleted_at=now

# Same for pages and projects
page.mark_modified()
project.mark_modified()
```

### Adding Merge Support to Custom Elements

If you create custom element types, ensure they:

1. **Inherit from BaseLayoutElement**
```python
class MyCustomElement(BaseLayoutElement):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)  # Initializes UUID and timestamps
        # Your custom fields here
```

2. **Call `_deserialize_base_fields()` first in deserialize**
```python
def deserialize(self, data: Dict[str, Any]):
    self._deserialize_base_fields(data)  # Load UUID/timestamps
    # Load your custom fields
```

3. **Include base fields in serialize**
```python
def serialize(self) -> Dict[str, Any]:
    data = {
        "type": "mycustom",
        # Your custom fields
    }
    data.update(self._serialize_base_fields())  # Add UUID/timestamps
    return data
```

4. **Call `mark_modified()` when changed**
```python
def set_my_property(self, value):
    self.my_property = value
    self.mark_modified()  # Update timestamp
```

### Migration System

To add a new migration (e.g., v3.0 to v4.0):

```python
# In version_manager.py

@DataMigration.register_migration("3.0", "4.0")
def migrate_3_0_to_4_0(data: Dict[str, Any]) -> Dict[str, Any]:
    """
    Migrate from version 3.0 to 4.0.

    Main changes:
    - Add new fields
    - Update structures
    """
    # Perform migration
    data['new_field'] = default_value

    # Update version
    data['data_version'] = "4.0"

    return data
```

### Testing

Run the provided test scripts:

```bash
# Test v2.0 → v3.0 migration
python test_migration.py

# Test merge functionality
python test_merge.py
```

Expected output: All tests should pass with ✅

---

## Testing

### Manual Testing Checklist

#### Test 1: Basic Migration
- [ ] Open a v2.0 project
- [ ] Verify it loads without errors
- [ ] Check console for "Migration 2.0 → 3.0" message
- [ ] Save the project
- [ ] Verify saved version is 3.0

#### Test 2: Same Project Merge
- [ ] Create a project, save it
- [ ] Open the file twice in different instances
- [ ] Modify same element in both
- [ ] Merge them
- [ ] Verify conflict dialog appears
- [ ] Resolve conflict
- [ ] Verify merged result

#### Test 3: Different Project Concatenation
- [ ] Create two different projects
- [ ] Try to merge them
- [ ] Verify concatenation option appears
- [ ] Verify combined project has all pages

#### Test 4: Auto-Merge Non-Conflicting
- [ ] Create project with 2 pages
- [ ] Version A: Edit page 1
- [ ] Version B: Edit page 2
- [ ] Merge
- [ ] Verify auto-merge without conflicts
- [ ] Verify both edits preserved

### Automated Testing

Run the test scripts:

```bash
cd /home/dtourolle/Development/pyPhotoAlbum

# Migration test
./test_migration.py

# Merge test
./test_merge.py
```

---

## Migration from v2.0

### Automatic Migration

When you open a v2.0 project in v3.0, it will automatically:

1. Generate a unique `project_id`
2. Generate `uuid` for all pages and elements
3. Set `created` and `last_modified` to current time
4. Add `deleted` and `deleted_at` fields (all set to False/None)
5. Update `data_version` to "3.0"

### Migration Output Example

```
Migration 2.0 → 3.0: Adding UUIDs, timestamps, and project_id
  Generated project_id: 550e8400-e29b-41d4-a716-446655440000
  Migrated 5 pages to v3.0
Migration completed successfully
```

### After Migration

- **Save the project** to persist the migration
- The migrated file can **only be opened in v3.0+**
- Keep a backup of v2.0 file if you need v2.0 compatibility

### Rollback

If you need to rollback to v2.0:
1. Don't save after opening in v3.0
2. Close without saving
3. Open original v2.0 file in v2.0

---

## Troubleshooting

### Merge Dialog Won't Appear

**Problem**: Clicking "Merge Projects" does nothing

**Solutions**:
- Check both projects are v3.0 (or were migrated)
- Verify projects have the same `project_id`
- Check console for error messages

### Can't Resolve Conflicts

**Problem**: "Apply Merge" button is grayed out

**Solutions**:
- Make a resolution choice for each conflict
- Or click "Auto-Resolve All" first

### Changes Not Preserved

**Problem**: After merge, some changes are missing

**Solutions**:
- Check which resolution strategy you used
- "Latest Wins" prefers most recent modifications
- Review each conflict manually if needed

### Project Won't Load

**Problem**: "Incompatible file version" error

**Solutions**:
- This is a v2.0 or v1.0 file
- Migration should happen automatically
- If not, check version_manager.py for errors

---

## FAQ

### Q: Can I merge more than two projects at once?
**A:** Not directly. Merge two at a time, then merge the result with a third.

### Q: What happens to undo history after merge?
**A:** Undo history is session-specific and not preserved during merge. Save before merging.

### Q: Can I see what changed before merging?
**A:** The merge dialog shows changed elements with timestamps. Future versions may add detailed diff view.

### Q: Is merge atomic?
**A:** No. If you cancel during conflict resolution, no changes are made. Once you click "Apply Merge", the changes are applied to the current project.

### Q: Can I merge projects from different versions?
**A:** Yes! v2.0 and v1.0 projects are automatically migrated to v3.0 before merging.

### Q: What if two people add the same image?
**A:** If the image has the same filename and is added to different pages, both instances are kept. If added to the same location on the same page, it becomes a conflict.

### Q: Can I programmatically merge projects?
**A:** Yes! See the Developer Guide section for `MergeManager` API usage.

---

## Future Enhancements

Potential improvements for future versions:

1. **Three-way merge** - Use base version for better conflict resolution
2. **Merge history tracking** - Log all merges performed
3. **Partial merge** - Merge only specific pages
4. **Cloud collaboration** - Real-time collaborative editing
5. **Merge preview** - Show full diff before applying
6. **Asset conflict handling** - Better handling of duplicate assets
7. **Conflict visualization** - Visual overlay showing changes

---

## Version History

### v3.0 (2025-01-22)
- ✨ Initial merge conflict resolution feature
- ✨ UUID and timestamp tracking
- ✨ Project ID-based merge detection
- ✨ Visual merge dialog
- ✨ Automatic migration from v2.0
- ✨ Soft delete support

---

## Credits

Merge system designed and implemented with the following principles:
- **UUID stability** - Elements tracked across versions
- **Timestamp precision** - ISO 8601 UTC for reliable ordering
- **Backwards compatibility** - Seamless migration from v2.0
- **User-friendly** - Visual conflict resolution
- **Developer-friendly** - Clean API, well-documented

For questions or issues, please file a bug report in the project repository.
