import XCTest
@testable import MangaReader

/// Tests para los modelos de datos: Manga, Chapter, MangaPage, ReadingProgress, DownloadedChapter
final class ModelTests: XCTestCase {

    // MARK: - Setup & Teardown

    override func setUp() {
        super.setUp()
        // Configuración inicial antes de cada test
    }

    override func tearDown() {
        // Limpieza después de cada test
        super.tearDown()
    }

    // MARK: - Manga Model Tests

    func testMangaInitialization() {
        let manga = Manga(
            slug: "tower-of-god",
            title: "Tower of God",
            description: "A young boy enters a mysterious tower",
            genres: ["Action", "Fantasy", "Adventure"],
            status: "PUBLICANDOSE",
            url: "https://manhwaweb.com/manga/tower-of-god",
            coverImage: "https://example.com/cover.jpg"
        )

        XCTAssertEqual(manga.slug, "tower-of-god")
        XCTAssertEqual(manga.title, "Tower of God")
        XCTAssertEqual(manga.genres.count, 3)
        XCTAssertEqual(manga.id, "tower-of-god")
    }

    func testMangaCodableSerialization() {
        let manga = Manga(
            slug: "solo-leveling",
            title: "Solo Leveling",
            description: "The weakest hunter becomes the strongest",
            genres: ["Action", "Adventure"],
            status: "FINALIZADO",
            url: "https://manhwaweb.com/manga/solo-leveling",
            coverImage: nil
        )

        // Test encoding
        do {
            let encoder = JSONEncoder()
            encoder.outputFormatting = .prettyPrinted
            let jsonData = try encoder.encode(manga)
            XCTAssertFalse(jsonData.isEmpty)

            // Test decoding
            let decoder = JSONDecoder()
            let decodedManga = try decoder.decode(Manga.self, from: jsonData)

            XCTAssertEqual(manga.slug, decodedManga.slug)
            XCTAssertEqual(manga.title, decodedManga.title)
            XCTAssertEqual(manga.description, decodedManga.description)
            XCTAssertEqual(manga.genres, decodedManga.genres)
            XCTAssertEqual(manga.status, decodedManga.status)
            XCTAssertEqual(manga.url, decodedManga.url)
            XCTAssertEqual(manga.coverImage, decodedManga.coverImage)
        } catch {
            XCTFail("Coding/decoding failed: \(error)")
        }
    }

    func testMangaDisplayStatus() {
        let publishingManga = Manga(
            slug: "test1",
            title: "Test",
            description: "Desc",
            genres: [],
            status: "PUBLICANDOSE",
            url: ""
        )
        XCTAssertEqual(publishingManga.displayStatus, "En publicación")

        let finishedManga = Manga(
            slug: "test2",
            title: "Test",
            description: "Desc",
            genres: [],
            status: "FINALIZADO",
            url: ""
        )
        XCTAssertEqual(finishedManga.displayStatus, "Finalizado")

        let pausedManga = Manga(
            slug: "test3",
            title: "Test",
            description: "Desc",
            genres: [],
            status: "EN_PAUSA",
            url: ""
        )
        XCTAssertEqual(pausedManga.displayStatus, "En pausa")

        let waitingManga = Manga(
            slug: "test4",
            title: "Test",
            description: "Desc",
            genres: [],
            status: "EN_ESPERA",
            url: ""
        )
        XCTAssertEqual(waitingManga.displayStatus, "En pausa")

        let unknownManga = Manga(
            slug: "test5",
            title: "Test",
            description: "Desc",
            genres: [],
            status: "UNKNOWN_STATUS",
            url: ""
        )
        XCTAssertEqual(unknownManga.displayStatus, "UNKNOWN_STATUS")
    }

    func testMangaHashable() {
        let manga1 = Manga(
            slug: "test",
            title: "Test Manga",
            description: "Description",
            genres: ["Action"],
            status: "PUBLICANDOSE",
            url: "https://example.com",
            coverImage: nil
        )

        let manga2 = Manga(
            slug: "test",
            title: "Different Title",
            description: "Different Description",
            genres: ["Drama"],
            status: "FINALIZADO",
            url: "https://different.com",
            coverImage: "cover.jpg"
        )

        XCTAssertEqual(manga1, manga2)
        XCTAssertEqual(manga1.hashValue, manga2.hashValue)

        let set: Set<Manga> = [manga1, manga2]
        XCTAssertEqual(set.count, 1, "Mangas with same slug should be considered equal")
    }

    // MARK: - Chapter Model Tests

    func testChapterInitialization() {
        let chapter = Chapter(
            number: 150,
            title: "The Final Battle",
            url: "https://manhwaweb.com/leer/solo-leveling/150",
            slug: "solo-leveling/150"
        )

        XCTAssertEqual(chapter.id, 150)
        XCTAssertEqual(chapter.number, 150)
        XCTAssertEqual(chapter.title, "The Final Battle")
        XCTAssertEqual(chapter.isRead, false)
        XCTAssertEqual(chapter.isDownloaded, false)
        XCTAssertEqual(chapter.lastReadPage, 0)
    }

    func testChapterDisplayNumber() {
        let chapter = Chapter(number: 42, title: "Test", url: "", slug: "")
        XCTAssertEqual(chapter.displayNumber, "Capítulo 42")
    }

    func testChapterProgress() {
        var chapter = Chapter(number: 1, title: "Test", url: "", slug: "")
        XCTAssertEqual(chapter.progress, 0.0)

        chapter.lastReadPage = 5
        XCTAssertEqual(chapter.progress, 5.0)

        chapter.lastReadPage = 100
        XCTAssertEqual(chapter.progress, 100.0)
    }

    func testChapterCodableSerialization() {
        let chapter = Chapter(
            number: 50,
            title: "Chapter 50",
            url: "https://manhwaweb.com/leer/test/50",
            slug: "test/50",
            isRead: true,
            isDownloaded: true,
            lastReadPage: 25
        )

        // Test encoding
        do {
            let encoder = JSONEncoder()
            let jsonData = try encoder.encode(chapter)

            // Test decoding
            let decoder = JSONDecoder()
            let decodedChapter = try decoder.decode(Chapter.self, from: jsonData)

            XCTAssertEqual(chapter.number, decodedChapter.number)
            XCTAssertEqual(chapter.title, decodedChapter.title)
            XCTAssertEqual(chapter.url, decodedChapter.url)
            XCTAssertEqual(chapter.slug, decodedChapter.slug)
            XCTAssertEqual(chapter.isRead, decodedChapter.isRead)
            XCTAssertEqual(chapter.isDownloaded, decodedChapter.isDownloaded)
            XCTAssertEqual(chapter.lastReadPage, decodedChapter.lastReadPage)
        } catch {
            XCTFail("Chapter coding/decoding failed: \(error)")
        }
    }

    func testChapterHashable() {
        let chapter1 = Chapter(
            number: 10,
            title: "Chapter 10",
            url: "url1",
            slug: "slug1",
            isRead: true,
            isDownloaded: false,
            lastReadPage: 5
        )

        let chapter2 = Chapter(
            number: 10,
            title: "Different Title",
            url: "url2",
            slug: "slug2",
            isRead: false,
            isDownloaded: true,
            lastReadPage: 10
        )

        XCTAssertEqual(chapter1, chapter2)
        XCTAssertEqual(chapter1.hashValue, chapter2.hashValue)
    }

    // MARK: - MangaPage Model Tests

    func testMangaPageInitialization() {
        let page = MangaPage(url: "https://example.com/page1.jpg", index: 0)

        XCTAssertEqual(page.id, "https://example.com/page1.jpg")
        XCTAssertEqual(page.url, "https://example.com/page1.jpg")
        XCTAssertEqual(page.index, 0)
        XCTAssertEqual(page.isCached, false)
    }

    func testMangaPageThumbnailURL() {
        let page = MangaPage(url: "https://example.com/high-res.jpg", index: 5)
        XCTAssertEqual(page.thumbnailURL, "https://example.com/high-res.jpg")
    }

    func testMangaPageCodableSerialization() {
        let page = MangaPage(
            url: "https://example.com/page.jpg",
            index: 10,
            isCached: true
        )

        do {
            let encoder = JSONEncoder()
            let jsonData = try encoder.encode(page)

            let decoder = JSONDecoder()
            let decodedPage = try decoder.decode(MangaPage.self, from: jsonData)

            XCTAssertEqual(page.url, decodedPage.url)
            XCTAssertEqual(page.index, decodedPage.index)
            XCTAssertEqual(page.isCached, decodedPage.isCached)
        } catch {
            XCTFail("MangaPage coding/decoding failed: \(error)")
        }
    }

    func testMangaPageHashable() {
        let page1 = MangaPage(url: "https://example.com/page.jpg", index: 0)
        let page2 = MangaPage(url: "https://example.com/page.jpg", index: 5, isCached: true)

        XCTAssertEqual(page1, page2)
        XCTAssertEqual(page1.hashValue, page2.hashValue)
    }

    // MARK: - ReadingProgress Model Tests

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

        XCTAssertEqual(progress.mangaSlug, "solo-leveling")
        XCTAssertEqual(progress.chapterNumber, 50)
        XCTAssertEqual(progress.pageNumber, 10)
    }

    func testReadingProgressIsCompleted() {
        let incompleteProgress = ReadingProgress(
            mangaSlug: "test",
            chapterNumber: 1,
            pageNumber: 3,
            timestamp: Date()
        )
        XCTAssertFalse(incompleteProgress.isCompleted)

        let completedProgress = ReadingProgress(
            mangaSlug: "test",
            chapterNumber: 1,
            pageNumber: 6,
            timestamp: Date()
        )
        XCTAssertTrue(completedProgress.isCompleted)

        let boundaryProgress = ReadingProgress(
            mangaSlug: "test",
            chapterNumber: 1,
            pageNumber: 5,
            timestamp: Date()
        )
        XCTAssertFalse(boundaryProgress.isCompleted, "Exactly 5 pages should not be considered completed")
    }

    func testReadingProgressCodableSerialization() {
        let timestamp = Date(timeIntervalSince1970: 1609459200) // 2021-01-01
        let progress = ReadingProgress(
            mangaSlug: "tower-of-god",
            chapterNumber: 500,
            pageNumber: 42,
            timestamp: timestamp
        )

        do {
            let encoder = JSONEncoder()
            let jsonData = try encoder.encode(progress)

            let decoder = JSONDecoder()
            let decodedProgress = try decoder.decode(ReadingProgress.self, from: jsonData)

            XCTAssertEqual(progress.mangaSlug, decodedProgress.mangaSlug)
            XCTAssertEqual(progress.chapterNumber, decodedProgress.chapterNumber)
            XCTAssertEqual(progress.pageNumber, decodedProgress.pageNumber)

            // Compare timestamp with tolerance for encoding/decoding precision
            let timeDifference = abs(progress.timestamp.timeIntervalSince(decodedProgress.timestamp))
            XCTAssertLessThan(timeDifference, 0.001, "Timestamps should match within milliseconds")
        } catch {
            XCTFail("ReadingProgress coding/decoding failed: \(error)")
        }
    }

    // MARK: - DownloadedChapter Model Tests

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

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

        XCTAssertEqual(downloadedChapter.id, "solo-leveling-chapter100")
        XCTAssertEqual(downloadedChapter.mangaSlug, "solo-leveling")
        XCTAssertEqual(downloadedChapter.pages.count, 2)
    }

    func testDownloadedChapterDisplayTitle() {
        let chapter = DownloadedChapter(
            mangaSlug: "test",
            mangaTitle: "Test Manga",
            chapterNumber: 25,
            pages: [],
            downloadedAt: Date()
        )

        XCTAssertEqual(chapter.displayTitle, "Test Manga - Capítulo 25")
    }

    func testDownloadedChapterCodableSerialization() {
        let pages = [
            MangaPage(url: "page1.jpg", index: 0, isCached: true),
            MangaPage(url: "page2.jpg", index: 1, isCached: true)
        ]

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

        do {
            let encoder = JSONEncoder()
            let jsonData = try encoder.encode(downloadedChapter)

            let decoder = JSONDecoder()
            let decodedChapter = try decoder.decode(DownloadedChapter.self, from: jsonData)

            XCTAssertEqual(downloadedChapter.mangaSlug, decodedChapter.mangaSlug)
            XCTAssertEqual(downloadedChapter.mangaTitle, decodedChapter.mangaTitle)
            XCTAssertEqual(downloadedChapter.chapterNumber, decodedChapter.chapterNumber)
            XCTAssertEqual(downloadedChapter.pages.count, decodedChapter.pages.count)
            XCTAssertEqual(downloadedChapter.totalSize, decodedChapter.totalSize)
        } catch {
            XCTFail("DownloadedChapter coding/decoding failed: \(error)")
        }
    }

    // MARK: - Edge Cases Tests

    func testMangaWithEmptyGenres() {
        let manga = Manga(
            slug: "test",
            title: "Test",
            description: "Description",
            genres: [],
            status: "PUBLICANDOSE",
            url: "url"
        )

        XCTAssertTrue(manga.genres.isEmpty)
        XCTAssertEqual(manga.genres.count, 0)
    }

    func testMangaWithNilCoverImage() {
        let manga1 = Manga(
            slug: "test",
            title: "Test",
            description: "Desc",
            genres: [],
            status: "PUBLICANDOSE",
            url: "url",
            coverImage: nil
        )
        XCTAssertNil(manga1.coverImage)

        let manga2 = Manga(
            slug: "test",
            title: "Test",
            description: "Desc",
            genres: [],
            status: "PUBLICANDOSE",
            url: "url",
            coverImage: ""
        )
        XCTAssertNil(manga2.coverImage, "Empty string should be treated as nil")
    }

    func testChapterWithZeroNumber() {
        let chapter = Chapter(number: 0, title: "Prologue", url: "url", slug: "slug")
        XCTAssertEqual(chapter.id, 0)
        XCTAssertEqual(chapter.displayNumber, "Capítulo 0")
    }

    func testChapterWithLargePageNumber() {
        var chapter = Chapter(number: 1, title: "Test", url: "url", slug: "slug")
        chapter.lastReadPage = 10000

        XCTAssertEqual(chapter.progress, 10000.0)
    }

    func testMangaPageWithNegativeIndex() {
        let page = MangaPage(url: "test.jpg", index: -1)
        XCTAssertEqual(page.index, -1)
        // Edge case: negative indices should still work
    }

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

        XCTAssertFalse(progress.isCompleted)
    }

    func testDownloadedChapterWithEmptyPages() {
        let chapter = DownloadedChapter(
            mangaSlug: "test",
            mangaTitle: "Test",
            chapterNumber: 1,
            pages: [],
            downloadedAt: Date()
        )

        XCTAssertTrue(chapter.pages.isEmpty)
        XCTAssertEqual(chapter.totalSize, 0)
    }

    // MARK: - Performance Tests

    func testMangaEncodingPerformance() {
        let manga = Manga(
            slug: "test-manga-with-very-long-slug",
            title: "Test Manga Title That Is Quite Long",
            description: "This is a very long description that contains a lot of text about the manga plot and characters",
            genres: ["Action", "Adventure", "Comedy", "Drama", "Fantasy", "Horror", "Mystery", "Romance", "Sci-Fi", "Thriller"],
            status: "PUBLICANDOSE",
            url: "https://manhwaweb.com/manga/test-manga",
            coverImage: "https://example.com/cover.jpg"
        )

        measure {
            for _ in 0..<1000 {
                _ = try? JSONEncoder().encode(manga)
            }
        }
    }

    func testChapterArrayEqualityPerformance() {
        var chapters: [Chapter] = []
        for i in 0..<1000 {
            chapters.append(Chapter(number: i, title: "Chapter \(i)", url: "url\(i)", slug: "slug\(i)"))
        }

        let set = Set(chapters)

        measure {
            _ = set.contains(chapters[500])
        }
    }
}
