import XCTest
@testable import MangaReader

/// Ejemplos de cómo escribir tests para el proyecto MangaReader
/// Este archivo serve como guía de referencia para crear nuevos tests

// MARK: - Ejemplo 1: Test Unitario Básico

final class ExampleTests: XCTestCase {

    // MARK: - Ejemplo: Test simple de modelo

    func testEjemploModeloSimple() {
        // Arrange: Preparar los datos
        let manga = Manga(
            slug: "test",
            title: "Test Manga",
            description: "Desc",
            genres: ["Action"],
            status: "PUBLICANDOSE",
            url: "https://example.com"
        )

        // Act: Ejecutar la acción (si es necesario)
        let displayTitle = manga.title

        // Assert: Verificar el resultado
        XCTAssertEqual(displayTitle, "Test Manga")
        XCTAssertEqual(manga.slug, "test")
        XCTAssertTrue(manga.genres.contains("Action"))
    }

    // MARK: - Ejemplo: Test con async/await

    func testEjemploAsync() async throws {
        // Arrange
        let storageService = StorageService.shared
        let image = createTestImage()

        // Act
        let url = try await storageService.saveImage(
            image,
            mangaSlug: "test",
            chapterNumber: 1,
            pageIndex: 0
        )

        // Assert
        XCTAssertTrue(FileManager.default.fileExists(atPath: url.path))
    }

    // MARK: - Ejemplo: Test de error

    func testEjemploManejoDeError() async {
        // Arrange
        let storageService = StorageService.shared

        // Act & Assert
        do {
            _ = try await storageService.saveImage(
                UIImage(), // Imagen vacía
                mangaSlug: "test",
                chapterNumber: 1,
                pageIndex: 0
            )
            XCTFail("Debería haber lanzado un error")
        } catch {
            XCTAssertNotNil(error)
        }
    }

    // MARK: - Ejemplo: Test con helpers

    func testEjemploConHelpers() {
        // Usar TestDataFactory para crear datos de prueba
        let manga = TestDataFactory.createManga(
            slug: "mi-manga",
            title: "Mi Manga",
            genres: ["Action", "Fantasy"]
        )

        // Usar AssertionHelpers
        AssertionHelpers.assertValidManga(manga)
        XCTAssertTrue(manga.genres.count == 2)
    }

    // MARK: - Ejemplo: Test de múltiples escenarios

    func testEjemploMultiplesEscenarios() {
        // Escenario 1: Manga publicado
        let publishedManga = TestDataFactory.createManga(
            status: "PUBLICANDOSE"
        )
        XCTAssertEqual(publishedManga.displayStatus, "En publicación")

        // Escenario 2: Manga finalizado
        let finishedManga = TestDataFactory.createManga(
            status: "FINALIZADO"
        )
        XCTAssertEqual(finishedManga.displayStatus, "Finalizado")

        // Escenario 3: Manga en pausa
        let pausedManga = TestDataFactory.createManga(
            status: "EN_PAUSA"
        )
        XCTAssertEqual(pausedManga.displayStatus, "En pausa")
    }

    // MARK: - Ejemplo: Test de performance

    func testEjemploPerformance() {
        let manga = TestDataFactory.createManga()

        // Medir cuánto tarda en codificar 1000 veces
        measure {
            for _ in 0..<1000 {
                _ = try? JSONEncoder().encode(manga)
            }
        }
    }

    // MARK: - Ejemplo: Test con setup/teardown

    var storageService: StorageService!

    override func setUp() async throws {
        try await super.setUp()
        // Se ejecuta antes de cada test
        storageService = StorageService.shared
        StorageTestHelpers.clearAllStorage()
    }

    override func tearDown() async throws {
        // Se ejecuta después de cada test
        StorageTestHelpers.clearAllStorage()
        storageService = nil
        try await super.tearDown()
    }

    func testEjemploConSetup() {
        // El storageService ya está inicializado y limpio
        storageService.saveFavorite(mangaSlug: "test")
        XCTAssertTrue(storageService.isFavorite(mangaSlug: "test"))
    }

    // MARK: - Ejemplo: Test de integración

    func testEjemploIntegracion() async throws {
        // Simular flujo completo del usuario

        // 1. Usuario busca un manga
        let manga = TestDataFactory.createManga(slug: "tower-of-god")

        // 2. Usuario lo agrega a favoritos
        storageService.saveFavorite(mangaSlug: manga.slug)
        XCTAssertTrue(storageService.isFavorite(mangaSlug: manga.slug))

        // 3. Usuario comienza a leer
        let progress = ReadingProgress(
            mangaSlug: manga.slug,
            chapterNumber: 1,
            pageNumber: 0,
            timestamp: Date()
        )
        storageService.saveReadingProgress(progress)

        // 4. Verificar que todo se guardó correctamente
        let retrieved = storageService.getReadingProgress(
            mangaSlug: manga.slug,
            chapterNumber: 1
        )
        XCTAssertNotNil(retrieved)
        XCTAssertEqual(retrieved?.pageNumber, 0)
    }

    // MARK: - Ejemplo: Test concurrente

    func testEjemploConcurrencia() async throws {
        // Crear múltiples tareas concurrentes
        await withTaskGroup(of: Void.self) { group in
            for i in 0..<10 {
                group.addTask {
                    let manga = TestDataFactory.createManga(slug: "manga-\(i)")
                    self.storageService.saveFavorite(mangaSlug: manga.slug)
                }
            }
        }

        // Verificar que todas se guardaron
        let favorites = storageService.getFavorites()
        XCTAssertEqual(favorites.count, 10)
    }

    // MARK: - Ejemplo: Test con mock

    func testEjemploConMock() {
        // Mock de respuesta de JavaScript
        let mockResponse: [[String: Any]] = [
            ["number": 1, "title": "Chapter 1", "url": "url1", "slug": "slug1"],
            ["number": 2, "title": "Chapter 2", "url": "url2", "slug": "slug2"]
        ]

        // Parsear respuesta mock
        let chapters = mockResponse.compactMap { dict -> Chapter? in
            guard let number = dict["number"] as? Int,
                  let title = dict["title"] as? String,
                  let url = dict["url"] as? String,
                  let slug = dict["slug"] as? String else {
                return nil
            }
            return Chapter(number: number, title: title, url: url, slug: slug)
        }

        // Verificar parsing
        XCTAssertEqual(chapters.count, 2)
        XCTAssertEqual(chapters[0].number, 1)
        XCTAssertEqual(chapters[1].number, 2)
    }

    // MARK: - Ejemplo: Test de edge cases

    func testEjemploEdgeCases() {
        // Caso 1: String vacío
        let emptyManga = TestDataFactory.createManga(title: "")
        XCTAssertEqual(emptyManga.title, "")

        // Caso 2: Array vacío
        let noGenresManga = TestDataFactory.createManga(genres: [])
        XCTAssertTrue(noGenresManga.genres.isEmpty)

        // Caso 3: Valor negativo
        let chapter = TestDataFactory.createChapter(number: -1)
        XCTAssertEqual(chapter.number, -1)

        // Caso 4: Caracteres especiales
        let specialSlug = "manga-áéíóú-ñ-@#$"
        storageService.saveFavorite(mangaSlug: specialSlug)
        XCTAssertTrue(storageService.isFavorite(mangaSlug: specialSlug))
    }

    // MARK: - Ejemplo: Test con assertions personalizadas

    func testEjemploAssertionsPersonalizadas() {
        let manga1 = TestDataFactory.createManga(slug: "test")
        let manga2 = TestDataFactory.createManga(slug: "test")

        // Usar assert personalizado
        XCTAssertEqual(manga1, manga2)
        XCTAssertEqual(manga1.hashValue, manga2.hashValue)

        // Verificar URL válida
        AssertionHelpers.assertValidURL(manga1.url)

        // Verificar manga válido
        AssertionHelpers.assertValidManga(manga1)
    }

    // MARK: - Helpers para ejemplos

    private func createTestImage() -> UIImage {
        let size = CGSize(width: 800, height: 1200)
        UIGraphicsBeginImageContext(size)
        let context = UIGraphicsGetCurrentContext()
        context?.setFillColor(UIColor.blue.cgColor)
        context?.fill(CGRect(origin: .zero, size: size))
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return image!
    }
}

// MARK: - Plantillas de Tests

/// Plantilla para test unitario simple
/*
 func test[NombreFuncionalidad]_[Condición]_[ResultadoEsperado]() {
     // Arrange
     let [input] = [valor]

     // Act
     let result = [función](input)

     // Assert
     XCTAssertEqual(result, [esperado])
 }
 */

/// Plantilla para test asíncrono
/*
 func test[NombreFuncionalidad]_Async() async throws {
     // Arrange
     let [input] = [valor]

     // Act
     let result = try await [funciónAsync](input)

     // Assert
     XCTAssertNotNil(result)
 }
 */

/// Plantilla para test de error
/*
 func test[NombreFuncionalidad]_ThrowsError() {
     // Arrange
     let [inputInvalido] = [valor]

     // Act & Assert
     XCTAssertThrowsError(
         try [función](inputInvalido)
     ) { error in
         XCTAssertEqual(error as! [TipoError], [errorEsperado])
     }
 }
 */

/// Plantilla para test de performance
/*
 func test[NombreFuncionalidad]_Performance() {
     let [data] = [crearDatosDePrueba]

     measure {
         _ = [función](data)
     }
 }
 */

/// Plantilla para test de integración
/*
 func test[FlujoCompleto]_Integration() async throws {
     // Paso 1: [acción inicial]
     let [result1] = try await [función1]()

     // Paso 2: [acción siguiente]
     let [result2] = [función2](result1)

     // Paso 3: [verificación final]
     XCTAssertNotNil([resultFinal])
     XCTAssertTrue([condición])
 }
 */

// MARK: - Consejos para Escribir Tests

/*
 ✅ BUENOS HÁBITOS:

 1. Usa nombres descriptivos:
    - testSaveFavorite_AddsNewFavorite_WhenNotExists
    - testChapterProgress_ReturnsDouble_WhenSet

 2. Un assert por test:
    - Separa en múltiples tests si hay varios asserts
    - Usa subtests si están relacionados

 3. Arrange-Act-Assert:
    - Arrange: Prepara los datos
    - Act: Ejecuta la acción
    - Assert: Verifica el resultado

 4. Tests independientes:
    - No dependen del orden de ejecución
    - Limpian después de sí mismos

 5. Usa helpers:
    - TestDataFactory para crear objetos
    - AssertionHelpers para verificar condiciones

 ❌ MALOS HÁBITOS:

 1. Tests con múltiples asserts no relacionados
 2. Tests que dependen del orden
 3. Tests que no limpian después
 4. Tests con nombres ambiguos
 5. Tests que llaman a APIs reales
 */

// MARK: - Referencias Rápidas

/*
 COMUNES ASSERTIONS:

 - XCTAssertEqual(a, b)           - Verifica igualdad
 - XCTAssertNotEqual(a, b)        - Verifica desigualdad
 - XCTAssertTrue(condición)       - Verifica true
 - XCTAssertFalse(condición)      - Verifica false
 - XCTAssertNil(valor)            - Verifica nil
 - XCTAssertNotNil(valor)         - Verifica no nil
 - XCTAssertThrowsError(expr)     - Verifica que lanza error
 - XCTAssertNoThrow(expr)         - Verifica que NO lanza error

 ASYNC/AWAIT:

 - try await [función async]      - Ejecutar función async
 - try await Task.sleep(...)      - Esperar
 - await withTaskGroup {}         - Tareas concurrentes

 SETUP/TEARDOWN:

 - override func setUp()           - Antes de cada test
 - override func tearDown()        - Después de cada test
 - override func setUpWithError()  - Con errores
 - override func tearDownWithError() - Con errores

 PERFORMANCE:

 - measure { }                     - Medir bloques de código
 - measure(metrics: ...) { }       - Métricas específicas

 MOCKS:

 - TestDataFactory                 - Crear objetos de prueba
 - ImageTestHelpers                - Crear imágenes
 - FileSystemTestHelpers           - Operaciones de archivos
 */
