import XCTest
import UIKit
@testable import MangaReader

/// Tests para el StorageService
/// Tests para guardar/cargar favoritos, progreso de lectura y capítulos descargados
final class StorageServiceTests: XCTestCase {

    var storageService: StorageService!
    var mockUserDefaults: UserDefaults!

    // MARK: - Setup & Teardown

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

        // Crear un UserDefaults aislado para los tests
        mockUserDefaults = UserDefaults(suiteName: "test_manga_reader_\(UUID().uuidString)")!

        // Inyectar el mock UserDefaults si fuera posible (requiere modificación del StorageService)
        // Por ahora, limpiaremos UserDefaults después de cada test

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

        storageService = StorageService.shared
    }

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

        // Limpiar archivos de test
        storageService.clearAllDownloads()

        try await super.tearDown()
    }

    // MARK: - Favorites Tests

    func testSaveFavorite() {
        // Given
        let mangaSlug = "solo-leveling"

        // When
        storageService.saveFavorite(mangaSlug: mangaSlug)

        // Then
        let favorites = storageService.getFavorites()
        XCTAssertTrue(favorites.contains(mangaSlug))
        XCTAssertEqual(favorites.count, 1)
    }

    func testSaveMultipleFavorites() {
        // Given
        let slugs = ["solo-leveling", "tower-of-god", "the-beginning-after-the-end"]

        // When
        slugs.forEach { storageService.saveFavorite(mangaSlug: $0) }

        // Then
        let favorites = storageService.getFavorites()
        XCTAssertEqual(favorites.count, 3)
        XCTAssertTrue(favorites.contains("solo-leveling"))
        XCTAssertTrue(favorites.contains("tower-of-god"))
        XCTAssertTrue(favorites.contains("the-beginning-after-the-end"))
    }

    func testSaveDuplicateFavorite() {
        // Given
        let mangaSlug = "solo-leveling"

        // When
        storageService.saveFavorite(mangaSlug: mangaSlug)
        storageService.saveFavorite(mangaSlug: mangaSlug) // Intentar guardar duplicado

        // Then
        let favorites = storageService.getFavorites()
        XCTAssertEqual(favorites.count, 1, "Duplicate favorites should not be added")
        XCTAssertEqual(favorites.first, mangaSlug)
    }

    func testRemoveFavorite() {
        // Given
        let mangaSlug = "solo-leveling"
        storageService.saveFavorite(mangaSlug: mangaSlug)

        // When
        storageService.removeFavorite(mangaSlug: mangaSlug)

        // Then
        let favorites = storageService.getFavorites()
        XCTAssertFalse(favorites.contains(mangaSlug))
        XCTAssertEqual(favorites.count, 0)
    }

    func testRemoveNonExistentFavorite() {
        // Given
        storageService.saveFavorite(mangaSlug: "manga1")
        storageService.saveFavorite(mangaSlug: "manga2")

        // When
        storageService.removeFavorite(mangaSlug: "non-existent")

        // Then
        let favorites = storageService.getFavorites()
        XCTAssertEqual(favorites.count, 2, "Removing non-existent favorite should not affect others")
    }

    func testIsFavorite() {
        // Given
        let favoriteSlug = "solo-leveling"
        let nonFavoriteSlug = "tower-of-god"
        storageService.saveFavorite(mangaSlug: favoriteSlug)

        // When & Then
        XCTAssertTrue(storageService.isFavorite(mangaSlug: favoriteSlug))
        XCTAssertFalse(storageService.isFavorite(mangaSlug: nonFavoriteSlug))
    }

    func testGetFavoritesWhenEmpty() {
        // When
        let favorites = storageService.getFavorites()

        // Then
        XCTAssertTrue(favorites.isEmpty)
        XCTAssertEqual(favorites.count, 0)
    }

    // MARK: - Reading Progress Tests

    func testSaveReadingProgress() {
        // Given
        let progress = ReadingProgress(
            mangaSlug: "solo-leveling",
            chapterNumber: 50,
            pageNumber: 10,
            timestamp: Date()
        )

        // When
        storageService.saveReadingProgress(progress)

        // Then
        let retrievedProgress = storageService.getReadingProgress(
            mangaSlug: "solo-leveling",
            chapterNumber: 50
        )

        XCTAssertNotNil(retrievedProgress)
        XCTAssertEqual(retrievedProgress?.mangaSlug, "solo-leveling")
        XCTAssertEqual(retrievedProgress?.chapterNumber, 50)
        XCTAssertEqual(retrievedProgress?.pageNumber, 10)
    }

    func testSaveMultipleReadingProgress() {
        // Given
        let progress1 = ReadingProgress(
            mangaSlug: "manga1",
            chapterNumber: 1,
            pageNumber: 5,
            timestamp: Date()
        )

        let progress2 = ReadingProgress(
            mangaSlug: "manga1",
            chapterNumber: 2,
            pageNumber: 15,
            timestamp: Date()
        )

        let progress3 = ReadingProgress(
            mangaSlug: "manga2",
            chapterNumber: 1,
            pageNumber: 20,
            timestamp: Date()
        )

        // When
        storageService.saveReadingProgress(progress1)
        storageService.saveReadingProgress(progress2)
        storageService.saveReadingProgress(progress3)

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

    func testUpdateExistingReadingProgress() {
        // Given
        let initialProgress = ReadingProgress(
            mangaSlug: "solo-leveling",
            chapterNumber: 50,
            pageNumber: 10,
            timestamp: Date(timeIntervalSince1970: 1609459200)
        )

        let updatedProgress = ReadingProgress(
            mangaSlug: "solo-leveling",
            chapterNumber: 50,
            pageNumber: 25,
            timestamp: Date(timeIntervalSince1970: 1609459300)
        )

        // When
        storageService.saveReadingProgress(initialProgress)
        storageService.saveReadingProgress(updatedProgress)

        // Then
        let retrieved = storageService.getReadingProgress(
            mangaSlug: "solo-leveling",
            chapterNumber: 50
        )

        XCTAssertEqual(retrieved?.pageNumber, 25, "Progress should be updated")
        XCTAssertEqual(retrieved?.timestamp.timeIntervalSince1970, 1609459300, accuracy: 0.001)
    }

    func testGetReadingProgressWhenNotExists() {
        // When
        let progress = storageService.getReadingProgress(
            mangaSlug: "non-existent",
            chapterNumber: 999
        )

        // Then
        XCTAssertNil(progress)
    }

    func testGetLastReadChapter() {
        // Given
        let oldDate = Date(timeIntervalSince1970: 1609459200)
        let newDate = Date(timeIntervalSince1970: 1609459300)

        let progress1 = ReadingProgress(
            mangaSlug: "manga1",
            chapterNumber: 1,
            pageNumber: 10,
            timestamp: oldDate
        )

        let progress2 = ReadingProgress(
            mangaSlug: "manga1",
            chapterNumber: 2,
            pageNumber: 5,
            timestamp: newDate
        )

        // When
        storageService.saveReadingProgress(progress1)
        storageService.saveReadingProgress(progress2)

        let lastRead = storageService.getLastReadChapter(mangaSlug: "manga1")

        // Then
        XCTAssertNotNil(lastRead)
        XCTAssertEqual(lastRead?.chapterNumber, 2, "Should return the most recent chapter")
        XCTAssertEqual(lastRead?.timestamp.timeIntervalSince1970, 1609459300, accuracy: 0.001)
    }

    func testGetLastReadChapterWhenNoProgress() {
        // When
        let lastRead = storageService.getLastReadChapter(mangaSlug: "non-existent")

        // Then
        XCTAssertNil(lastRead)
    }

    func testGetAllReadingProgressWhenEmpty() {
        // When
        let allProgress = storageService.getAllReadingProgress()

        // Then
        XCTAssertTrue(allProgress.isEmpty)
    }

    // MARK: - Downloaded Chapters Tests

    func testSaveDownloadedChapter() {
        // Given
        let pages = [
            MangaPage(url: "page1.jpg", index: 0),
            MangaPage(url: "page2.jpg", index: 1)
        ]

        let downloadedChapter = DownloadedChapter(
            mangaSlug: "solo-leveling",
            mangaTitle: "Solo Leveling",
            chapterNumber: 50,
            pages: pages,
            downloadedAt: Date()
        )

        // When
        storageService.saveDownloadedChapter(downloadedChapter)

        // Then
        let retrieved = storageService.getDownloadedChapter(
            mangaSlug: "solo-leveling",
            chapterNumber: 50
        )

        XCTAssertNotNil(retrieved)
        XCTAssertEqual(retrieved?.mangaSlug, "solo-leveling")
        XCTAssertEqual(retrieved?.chapterNumber, 50)
        XCTAssertEqual(retrieved?.pages.count, 2)
    }

    func testIsChapterDownloaded() {
        // Given
        let chapter = DownloadedChapter(
            mangaSlug: "test-manga",
            mangaTitle: "Test",
            chapterNumber: 10,
            pages: [],
            downloadedAt: Date()
        )

        // When
        storageService.saveDownloadedChapter(chapter)

        // Then
        XCTAssertTrue(storageService.isChapterDownloaded(
            mangaSlug: "test-manga",
            chapterNumber: 10
        ))

        XCTAssertFalse(storageService.isChapterDownloaded(
            mangaSlug: "test-manga",
            chapterNumber: 11
        ))
    }

    func testGetDownloadedChapters() {
        // Given
        let chapter1 = DownloadedChapter(
            mangaSlug: "manga1",
            mangaTitle: "Manga 1",
            chapterNumber: 1,
            pages: [],
            downloadedAt: Date()
        )

        let chapter2 = DownloadedChapter(
            mangaSlug: "manga2",
            mangaTitle: "Manga 2",
            chapterNumber: 5,
            pages: [],
            downloadedAt: Date()
        )

        // When
        storageService.saveDownloadedChapter(chapter1)
        storageService.saveDownloadedChapter(chapter2)

        // Then
        let downloaded = storageService.getDownloadedChapters()
        XCTAssertEqual(downloaded.count, 2)
    }

    func testDeleteDownloadedChapter() {
        // Given
        let chapter = DownloadedChapter(
            mangaSlug: "test-manga",
            mangaTitle: "Test",
            chapterNumber: 10,
            pages: [],
            downloadedAt: Date()
        )

        storageService.saveDownloadedChapter(chapter)
        XCTAssertTrue(storageService.isChapterDownloaded(mangaSlug: "test-manga", chapterNumber: 10))

        // When
        storageService.deleteDownloadedChapter(mangaSlug: "test-manga", chapterNumber: 10)

        // Then
        XCTAssertFalse(storageService.isChapterDownloaded(mangaSlug: "test-manga", chapterNumber: 10))
    }

    func testDeleteNonExistentDownloadedChapter() {
        // When - No debería lanzar error
        storageService.deleteDownloadedChapter(mangaSlug: "non-existent", chapterNumber: 999)

        // Then - Simplemente no hace nada
        let downloaded = storageService.getDownloadedChapters()
        XCTAssertTrue(downloaded.isEmpty)
    }

    // MARK: - Image Caching Tests

    func testSaveAndLoadImage() async throws {
        // Given
        let testImage = createTestImage(size: CGSize(width: 800, height: 1200))

        // When
        let savedURL = try await storageService.saveImage(
            testImage,
            mangaSlug: "test-manga",
            chapterNumber: 1,
            pageIndex: 0
        )

        // Then
        XCTAssertTrue(FileManager.default.fileExists(atPath: savedURL.path))
        XCTAssertEqual(savedURL.lastPathComponent, "page_0.jpg")

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

        XCTAssertNotNil(loadedImage)
    }

    func testLoadNonExistentImage() {
        // When
        let image = storageService.loadImage(
            mangaSlug: "non-existent",
            chapterNumber: 999,
            pageIndex: 999
        )

        // Then
        XCTAssertNil(image)
    }

    func testGetImageURL() async throws {
        // Given
        let testImage = createTestImage(size: CGSize(width: 800, height: 1200))

        // When
        let savedURL = try await storageService.saveImage(
            testImage,
            mangaSlug: "test-manga",
            chapterNumber: 1,
            pageIndex: 0
        )

        let retrievedURL = storageService.getImageURL(
            mangaSlug: "test-manga",
            chapterNumber: 1,
            pageIndex: 0
        )

        // Then
        XCTAssertNotNil(retrievedURL)
        XCTAssertEqual(retrievedURL, savedURL)
    }

    func testGetImageURLForNonExistentImage() {
        // When
        let url = storageService.getImageURL(
            mangaSlug: "test",
            chapterNumber: 1,
            pageIndex: 0
        )

        // Then
        XCTAssertNil(url)
    }

    // MARK: - Storage Management Tests

    func testGetStorageSize() async throws {
        // Given
        let image1 = createTestImage(size: CGSize(width: 800, height: 1200))
        let image2 = createTestImage(size: CGSize(width: 800, height: 1200))

        // When
        _ = try await storageService.saveImage(image1, mangaSlug: "test", chapterNumber: 1, pageIndex: 0)
        _ = try await storageService.saveImage(image2, mangaSlug: "test", chapterNumber: 1, pageIndex: 1)

        let size = storageService.getStorageSize()

        // Then
        XCTAssertGreaterThan(size, 0, "Storage size should be greater than 0")
    }

    func testClearAllDownloads() async throws {
        // Given
        let image = createTestImage(size: CGSize(width: 800, height: 1200))
        _ = try await storageService.saveImage(image, mangaSlug: "test", chapterNumber: 1, pageIndex: 0)

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

        // When
        storageService.clearAllDownloads()

        // Then
        let downloaded = storageService.getDownloadedChapters()
        XCTAssertTrue(downloaded.isEmpty)

        let size = storageService.getStorageSize()
        XCTAssertEqual(size, 0, "Storage size should be 0 after clearing")
    }

    func testFormatFileSize() {
        // Given
        let bytes: Int64 = 1536000 // ~1.5 MB

        // When
        let formatted = storageService.formatFileSize(bytes)

        // Then
        XCTAssertTrue(formatted.contains("MB") || formatted.contains("KB"))
    }

    func testFormatFileSizeWithVariousSizes() {
        let tests: [(Int64, String)] = [
            (500, "B"),         // Bytes
            (1024, "KB"),       // 1 KB
            (1048576, "MB"),    // 1 MB
            (1073741824, "GB")  // 1 GB
        ]

        for (size, expectedUnit) in {
            let formatted = storageService.formatFileSize(size)
            XCTAssertTrue(
                formatted.contains(expectedUnit),
                "Expected \(expectedUnit) in formatted string: \(formatted)"
            )
        }
    }

    // MARK: - Directory Management Tests

    func testGetChapterDirectory() {
        // When
        let directory = storageService.getChapterDirectory(mangaSlug: "test-manga", chapterNumber: 10)

        // Then
        XCTAssertTrue(directory.path.contains("test-manga"))
        XCTAssertTrue(directory.path.contains("Chapter10"))
    }

    func testChapterDirectoryCreation() async throws {
        // Given
        let image = createTestImage(size: CGSize(width: 100, height: 100))

        // When
        let savedURL = try await storageService.saveImage(
            image,
            mangaSlug: "new-manga",
            chapterNumber: 1,
            pageIndex: 0
        )

        // Then
        let directory = savedURL.deletingLastPathComponent()
        XCTAssertTrue(FileManager.default.fileExists(atPath: directory.path))
    }

    // MARK: - Edge Cases Tests

    func testSaveFavoriteWithEmptySlug() {
        // When
        storageService.saveFavorite(mangaSlug: "")

        // Then
        let favorites = storageService.getFavorites()
        XCTAssertTrue(favorites.contains(""), "Empty slug should be saved")
    }

    func testSaveFavoriteWithSpecialCharacters() {
        // Given
        let specialSlug = "manga-with-special-chars-áéíóú-ñ-@#$"

        // When
        storageService.saveFavorite(mangaSlug: specialSlug)

        // Then
        XCTAssertTrue(storageService.isFavorite(mangaSlug: specialSlug))
    }

    func testReadingProgressWithZeroPage() {
        // Given
        let progress = ReadingProgress(
            mangaSlug: "test",
            chapterNumber: 1,
            pageNumber: 0,
            timestamp: Date()
        )

        // When
        storageService.saveReadingProgress(progress)

        // Then
        let retrieved = storageService.getReadingProgress(mangaSlug: "test", chapterNumber: 1)
        XCTAssertEqual(retrieved?.pageNumber, 0)
    }

    func testDownloadedChapterWithZeroChapterNumber() {
        // Given
        let chapter = DownloadedChapter(
            mangaSlug: "test",
            mangaTitle: "Test",
            chapterNumber: 0,
            pages: [],
            downloadedAt: Date()
        )

        // When
        storageService.saveDownloadedChapter(chapter)

        // Then
        XCTAssertTrue(storageService.isChapterDownloaded(mangaSlug: "test", chapterNumber: 0))
    }

    func testConcurrentImageSave() async throws {
        // Given
        let images = (0..<10).map { _ in createTestImage(size: CGSize(width: 800, height: 1200)) }

        // When - Guardar imágenes concurrentemente
        await withTaskGroup(of: URL.self) { group in
            for (index, image) in images.enumerated() {
                group.addTask {
                    try! await self.storageService.saveImage(
                        image,
                        mangaSlug: "concurrent-test",
                        chapterNumber: 1,
                        pageIndex: index
                    )
                }
            }
        }

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

    // MARK: - Helper Methods

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

    // MARK: - Performance Tests

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

    func testSaveManyReadingProgressPerformance() {
        let progresses = (0..<100).map { i in
            ReadingProgress(
                mangaSlug: "manga-\(i % 10)",
                chapterNumber: i,
                pageNumber: i * 2,
                timestamp: Date()
            )
        }

        measure {
            for progress in progresses {
                storageService.saveReadingProgress(progress)
            }
        }
    }
}
