# Quick Implementation Guide - MangaReader Optimizations

## 📂 Archivos Creados

```
ios-app/Sources/Services/
├── ManhwaWebScraperOptimized.swift    # Scraper con cache inteligente
├── StorageServiceOptimized.swift      # Storage con compresión y thumbnails
├── ImageCache.swift                    # Sistema de cache de imágenes
└── CacheManager.swift                  # Gerente central de cache

ios-app/Sources/Views/
└── ReaderViewOptimized.swift          # Reader optimizado

OPTIMIZATION_SUMMARY.md                # Documentación completa
IMPLEMENTATION_GUIDE.md                # Este archivo
```

---

## 🔄 Migración Rápida

### Opción 1: Reemplazo Directo (Recomendado para Testing)

```swift
// 1. En tus ViewModels, cambia:
private let scraper = ManhwaWebScraper.shared
// Por:
private let scraper = ManhwaWebScraperOptimized.shared

// 2. En tus Views, cambia:
private let storage = StorageService.shared
// Por:
private let storage = StorageServiceOptimized.shared

// 3. En ReaderView:
// Reemplaza completamente por ReaderViewOptimized
```

### Opción 2: Migración Gradual (Más Segura)

```swift
// Usa alias de tipo para migrar gradualmente
typealias Scraper = ManhwaWebScraperOptimized
typealias Storage = StorageServiceOptimized

// Código existente funciona sin cambios
private let scraper = Scraper.shared
private let storage = Storage.shared
```

---

## 🔧 Configuración Inicial

### 1. Inicializar CacheManager en AppDelegate

```swift
import UIKit

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions ...
    ) -> Bool {

        // Inicializar cache manager
        let cacheManager = CacheManager.shared

        #if DEBUG
        // Imprimir reporte inicial en debug
        cacheManager.printCacheReport()
        #endif

        return true
    }

    // Monitoring de background
    func applicationDidEnterBackground(_ application: UIApplication) {
        // El CacheManager ya maneja esto automáticamente
        // pero puedes agregar lógica adicional aquí
    }

    // Monitoring de memory warnings
    func applicationDidReceiveMemoryWarning(_ application: UIApplication) {
        // CacheManager ya responde automáticamente
        print("⚠️ Memory warning received")
    }
}
```

### 2. Configurar Ajustes de Cache

```swift
// En algún lugar de configuración (ej: SettingsViewModel)

struct CacheSettings {
    // Ajusta según tus necesidades

    // Tamaño máximo de cache (en bytes)
    static let maxImageCacheSize: Int64 = 500 * 1024 * 1024  // 500 MB
    static let maxMetadataCacheSize: Int64 = 50 * 1024 * 1024 // 50 MB

    // Tiempos de expiración (en segundos)
    static let htmlCacheDuration: TimeInterval = 1800        // 30 min
    static let imageCacheDuration: TimeInterval = 7 * 86400  // 7 días
    static let thumbnailCacheDuration: TimeInterval = 30 * 86400 // 30 días

    // Preloading
    static let preloadAdjacentPages: Int = 2  // 2 páginas antes/después
    static let enablePreloading: Bool = true
}
```

---

## 📊 Monitoreo y Debugging

### Ver Estadísticas en Debug

```swift
#if DEBUG
// Agregar botón de debug en settings
struct DebugSettingsView: View {
    var body: some View {
        VStack {
            Button("Print Cache Report") {
                CacheManager.shared.printCacheReport()
            }

            Button("Print Image Statistics") {
                ImageCache.shared.printStatistics()
            }

            Button("Clear All Cache") {
                CacheManager.shared.clearAllCache()
            }

            Button("Simulate Memory Warning") {
                NotificationCenter.default.post(
                    name: UIApplication.didReceiveMemoryWarningNotification,
                    object: nil
                )
            }
        }
    }
}
#endif
```

### Instruments Configuration

```bash
# Para perfilado de memoria
1. Xcode → Product → Profile
2. Seleccionar "Allocations"
3. Monitorear:
   - Overall memory usage
   - Anonymous VM
   - Image cache size

# Para perfilado de tiempo
1. Seleccionar "Time Profiler"
2. Buscar funciones lentas
3. Verificar que cache esté funcionando
```

---

## 🧪 Testing Checklist

### Tests Funcionales

```swift
class CachePerformanceTests: XCTestCase {

    func testScraperCache() async throws {
        let scraper = ManhwaWebScraperOptimized.shared

        // Primer scraping (debe ser lento)
        let start1 = Date()
        _ = try await scraper.scrapeChapters(mangaSlug: "test-manga")
        let time1 = Date().timeIntervalSince(start1)

        // Segundo scraping (debe ser rápido por cache)
        let start2 = Date()
        _ = try await scraper.scrapeChapters(mangaSlug: "test-manga")
        let time2 = Date().timeIntervalSince(start2)

        // El segundo debe ser >10x más rápido
        XCTAssertLessThan(time2, time1 / 10)
    }

    func testImageCompression() {
        let storage = StorageServiceOptimized.shared

        // Crear imagen de prueba grande (4 MB)
        let largeImage = createTestImage(size: CGSize(width: 2000, height: 3000))

        let expectation = XCTestExpectation(description: "Compress and save")

        Task {
            let url = try await storage.saveImage(
                largeImage,
                mangaSlug: "test",
                chapterNumber: 1,
                pageIndex: 0
            )

            // Verificar que archivo resultante es < 2 MB
            let attributes = try FileManager.default.attributesOfItem(atPath: url.path)
            let fileSize = attributes[.size] as! Int64

            XCTAssertLessThan(fileSize, 2 * 1024 * 1024)

            expectation.fulfill()
        }

        wait(for: [expectation], timeout: 5.0)
    }

    func testPreloading() {
        let viewModel = ReaderViewModelOptimized(
            manga: testManga,
            chapter: testChapter
        )

        // Cargar páginas
        let expectation = XCTestExpectation(description: "Load pages")
        Task {
            await viewModel.loadPages()
            expectation.fulfill()
        }
        wait(for: [expectation], timeout: 10.0)

        // Simular navegación a página 5
        viewModel.currentPage = 5

        // Verificar que páginas 3, 4, 6, 7 están en cache
        let imageCache = ImageCache.shared
        XCTAssertNotNil(imageCache.image(for: viewModel.pages[3].url))
        XCTAssertNotNil(imageCache.image(for: viewModel.pages[4].url))
        XCTAssertNotNil(imageCache.image(for: viewModel.pages[6].url))
        XCTAssertNotNil(imageCache.image(for: viewModel.pages[7].url))
    }
}
```

### Tests de Memoria

```swift
class MemoryTests: XCTestCase {

    func testMemoryUnderLoad() {
        let viewModel = ReaderViewModelOptimized(
            manga: largeManga,  // 100 páginas
            chapter: largeChapter
        )

        let memoryBefore = getMemoryUsage()

        let expectation = XCTestExpectation(description: "Load all pages")
        Task {
            await viewModel.loadPages()

            // Navegar por todas las páginas
            for i in 0..<viewModel.pages.count {
                viewModel.currentPage = i
                try? await Task.sleep(nanoseconds: 100_000_000) // 0.1s
            }

            expectation.fulfill()
        }

        wait(for: [expectation], timeout: 60.0)

        let memoryAfter = getMemoryUsage()
        let memoryIncrease = memoryAfter - memoryBefore

        // No debe aumentar más de 150 MB
        XCTAssertLessThan(memoryIncrease, 150 * 1024 * 1024)
    }

    func testMemoryWarningResponse() {
        // Llenar cache
        let cache = ImageCache.shared
        for i in 0..<100 {
            cache.setImage(createTestImage(), for: "test_\(i)")
        }

        let itemsBefore = cache.getCacheStatistics().memoryCacheHits

        // Simular memory warning
        NotificationCenter.default.post(
            name: UIApplication.didReceiveMemoryWarningNotification,
            object: nil
        )

        // Esperar un poco
        Thread.sleep(forTimeInterval: 0.5)

        // Cache debe estar vacío o casi vacío
        let itemsAfter = cache.getCacheStatistics().memoryCacheHits
        XCTAssertLessThan(itemsAfter, itemsBefore / 2)
    }

    private func getMemoryUsage() -> UInt64 {
        var info = mach_task_basic_info()
        var count = mach_msg_type_number_t(MemoryLayout<mach_task_basic_info>.size)/4

        let result = withUnsafeMutablePointer(to: &info) {
            $0.withMemoryRebound(to: integer_t.self, capacity: 1) {
                task_info(mach_task_self_, task_flavor_t(MACH_TASK_BASIC_INFO), $0, &count)
            }
        }

        return result == KERN_SUCCESS ? info.resident_size : 0
    }
}
```

---

## 🎯 Optimizaciones por Categoría

### Scraper Optimizations

| Optimización | Archivo | Líneas Clave |
|--------------|---------|--------------|
| WKWebView reuse | `ManhwaWebScraperOptimized.swift` | 30-45 |
| HTML cache | `ManhwaWebScraperOptimized.swift` | 48-62 |
| Precompiled JS | `ManhwaWebScraperOptimized.swift` | 67-122 |
| Adaptive timeout | `ManhwaWebScraperOptimized.swift` | 127-165 |
| Concurrency control | `ManhwaWebScraperOptimized.swift` | 170-195 |

### Storage Optimizations

| Optimización | Archivo | Líneas Clave |
|--------------|---------|--------------|
| Adaptive compression | `StorageServiceOptimized.swift` | 28-52 |
| Thumbnail system | `StorageServiceOptimized.swift` | 57-85 |
| Lazy loading | `StorageServiceOptimized.swift` | 478-488 |
| Auto cleanup | `StorageServiceOptimized.swift` | 330-380 |
| Batch operations | `StorageServiceOptimized.swift` | 220-245 |

### Reader Optimizations

| Optimización | Archivo | Líneas Clave |
|--------------|---------|--------------|
| NSCache | `ImageCache.swift` | 22-45 |
| Preloading | `ReaderViewOptimized.swift` | 195-215 |
| Memory management | `ReaderViewOptimized.swift` | 420-445 |
| Debouncing | `ReaderViewOptimized.swift` | 505-525 |

---

## 🔍 Troubleshooting

### Problema: Cache no funciona

```swift
// Verificar que el singleton se esté usando correctamente
let scraper = ManhwaWebScraperOptimized.shared
// NO: let scraper = ManhwaWebScraperOptimized()

// Verificar que los métodos tengan @MainActor si es necesario
@MainActor
func loadChapters() async throws {
    // código
}
```

### Problema: Memoria sigue alta

```swift
// 1. Verificar que se estén liberando referencias
deinit {
    progressSaveTimer?.invalidate()
    NotificationCenter.default.removeObserver(self)
}

// 2. Usizar weak en closures
Task { [weak self] in
    await self?.loadPages()
}

// 3. Verificar en Instruments
// - Revisar "Anonymous VM" (debe ser bajo)
// - Buscar "Malloc" leaks
```

### Problema: Preloading no funciona

```swift
// Verificar que esté habilitado
viewModel.enablePreloading = true  // Debe ser true

// Verificar que onAppear se llame
PageView(page: page)
    .onAppear {
        print("Page \(page.index) appeared")  // Debug
        viewModel.preloadAdjacentPages(...)
    }

// Verificar priority queue
Task(priority: .utility) {
    // Debe usar .utility, no .background
}
```

### Problema: Thumbnails no se generan

```swift
// Verificar directorio de thumbnails
let thumbDir = storage.getThumbnailDirectory(...)
print("Thumb dir: \(thumbDir.path)")

// Verificar que se cree el directorio
try? FileManager.default.createDirectory(
    at: thumbDir,
    withIntermediateDirectories: true
)

// Verificar que se guarde correctamente
try? thumbData.write(to: thumbnailURL)
```

---

## 📈 Métricas de Éxito

### Objetivos de Rendimiento

```swift
struct PerformanceTargets {
    // Tiempos de carga
    static let maxChapterLoadTime: TimeInterval = 2.0  // segundos
    static let maxPageLoadTime: TimeInterval = 0.5     // segundos (con cache)
    static let maxAppLaunchTime: TimeInterval = 1.0    // segundos

    // Memoria
    static let maxReaderMemory: UInt64 = 120 * 1024 * 1024  // 120 MB
    static let maxScraperMemory: UInt64 = 50 * 1024 * 1024  // 50 MB

    // Cache
    static let minCacheHitRate: Double = 0.80  // 80%
    static let maxCacheSize: Int64 = 500 * 1024 * 1024  // 500 MB
}

// Función de verificación
func verifyPerformanceTargets() {
    let cacheStats = ImageCache.shared.getCacheStatistics()

    let hitRate = cacheStats.hitRate
    XCTAssertGreaterThan(hitRate, PerformanceTargets.minCacheHitRate,
                        "Cache hit rate \(hitRate) below target")

    // ... más verificaciones
}
```

---

## 🚀 Próximos Pasos

### Implementación Inmediata

1. **Backup del código existente**
   ```bash
   cp -r ios-app/Sources ios-app/Sources.backup
   ```

2. **Reemplazar archivos uno por uno**
   - Empezar con ManhwaWebScraperOptimized
   - Probar exhaustivamente
   - Continuar con StorageServiceOptimized
   - etc.

3. **Testing completo**
   - Unit tests
   - Integration tests
   - Manual testing

### Optimizaciones Futuras (Opcionales)

1. **Prefetching Predictivo**
   ```swift
   // Usar ML para predecir próximo capítulo
   func predictNextChapter(userHistory: [Chapter]) -> Chapter? {
       // Implementación básica de ML
   }
   ```

2. **Compresión HEIC**
   ```swift
   // Usar HEIC en lugar de JPEG (50% más eficiente)
   if #available(iOS 11.0, *) {
       let heicData = image.heicData()
   }
   ```

3. **Progressive Image Loading**
   ```swift
   // Cargar versión baja resolución primero
   // Luego reemplazar con alta resolución
   ```

4. **Background Sync**
   ```swift
   // Sincronizar en background usando BGTaskScheduler
   func scheduleBackgroundSync() {
       let request = BGAppRefreshTaskRequest(...)
       try? BGTaskScheduler.shared.submit(request)
   }
   ```

---

## 💡 Tips y Best Practices

### DO ✅

```swift
// 1. Usar singletons para caches
static let shared = ImageCache()

// 2. Usar colas para I/O
private let ioQueue = DispatchQueue(label: "...", qos: .utility)

// 3. Weak references en closures
Task { [weak self] in
    await self?.loadData()
}

// 4. Responder a memory warnings
@objc func handleMemoryWarning() {
    cache.removeAllObjects()
}

// 5. Debouncing de operaciones frecuentes
func saveDebounced() {
    timer?.invalidate()
    timer = Timer.scheduledTimer(...)
}
```

### DON'T ❌

```swift
// 1. NO crear múltiples instancias de cache
// let cache1 = ImageCache()
// let cache2 = ImageCache()  // ❌

// 2. NO hacer I/O en main thread
// let data = try Data(contentsOf: url)  // ❌ Bloquea

// 3. NO olvidar weak en closures
// Task {
//     self.doSomething()  // ❌ Memory leak
// }

// 4. NO ignorar memory warnings
// @objc func handleMemoryWarning() {
//     // ❌ No hacer nada
// }

// 5. NO guardar en cada cambio
// func onChange() {
//     saveToDisk()  // ❌ Demasiado frecuente
// }
```

---

## 📞 Soporte

Si encuentras problemas:

1. Revisa `OPTIMIZATION_SUMMARY.md` para detalles técnicos
2. Usa los tests de ejemplo como guía
3. Habilita logging debug en development
4. Usa Instruments para perfilado

---

**Happy Optimizing! 🚀**
