import XCTest
@testable import MangaReader

/// Tests unitarios para DownloadManager
///
/// Estos tests deben agregarse a tu target de tests en Xcode
@MainActor
class DownloadManagerTests: XCTestCase {

    var downloadManager: DownloadManager!
    var storage: StorageService!

    override func setUp() async throws {
        downloadManager = DownloadManager.shared
        storage = StorageService.shared

        // Limpiar estado antes de cada test
        downloadManager.cancelAllDownloads()
        downloadManager.clearCompletedHistory()
        downloadManager.clearFailedHistory()
    }

    override func tearDown() async throws {
        // Limpiar estado después de cada test
        downloadManager.cancelAllDownloads()
    }

    // MARK: - Test: Descarga Individual

    func testDownloadSingleChapter() async throws {
        // Given
        let mangaSlug = "test-manga"
        let mangaTitle = "Test Manga"
        let chapter = Chapter(
            number: 1,
            title: "Chapter 1",
            url: "https://example.com/chapter1",
            slug: "chapter-1"
        )

        // When
        try await downloadManager.downloadChapter(
            mangaSlug: mangaSlug,
            mangaTitle: mangaTitle,
            chapter: chapter
        )

        // Then
        XCTAssertEqual(downloadManager.activeDownloads.count, 0, "No debe haber descargas activas")
        XCTAssertEqual(downloadManager.completedDownloads.count, 1, "Debe haber una descarga completada")
        XCTAssertTrue(storage.isChapterDownloaded(mangaSlug: mangaSlug, chapterNumber: 1), "El capítulo debe estar descargado")
    }

    func testDownloadAlreadyDownloadedChapter() async throws {
        // Given
        let mangaSlug = "test-manga"
        let mangaTitle = "Test Manga"
        let chapter = Chapter(
            number: 1,
            title: "Chapter 1",
            url: "https://example.com/chapter1",
            slug: "chapter-1"
        )

        // Descargar por primera vez
        try await downloadManager.downloadChapter(
            mangaSlug: mangaSlug,
            mangaTitle: mangaTitle,
            chapter: chapter
        )

        // When & Then - Intentar descargar nuevamente
        do {
            try await downloadManager.downloadChapter(
                mangaSlug: mangaSlug,
                mangaTitle: mangaTitle,
                chapter: chapter
            )
            XCTFail("Debe lanzar error alreadyDownloaded")
        } catch DownloadError.alreadyDownloaded {
            // Éxito - Error esperado
        } catch {
            XCTFail("Error incorrecto: \(error)")
        }
    }

    // MARK: - Test: Descarga Múltiple

    func testDownloadMultipleChapters() async throws {
        // Given
        let mangaSlug = "test-manga"
        let mangaTitle = "Test Manga"
        let chapters = [
            Chapter(number: 1, title: "Chapter 1", url: "https://example.com/ch1", slug: "ch1"),
            Chapter(number: 2, title: "Chapter 2", url: "https://example.com/ch2", slug: "ch2"),
            Chapter(number: 3, title: "Chapter 3", url: "https://example.com/ch3", slug: "ch3")
        ]

        // When
        await downloadManager.downloadChapters(
            mangaSlug: mangaSlug,
            mangaTitle: mangaTitle,
            chapters: chapters
        )

        // Esperar a que todas terminen
        try await Task.sleep(nanoseconds: 10_000_000_000) // 10 segundos

        // Then
        XCTAssertEqual(downloadManager.completedDownloads.count, 3, "Debe haber 3 descargas completadas")
    }

    // MARK: - Test: Cancelación

    func testCancelDownload() async throws {
        // Given
        let mangaSlug = "test-manga"
        let mangaTitle = "Test Manga"
        let chapter = Chapter(
            number: 1,
            title: "Chapter 1",
            url: "https://example.com/chapter1",
            slug: "chapter-1"
        )

        // Iniciar descarga en background
        Task {
            try? await downloadManager.downloadChapter(
                mangaSlug: mangaSlug,
                mangaTitle: mangaTitle,
                chapter: chapter
            )
        }

        // Esperar un poco para que inicie
        try await Task.sleep(nanoseconds: 500_000_000) // 0.5 segundos

        // When
        guard let task = downloadManager.activeDownloads.first else {
            XCTFail("Debe haber una descarga activa")
            return
        }
        downloadManager.cancelDownload(taskId: task.id)

        // Then
        XCTAssertEqual(downloadManager.activeDownloads.count, 0, "No debe haber descargas activas")
        XCTAssertFalse(storage.isChapterDownloaded(mangaSlug: mangaSlug, chapterNumber: 1), "El capítulo no debe estar descargado")
    }

    func testCancelAllDownloads() async throws {
        // Given
        let mangaSlug = "test-manga"
        let mangaTitle = "Test Manga"
        let chapters = [
            Chapter(number: 1, title: "Chapter 1", url: "https://example.com/ch1", slug: "ch1"),
            Chapter(number: 2, title: "Chapter 2", url: "https://example.com/ch2", slug: "ch2"),
            Chapter(number: 3, title: "Chapter 3", url: "https://example.com/ch3", slug: "ch3")
        ]

        // Iniciar descargas
        Task {
            await downloadManager.downloadChapters(
                mangaSlug: mangaSlug,
                mangaTitle: mangaTitle,
                chapters: chapters
            )
        }

        // Esperar un poco
        try await Task.sleep(nanoseconds: 500_000_000) // 0.5 segundos

        // When
        downloadManager.cancelAllDownloads()

        // Then
        XCTAssertEqual(downloadManager.activeDownloads.count, 0, "No debe haber descargas activas")
    }

    // MARK: - Test: Progreso

    func testDownloadProgress() async throws {
        // Given
        let mangaSlug = "test-manga"
        let mangaTitle = "Test Manga"
        let chapter = Chapter(
            number: 1,
            title: "Chapter 1",
            url: "https://example.com/chapter1",
            slug: "chapter-1"
        )

        // Expectation para progreso
        let progressExpectation = expectation(description: "Progreso actualizado")

        // Observer de progreso
        let cancellable = downloadManager.$activeDownloads.sink { tasks in
            if let task = tasks.first {
                if task.progress > 0 && task.progress < 1 {
                    progressExpectation.fulfill()
                }
            }
        }

        // When
        Task {
            try? await downloadManager.downloadChapter(
                mangaSlug: mangaSlug,
                mangaTitle: mangaTitle,
                chapter: chapter
            )
        }

        // Then
        await fulfillment(of: [progressExpectation], timeout: 5.0)
        cancellable.cancel()
    }

    // MARK: - Test: Errores

    func testDownloadWithInvalidURL() async throws {
        // Given
        let mangaSlug = "test-manga"
        let mangaTitle = "Test Manga"
        let chapter = Chapter(
            number: 1,
            title: "Chapter 1",
            url: "invalid-url",
            slug: "invalid-chapter"
        )

        // When & Then
        do {
            try await downloadManager.downloadChapter(
                mangaSlug: mangaSlug,
                mangaTitle: mangaTitle,
                chapter: chapter
            )
            XCTFail("Debe lanzar error")
        } catch {
            // Éxito - Se espera un error
            XCTAssertNotNil(error, "Debe haber un error")
        }
    }

    // MARK: - Test: Concurrencia

    func testMaxConcurrentDownloads() async throws {
        // Given
        let mangaSlug = "test-manga"
        let mangaTitle = "Test Manga"
        let chapters = (1...10).map { i in
            Chapter(number: i, title: "Chapter \(i)", url: "https://example.com/ch\(i)", slug: "ch\(i)")
        }

        // When
        Task {
            await downloadManager.downloadChapters(
                mangaSlug: mangaSlug,
                mangaTitle: mangaTitle,
                chapters: chapters
            )
        }

        // Esperar un poco
        try await Task.sleep(nanoseconds: 1_000_000_000) // 1 segundo

        // Then - No debe exceder el máximo de descargas concurrentes
        XCTAssertLessThanOrEqual(
            downloadManager.activeDownloads.count,
            3,
            "No debe haber más de 3 descargas activas simultáneas"
        )
    }

    // MARK: - Test: Storage

    func testStorageIntegration() async throws {
        // Given
        let mangaSlug = "test-manga"
        let mangaTitle = "Test Manga"
        let chapter = Chapter(
            number: 1,
            title: "Chapter 1",
            url: "https://example.com/chapter1",
            slug: "chapter-1"
        )

        // When
        try await downloadManager.downloadChapter(
            mangaSlug: mangaSlug,
            mangaTitle: mangaTitle,
            chapter: chapter
        )

        // Then - Verificar integración con StorageService
        XCTAssertTrue(
            storage.isChapterDownloaded(mangaSlug: mangaSlug, chapterNumber: 1),
            "StorageService debe reportar el capítulo como descargado"
        )

        XCTAssertNotNil(
            storage.getDownloadedChapter(mangaSlug: mangaSlug, chapterNumber: 1),
            "StorageService debe retornar metadata del capítulo"
        )

        let chapterDir = storage.getChapterDirectory(mangaSlug: mangaSlug, chapterNumber: 1)
        XCTAssertTrue(
            FileManager.default.fileExists(atPath: chapterDir.path),
            "El directorio del capítulo debe existir"
        )
    }

    func testClearStorage() async throws {
        // Given
        let mangaSlug = "test-manga"
        let mangaTitle = "Test Manga"
        let chapter = Chapter(
            number: 1,
            title: "Chapter 1",
            url: "https://example.com/chapter1",
            slug: "chapter-1"
        )

        // Descargar capítulo
        try await downloadManager.downloadChapter(
            mangaSlug: mangaSlug,
            mangaTitle: mangaTitle,
            chapter: chapter
        )

        // When
        storage.deleteDownloadedChapter(mangaSlug: mangaSlug, chapterNumber: 1)

        // Then
        XCTAssertFalse(
            storage.isChapterDownloaded(mangaSlug: mangaSlug, chapterNumber: 1),
            "El capítulo no debe estar descargado después de eliminarlo"
        )

        XCTAssertEqual(
            storage.getStorageSize(),
            0,
            "El tamaño de almacenamiento debe ser 0"
        )
    }

    // MARK: - Test: Estadísticas

    func testDownloadStats() async throws {
        // Given
        let initialStats = downloadManager.downloadStats

        // When
        let mangaSlug = "test-manga"
        let mangaTitle = "Test Manga"
        let chapter = Chapter(
            number: 1,
            title: "Chapter 1",
            url: "https://example.com/chapter1",
            slug: "chapter-1"
        )

        try await downloadManager.downloadChapter(
            mangaSlug: mangaSlug,
            mangaTitle: mangaTitle,
            chapter: chapter
        )

        let finalStats = downloadManager.downloadStats

        // Then
        XCTAssertEqual(
            finalStats.completedDownloads,
            initialStats.completedDownloads + 1,
            "Las descargas completadas deben incrementar en 1"
        )
    }

    // MARK: - Test: Historiales

    func testClearCompletedHistory() async throws {
        // Given
        let mangaSlug = "test-manga"
        let mangaTitle = "Test Manga"
        let chapter = Chapter(
            number: 1,
            title: "Chapter 1",
            url: "https://example.com/chapter1",
            slug: "chapter-1"
        )

        try await downloadManager.downloadChapter(
            mangaSlug: mangaSlug,
            mangaTitle: mangaTitle,
            chapter: chapter
        )

        // When
        downloadManager.clearCompletedHistory()

        // Then
        XCTAssertEqual(
            downloadManager.completedDownloads.count,
            0,
            "El historial de completadas debe estar vacío"
        )
    }
}

/// Tests para DownloadTask
@MainActor
class DownloadTaskTests: XCTestCase {

    func testDownloadTaskInitialization() {
        // Given
        let task = DownloadTask(
            mangaSlug: "test-manga",
            mangaTitle: "Test Manga",
            chapterNumber: 1,
            chapterTitle: "Chapter 1",
            imageURLs: ["url1", "url2", "url3"]
        )

        // Then
        XCTAssertEqual(task.mangaSlug, "test-manga")
        XCTAssertEqual(task.chapterNumber, 1)
        XCTAssertEqual(task.imageURLs.count, 3)
        XCTAssertEqual(task.downloadedPages, 0)
        XCTAssertEqual(task.progress, 0.0)
    }

    func testDownloadTaskProgress() {
        // Given
        let task = DownloadTask(
            mangaSlug: "test-manga",
            mangaTitle: "Test Manga",
            chapterNumber: 1,
            chapterTitle: "Chapter 1",
            imageURLs: ["url1", "url2", "url3", "url4", "url5"]
        )

        // When
        task.updateProgress(downloaded: 2, total: 5)

        // Then
        XCTAssertEqual(task.downloadedPages, 2)
        XCTAssertEqual(task.progress, 0.4, accuracy: 0.01)
    }

    func testDownloadTaskCompletion() {
        // Given
        let task = DownloadTask(
            mangaSlug: "test-manga",
            mangaTitle: "Test Manga",
            chapterNumber: 1,
            chapterTitle: "Chapter 1",
            imageURLs: ["url1", "url2", "url3"]
        )

        // When
        task.complete()

        // Then
        XCTAssertTrue(task.state.isCompleted)
    }

    func testDownloadTaskCancellation() {
        // Given
        let task = DownloadTask(
            mangaSlug: "test-manga",
            mangaTitle: "Test Manga",
            chapterNumber: 1,
            chapterTitle: "Chapter 1",
            imageURLs: ["url1", "url2", "url3"]
        )

        // When
        task.cancel()

        // Then
        XCTAssertTrue(task.isCancelled)
        XCTAssertTrue(task.state.isTerminal)
    }
}

/// Tests para Extensions
class DownloadExtensionsTests: XCTestCase {

    func testDownloadTaskFormattedSize() {
        // Given
        let task = DownloadTask(
            mangaSlug: "test-manga",
            mangaTitle: "Test Manga",
            chapterNumber: 1,
            chapterTitle: "Chapter 1",
            imageURLs: Array(repeating: "url", count: 10)
        )

        // When
        let size = task.formattedSize

        // Then
        XCTAssertFalse(size.isEmpty, "El tamaño formateado no debe estar vacío")
    }

    func testUIImageOptimization() {
        // Given
        let imageSize = CGSize(width: 3000, height: 4000)
        UIGraphicsBeginImageContextWithOptions(imageSize, false, 1.0)
        let context = UIGraphicsGetCurrentContext()
        context?.setFillColor(UIColor.blue.cgColor)
        context?.fill(CGRect(origin: .zero, size: imageSize))
        let largeImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        guard let image = largeImage else {
            XCTFail("No se pudo crear la imagen de prueba")
            return
        }

        // When
        let optimizedData = image.optimizedForStorage()

        // Then
        XCTAssertNotNil(optimizedData, "Debe generar datos optimizados")
        XCTAssertTrue(optimizedData!.count > 0, "Los datos no deben estar vacíos")
    }
}
