import XCTest
import UIKit
@testable import MangaReader

/// Tests de integración para el flujo completo: scraper -> storage -> view
/// Pruebas de descarga de capítulos y navegación entre vistas
@MainActor
final class IntegrationTests: XCTestCase {

    var scraper: ManhwaWebScraper!
    var storageService: StorageService!

    // MARK: - Setup & Teardown

    override func setUp() async throws {
        try await super.setUp()

        // Limpiar estado antes de cada test
        UserDefaults.standard.removeObject(forKey: "favoriteMangas")
        UserDefaults.standard.removeObject(forKey: "readingProgress")

        scraper = ManhwaWebScraper.shared
        storageService = StorageService.shared
        storageService.clearAllDownloads()

        // Esperar un momento para asegurar limpieza completa
        try await Task.sleep(nanoseconds: 100_000_000) // 0.1 segundos
    }

    override func tearDown() async throws {
        // Limpiar después de los tests
        storageService.clearAllDownloads()
        UserDefaults.standard.removeObject(forKey: "favoriteMangas")
        UserDefaults.standard.removeObject(forKey: "readingProgress")

        scraper = nil
        storageService = nil

        try await super.tearDown()
    }

    // MARK: - Complete Flow Tests: Scraper -> Storage

    func testCompleteScrapingAndStorageFlow() async throws {
        // Este test simula el flujo completo: scraper -> storage

        // 1. Simular datos del scraper (capítulos)
        let mockChapters = [
            Chapter(number: 10, title: "Chapter 10", url: "url10", slug: "slug10"),
            Chapter(number: 9, title: "Chapter 9", url: "url9", slug: "slug9"),
            Chapter(number: 8, title: "Chapter 8", url: "url8", slug: "slug8")
        ]

        // 2. Guardar progreso de lectura simulado
        let progress = ReadingProgress(
            mangaSlug: "test-manga",
            chapterNumber: 9,
            pageNumber: 5,
            timestamp: Date()
        )
        storageService.saveReadingProgress(progress)

        // 3. Verificar que el progreso se guardó
        let retrievedProgress = storageService.getReadingProgress(
            mangaSlug: "test-manga",
            chapterNumber: 9
        )

        XCTAssertNotNil(retrievedProgress)
        XCTAssertEqual(retrievedProgress?.pageNumber, 5)

        // 4. Marcar manga como favorito
        storageService.saveFavorite(mangaSlug: "test-manga")

        // 5. Verificar favoritos
        let favorites = storageService.getFavorites()
        XCTAssertTrue(favorites.contains("test-manga"))

        // 6. Simular guardado de capítulo descargado
        let pages = mockChapters[0].number == 10 ? [
            MangaPage(url: "page1.jpg", index: 0),
            MangaPage(url: "page2.jpg", index: 1)
        ] : []

        let downloadedChapter = DownloadedChapter(
            mangaSlug: "test-manga",
            mangaTitle: "Test Manga",
            chapterNumber: 10,
            pages: pages,
            downloadedAt: Date()
        )

        storageService.saveDownloadedChapter(downloadedChapter)

        // 7. Verificar capítulo descargado
        XCTAssertTrue(storageService.isChapterDownloaded(
            mangaSlug: "test-manga",
            chapterNumber: 10
        ))

        // 8. Obtener todos los datos relacionados
        let allProgress = storageService.getAllReadingProgress()
        let lastRead = storageService.getLastReadChapter(mangaSlug: "test-manga")
        let downloadedChapters = storageService.getDownloadedChapters()

        XCTAssertEqual(allProgress.count, 1)
        XCTAssertNotNil(lastRead)
        XCTAssertEqual(downloadedChapters.count, 1)
    }

    func testChapterDownloadFlow() async throws {
        // Simular el flujo completo de descarga de un capítulo

        // 1. Crear imágenes de prueba
        let image1 = createTestImage(color: .red, size: CGSize(width: 800, height: 1200))
        let image2 = createTestImage(color: .blue, size: CGSize(width: 800, height: 1200))
        let image3 = createTestImage(color: .green, size: CGSize(width: 800, height: 1200))

        // 2. Guardar imágenes
        let url1 = try await storageService.saveImage(
            image1,
            mangaSlug: "test-manga",
            chapterNumber: 1,
            pageIndex: 0
        )

        let url2 = try await storageService.saveImage(
            image2,
            mangaSlug: "test-manga",
            chapterNumber: 1,
            pageIndex: 1
        )

        let url3 = try await storageService.saveImage(
            image3,
            mangaSlug: "test-manga",
            chapterNumber: 1,
            pageIndex: 2
        )

        // 3. Verificar que las imágenes se guardaron
        XCTAssertTrue(FileManager.default.fileExists(atPath: url1.path))
        XCTAssertTrue(FileManager.default.fileExists(atPath: url2.path))
        XCTAssertTrue(FileManager.default.fileExists(atPath: url3.path))

        // 4. Crear objeto de capítulo descargado
        let pages = [
            MangaPage(url: url1.absoluteString, index: 0, isCached: true),
            MangaPage(url: url2.absoluteString, index: 1, isCached: true),
            MangaPage(url: url3.absoluteString, index: 2, isCached: true)
        ]

        let downloadedChapter = DownloadedChapter(
            mangaSlug: "test-manga",
            mangaTitle: "Test Manga",
            chapterNumber: 1,
            pages: pages,
            downloadedAt: Date()
        )

        // 5. Guardar metadatos del capítulo
        storageService.saveDownloadedChapter(downloadedChapter)

        // 6. Verificar que el capítulo está marcado como descargado
        XCTAssertTrue(storageService.isChapterDownloaded(
            mangaSlug: "test-manga",
            chapterNumber: 1
        ))

        // 7. Recuperar el capítulo descargado
        let retrieved = storageService.getDownloadedChapter(
            mangaSlug: "test-manga",
            chapterNumber: 1
        )

        XCTAssertNotNil(retrieved)
        XCTAssertEqual(retrieved?.pages.count, 3)

        // 8. Cargar imágenes desde disco
        let loadedImage1 = storageService.loadImage(
            mangaSlug: "test-manga",
            chapterNumber: 1,
            pageIndex: 0
        )

        let loadedImage2 = storageService.loadImage(
            mangaSlug: "test-manga",
            chapterNumber: 1,
            pageIndex: 1
        )

        let loadedImage3 = storageService.loadImage(
            mangaSlug: "test-manga",
            chapterNumber: 1,
            pageIndex: 2
        )

        XCTAssertNotNil(loadedImage1)
        XCTAssertNotNil(loadedImage2)
        XCTAssertNotNil(loadedImage3)
    }

    func testReadingProgressTrackingFlow() async throws {
        // Simular el flujo de seguimiento de progreso de lectura

        let mangaSlug = "tower-of-god"

        // 1. Usuario comienza a leer capítulo 1
        let progress1 = ReadingProgress(
            mangaSlug: mangaSlug,
            chapterNumber: 1,
            pageNumber: 0,
            timestamp: Date()
        )
        storageService.saveReadingProgress(progress1)

        // 2. Usuario avanza a página 5
        let progress2 = ReadingProgress(
            mangaSlug: mangaSlug,
            chapterNumber: 1,
            pageNumber: 5,
            timestamp: Date().addingTimeInterval(60) // 1 minuto después
        )
        storageService.saveReadingProgress(progress2)

        // 3. Usuario cambia al capítulo 2
        let progress3 = ReadingProgress(
            mangaSlug: mangaSlug,
            chapterNumber: 2,
            pageNumber: 0,
            timestamp: Date().addingTimeInterval(120) // 2 minutos después
        )
        storageService.saveReadingProgress(progress3)

        // 4. Usuario lee capítulo 2 hasta página 10
        let progress4 = ReadingProgress(
            mangaSlug: mangaSlug,
            chapterNumber: 2,
            pageNumber: 10,
            timestamp: Date().addingTimeInterval(300) // 5 minutos después
        )
        storageService.saveReadingProgress(progress4)

        // 5. Verificar progreso del capítulo 1
        let ch1Progress = storageService.getReadingProgress(
            mangaSlug: mangaSlug,
            chapterNumber: 1
        )
        XCTAssertEqual(ch1Progress?.pageNumber, 5)

        // 6. Verificar progreso del capítulo 2
        let ch2Progress = storageService.getReadingProgress(
            mangaSlug: mangaSlug,
            chapterNumber: 2
        )
        XCTAssertEqual(ch2Progress?.pageNumber, 10)

        // 7. Verificar último capítulo leído
        let lastRead = storageService.getLastReadChapter(mangaSlug: mangaSlug)
        XCTAssertEqual(lastRead?.chapterNumber, 2)

        // 8. Verificar que el capítulo se marca como completado
        XCTAssertTrue(ch2Progress?.isCompleted ?? false)
    }

    func testFavoriteManagementFlow() {
        // Simular el flujo de gestión de favoritos

        let mangaSlugs = [
            "solo-leveling",
            "tower-of-god",
            "the-beginning-after-the-end",
            "omniscient-reader"
        ]

        // 1. Agregar varios favoritos
        mangaSlugs.forEach { slug in
            storageService.saveFavorite(mangaSlug: slug)
        }

        // 2. Verificar que todos están en favoritos
        let favorites = storageService.getFavorites()
        XCTAssertEqual(favorites.count, 4)

        mangaSlugs.forEach { slug in
            XCTAssertTrue(storageService.isFavorite(mangaSlug: slug))
        }

        // 3. Remover uno
        storageService.removeFavorite(mangaSlug: "tower-of-god")

        // 4. Verificar que se eliminó
        XCTAssertFalse(storageService.isFavorite(mangaSlug: "tower-of-god"))
        XCTAssertEqual(storageService.getFavorites().count, 3)

        // 5. Intentar agregar duplicado
        storageService.saveFavorite(mangaSlug: "solo-leveling")

        // 6. Verificar que no se duplicó
        let updatedFavorites = storageService.getFavorites()
        XCTAssertEqual(updatedFavorites.count, 3)

        // 7. Contar ocurrencias
        let soloLevelingCount = updatedFavorites.filter { $0 == "solo-leveling" }.count
        XCTAssertEqual(soloLevelingCount, 1, "Should only appear once")
    }

    // MARK: - Multi-Manga Scenarios

    func testMultipleMangasProgressTracking() async throws {
        // Simular seguimiento de progreso para múltiples mangas

        let mangas = [
            "solo-leveling",
            "tower-of-god",
            "the-beginning-after-the-end"
        ]

        // Agregar progreso para cada manga
        for (index, manga) in mangas.enumerated() {
            let progress = ReadingProgress(
                mangaSlug: manga,
                chapterNumber: index + 1,
                pageNumber: (index + 1) * 10,
                timestamp: Date().addingTimeInterval(Double(index * 100))
            )
            storageService.saveReadingProgress(progress)
        }

        // Verificar progreso individual
        for (index, manga) in mangas.enumerated() {
            let progress = storageService.getLastReadChapter(mangaSlug: manga)
            XCTAssertNotNil(progress)
            XCTAssertEqual(progress?.chapterNumber, index + 1)
        }

        // Verificar todo el progreso
        let allProgress = storageService.getAllReadingProgress()
        XCTAssertEqual(allProgress.count, 3)
    }

    func testMultipleChapterDownloads() async throws {
        // Simular descarga de múltiples capítulos de diferentes mangas

        let downloads = [
            ("manga1", 1),
            ("manga1", 2),
            ("manga2", 1),
            ("manga2", 3),
            ("manga3", 5)
        ]

        // Crear y guardar capítulos descargados
        for (manga, chapter) in downloads {
            let chapter = DownloadedChapter(
                mangaSlug: manga,
                mangaTitle: "Manga \(manga)",
                chapterNumber: chapter,
                pages: [],
                downloadedAt: Date()
            )
            storageService.saveDownloadedChapter(chapter)
        }

        // Verificar todos los capítulos descargados
        let allDownloaded = storageService.getDownloadedChapters()
        XCTAssertEqual(allDownloaded.count, 5)

        // Verificar capítulos por manga
        let manga1Chapters = allDownloaded.filter { $0.mangaSlug == "manga1" }
        XCTAssertEqual(manga1Chapters.count, 2)

        let manga2Chapters = allDownloaded.filter { $0.mangaSlug == "manga2" }
        XCTAssertEqual(manga2Chapters.count, 2)

        let manga3Chapters = allDownloaded.filter { $0.mangaSlug == "manga3" }
        XCTAssertEqual(manga3Chapters.count, 1)
    }

    // MARK: - Error Handling Scenarios

    func testDownloadFlowWithMissingImages() async throws {
        // Simular descarga con imágenes faltantes

        // Guardar solo algunas imágenes
        let image1 = createTestImage(color: .red, size: CGSize(width: 800, height: 1200))
        _ = try await storageService.saveImage(
            image1,
            mangaSlug: "test-manga",
            chapterNumber: 1,
            pageIndex: 0
        )

        // Intentar cargar imagen que no existe
        let missingImage = storageService.loadImage(
            mangaSlug: "test-manga",
            chapterNumber: 1,
            pageIndex: 1
        )

        XCTAssertNil(missingImage, "Missing image should return nil")

        // Verificar que la imagen existente sí se carga
        let existingImage = storageService.loadImage(
            mangaSlug: "test-manga",
            chapterNumber: 1,
            pageIndex: 0
        )

        XCTAssertNotNil(existingImage, "Existing image should load successfully")
    }

    func testStorageCleanupFlow() async throws {
        // Simular flujo de limpieza de almacenamiento

        // 1. Llenar almacenamiento con datos
        for i in 0..<5 {
            let image = createTestImage(color: .blue, size: CGSize(width: 800, height: 1200))
            _ = try await storageService.saveImage(
                image,
                mangaSlug: "test",
                chapterNumber: i,
                pageIndex: 0
            )

            let chapter = DownloadedChapter(
                mangaSlug: "test",
                mangaTitle: "Test",
                chapterNumber: i,
                pages: [],
                downloadedAt: Date()
            )
            storageService.saveDownloadedChapter(chapter)
        }

        // Verificar que hay datos
        XCTAssertGreaterThan(storageService.getStorageSize(), 0)
        XCTAssertEqual(storageService.getDownloadedChapters().count, 5)

        // 2. Limpiar todo
        storageService.clearAllDownloads()

        // 3. Verificar que todo se eliminó
        XCTAssertEqual(storageService.getStorageSize(), 0)
        XCTAssertEqual(storageService.getDownloadedChapters().count, 0)
    }

    // MARK: - Data Persistence Tests

    func testDataPersistenceAcrossOperations() async throws {
        // Verificar que los datos persisten a través de múltiples operaciones

        // 1. Guardar datos iniciales
        storageService.saveFavorite(mangaSlug: "persistent-manga")

        let progress1 = ReadingProgress(
            mangaSlug: "persistent-manga",
            chapterNumber: 1,
            pageNumber: 5,
            timestamp: Date()
        )
        storageService.saveReadingProgress(progress1)

        // 2. Verificar que existen
        XCTAssertTrue(storageService.isFavorite(mangaSlug: "persistent-manga"))
        XCTAssertNotNil(storageService.getReadingProgress(
            mangaSlug: "persistent-manga",
            chapterNumber: 1
        ))

        // 3. Realizar operaciones intermedias
        storageService.saveFavorite(mangaSlug: "temp-manga")
        storageService.removeFavorite(mangaSlug: "temp-manga")

        // 4. Verificar que los datos originales persistieron
        XCTAssertTrue(storageService.isFavorite(mangaSlug: "persistent-manga"))
        let originalProgress = storageService.getReadingProgress(
            mangaSlug: "persistent-manga",
            chapterNumber: 1
        )
        XCTAssertEqual(originalProgress?.pageNumber, 5)
    }

    // MARK: - Concurrent Operations Tests

    func testConcurrentFavoriteOperations() {
        // Probar operaciones concurrentes en favoritos
        let expectations = (0..<10).map { _ in
            XCTestExpectation(description: "Favorite operation")
        }

        let queue = DispatchQueue.global(qos: .userInitiated)

        for i in 0..<10 {
            queue.async {
                self.storageService.saveFavorite(mangaSlug: "manga-\(i)")
                expectations[i].fulfill()
            }
        }

        wait(for: expectations, timeout: 5.0)

        let favorites = storageService.getFavorites()
        XCTAssertEqual(favorites.count, 10)
    }

    func testConcurrentProgressOperations() {
        // Probar operaciones concurrentes de progreso
        let expectations = (0..<10).map { _ in
            XCTestExpectation(description: "Progress operation")
        }

        let queue = DispatchQueue.global(qos: .userInitiated)

        for i in 0..<10 {
            queue.async {
                let progress = ReadingProgress(
                    mangaSlug: "manga-\(i % 3)",
                    chapterNumber: i,
                    pageNumber: i * 2,
                    timestamp: Date()
                )
                self.storageService.saveReadingProgress(progress)
                expectations[i].fulfill()
            }
        }

        wait(for: expectations, timeout: 5.0)

        let allProgress = storageService.getAllReadingProgress()
        XCTAssertEqual(allProgress.count, 10)
    }

    func testConcurrentImageOperations() async throws {
        // Probar guardado concurrente de imágenes
        await withTaskGroup(of: URL.self) { group in
            for i in 0..<20 {
                group.addTask {
                    let image = self.createTestImage(
                        color: .red,
                        size: CGSize(width: 800, height: 1200)
                    )
                    return try! await self.storageService.saveImage(
                        image,
                        mangaSlug: "concurrent-test",
                        chapterNumber: 1,
                        pageIndex: i
                    )
                }
            }
        }

        // Verificar que todas las imágenes se guardaron
        for i in 0..<20 {
            let image = storageService.loadImage(
                mangaSlug: "concurrent-test",
                chapterNumber: 1,
                pageIndex: i
            )
            XCTAssertNotNil(image, "Image at index \(i) should exist")
        }
    }

    // MARK: - Large Scale Tests

    func testLargeScaleFavoriteOperations() {
        // Probar con muchos favoritos
        let count = 1000

        measure {
            for i in 0..<count {
                storageService.saveFavorite(mangaSlug: "manga-\(i)")
            }
        }

        let favorites = storageService.getFavorites()
        XCTAssertEqual(favorites.count, count)
    }

    func testLargeScaleProgressOperations() {
        // Probar con mucho progreso de lectura
        let count = 500

        measure {
            for i in 0..<count {
                let progress = ReadingProgress(
                    mangaSlug: "manga-\(i % 50)",
                    chapterNumber: i,
                    pageNumber: i,
                    timestamp: Date()
                )
                storageService.saveReadingProgress(progress)
            }
        }

        let allProgress = storageService.getAllReadingProgress()
        XCTAssertEqual(allProgress.count, count)
    }

    // MARK: - Helper Methods

    private func createTestImage(color: UIColor, size: CGSize) -> UIImage {
        let renderer = UIGraphicsImageRenderer(size: size)
        return renderer.image { context in
            color.setFill()
            context.fill(CGRect(origin: .zero, size: size))
        }
    }
}
