import json
from datetime import date

import pytest

import hircine.enums as enums
from hircine.plugins.scrapers.handlers.dynastyscans import DynastyScansHandler
from hircine.plugins.scrapers.handlers.e621 import E621Handler
from hircine.plugins.scrapers.handlers.exhentai import (
    ExHentaiHandler,
)
from hircine.plugins.scrapers.handlers.exhentai import (
    sanitize as exhentai_sanitize,
)
from hircine.plugins.scrapers.handlers.mangadex import MangadexHandler
from hircine.scraper import Scraper
from hircine.scraper.types import (
    URL,
    Artist,
    Category,
    Censorship,
    Character,
    Circle,
    Date,
    Direction,
    Language,
    OriginalTitle,
    Rating,
    Tag,
    Title,
    World,
)


class Scraper(Scraper):
    def __init__(self, handler, json):
        self.handler = handler
        self.json = json
        super().__init__(None)

    def scrape(self):
        yield from self.handler.scrape(json.loads(self.json))


def test_dynastyscans():
    scraper = Scraper(
        DynastyScansHandler(),
        """
        {
            "manga": "Hoshiiro GirlDrop Comic Anthology",
            "chapter": 1,
            "chapter_minor": "",
            "title": "Hop, Step, Drop!",
            "author": "Fujisawa Kamiya",
            "group": "Cyan Steam (Stan Miller)",
            "date": "2018-02-05 00:00:00",
            "lang": "en",
            "language": "English",
            "count": 15,
            "category": "dynastyscans",
            "subcategory": "manga"
        }
        """,
    )

    assert set(scraper.collect()) == set(
        [
            Artist(name="Fujisawa Kamiya"),
            Circle(name="Cyan Steam (Stan Miller)"),
            Date(value=date(2018, 2, 5)),
            Language(value=enums.Language.EN),
            Title(value="Hoshiiro GirlDrop Comic Anthology Ch. 1: Hop, Step, Drop!"),
        ]
    )


def test_mangadex():
    scraper = Scraper(
        MangadexHandler(),
        """
        {
            "manga": "Shimeji Simulation",
            "manga_id": "28b5d037-175d-4119-96f8-e860e408ebe9",
            "title": "Danchi",
            "volume": 1,
            "chapter": 8,
            "chapter_minor": "",
            "chapter_id": "2a115ccb-de52-4b84-9166-cebd152d9396",
            "date": "2019-09-22 04:19:15",
            "lang": "en",
            "language": "English",
            "count": 12,
            "artist": [
                "Tsukumizu"
            ],
            "author": [
                "Tsukumizu"
            ],
            "group": [
                "Orchesc/a/ns"
            ],
            "status": "completed",
            "tags": [
                "Sci-Fi",
                "Comedy",
                "Girls' Love",
                "4-Koma",
                "Philosophical",
                "School Life",
                "Slice of Life"
            ],
            "category": "mangadex",
            "subcategory": "chapter"
        }
        """,
    )

    assert set(scraper.collect()) == set(
        [
            Artist(name="Tsukumizu"),
            Circle(name="Orchesc/a/ns"),
            Date(value=date(2019, 9, 22)),
            Language(value=enums.Language.EN),
            Tag(namespace="none", tag="4-Koma"),
            Tag(namespace="none", tag="Comedy"),
            Tag(namespace="none", tag="Girls' Love"),
            Tag(namespace="none", tag="Philosophical"),
            Tag(namespace="none", tag="School Life"),
            Tag(namespace="none", tag="Sci-Fi"),
            Tag(namespace="none", tag="Slice of Life"),
            Title(value="Shimeji Simulation Vol. 1, Ch. 8: Danchi"),
            URL("https://mangadex.org/chapter/2a115ccb-de52-4b84-9166-cebd152d9396"),
        ]
    )


@pytest.mark.parametrize(
    "data, title",
    [
        ({"volume": 1, "chapter": 8}, "Manga Vol. 1, Ch. 8: Title"),
        ({"volume": 0, "chapter": 1}, "Manga Ch. 1: Title"),
        ({"volume": 0, "chapter": 0}, "Manga: Title"),
    ],
    ids=[
        "volume and chapter",
        "chapter only",
        "none",
    ],
)
def test_mangadex_handles_volume_and_chapter(data, title):
    common = {"manga": "Manga", "title": "Title"}
    scraper = Scraper(MangadexHandler(), json.dumps(common | data))

    assert list(scraper.collect()) == [Title(value=title)]


def test_e621_pool():
    scraper = Scraper(
        E621Handler(),
        """
        {
            "id": 2968472,
            "created_at": "2021-10-10T04:13:53.286-04:00",
            "updated_at": "2024-11-02T08:58:06.724-04:00",
            "file": {
                "width": 800,
                "height": 800,
                "ext": "jpg",
                "size": 530984,
                "md5": "1ec7e397bb22c1454ab1986fd3f3edc5",
                "url": "https://static1.e621.net/data/1e/c7/1ec7e397bb22c1454ab1986fd3f3edc5.jpg"
            },
            "preview": {
                "width": 150,
                "height": 150,
                "url": "https://static1.e621.net/data/preview/1e/c7/1ec7e397bb22c1454ab1986fd3f3edc5.jpg"
            },
            "sample": {
                "has": false,
                "height": 800,
                "width": 800,
                "url": "https://static1.e621.net/data/1e/c7/1ec7e397bb22c1454ab1986fd3f3edc5.jpg",
                "alternates": {}
            },
            "score": {
                "up": 202,
                "down": -1,
                "total": 201
            },
            "tags": {
                "general": [
                    "beak"
                ],
                "artist": [
                    "falseknees"
                ],
                "copyright": [],
                "character": [],
                "species": [
                    "bird"
                ],
                "invalid": [],
                "meta": [
                    "comic",
                    "english_text"
                ],
                "lore": [
                    "parent_(lore)",
                    "parent_and_child_(lore)"
                ]
            },
            "locked_tags": [],
            "change_seq": 60808337,
            "flags": {
                "pending": false,
                "flagged": false,
                "note_locked": false,
                "status_locked": false,
                "rating_locked": false,
                "deleted": false
            },
            "rating": "s",
            "fav_count": 194,
            "sources": [
                "https://twitter.com/FalseKnees/status/1324869853627478022"
            ],
            "pools": [
                25779
            ],
            "relationships": {
                "parent_id": null,
                "has_children": false,
                "has_active_children": false,
                "children": []
            },
            "approver_id": 171673,
            "uploader_id": 178921,
            "description": "",
            "comment_count": 1,
            "is_favorited": false,
            "has_notes": false,
            "duration": null,
            "num": 1,
            "filename": "1ec7e397bb22c1454ab1986fd3f3edc5",
            "extension": "jpg",
            "date": "2021-10-10 08:13:53",
            "pool": {
                "id": 25779,
                "name": "Kids say the darnedest shit - falseknees",
                "created_at": "2021-10-10T04:17:07.006-04:00",
                "updated_at": "2021-10-10T04:17:07.006-04:00",
                "creator_id": 178921,
                "description": "The terror of every parent.",
                "is_active": true,
                "category": "series",
                "creator_name": "OneMoreAnonymous",
                "post_count": 4
            },
            "category": "e621",
            "subcategory": "pool"
        }
        """,
    )

    assert set(scraper.collect()) == set(
        [
            Artist(name="falseknees"),
            Category(value=enums.Category.COMIC),
            Censorship(value=enums.Censorship.NONE),
            Date(value=date(2021, 10, 10)),
            Language(value=enums.Language.EN),
            Rating(value=enums.Rating.SAFE),
            Tag(namespace="none", tag="beak"),
            Tag(namespace="none", tag="bird"),
            Title(value="Kids say the darnedest shit - falseknees"),
            URL("https://e621.net/pools/25779"),
        ]
    )


@pytest.mark.parametrize(
    "data, censorship",
    [
        ({"tags": {"meta": ["censor_bar"]}}, enums.Censorship.BAR),
        ({"tags": {"meta": ["mosaic_censorship"]}}, enums.Censorship.MOSAIC),
        ({"tags": {"meta": ["uncensored"]}}, enums.Censorship.NONE),
        ({"tags": {"meta": []}}, enums.Censorship.NONE),
    ],
    ids=[
        "bars",
        "mosaic",
        "uncensored",
        "uncensored (implied)",
    ],
)
def test_e621_handles_censorship(data, censorship):
    common = {"subcategory": "pool"}
    scraper = Scraper(E621Handler(), json.dumps(common | data))

    assert set(scraper.collect()) == set([Censorship(value=censorship)])


def test_exhentai_explicit():
    scraper = Scraper(
        ExHentaiHandler(),
        """
        {
            "gid": 2771624,
            "token": "43108ee23b",
            "thumb": "https://s.exhentai.org/t/12/80/1280a064a2ab3d70b9feb56bd0c55dbfc3ab6a39-309830-950-1351-jpg_250.jpg",
            "title": "[NAGABE] Smell ch.01 - ch.06",
            "title_jpn": "SMELL",
            "eh_category": "Doujinshi",
            "uploader": "randaldog",
            "date": "2023-12-19 23:50:00",
            "parent": "https://exhentai.org/g/2736803/b191bfed72/",
            "expunged": false,
            "language": "English",
            "filesize": 74469868,
            "filecount": "170",
            "favorites": "751",
            "rating": "4.83",
            "torrentcount": "0",
            "lang": "en",
            "tags": [
                "language:english",
                "language:translated",
                "parody:original",
                "artist:nagabe",
                "male:dog boy",
                "male:furry",
                "male:males only",
                "male:smell",
                "male:yaoi",
                "other:story arc"
            ],
            "category": "exhentai",
            "subcategory": "gallery"
        }
        """,
    )

    assert set(scraper.collect()) == set(
        [
            Artist(name="nagabe"),
            Category(value=enums.Category.DOUJINSHI),
            Censorship(value=enums.Censorship.BAR),
            Date(value=date(2023, 12, 19)),
            Direction(value=enums.Direction.RIGHT_TO_LEFT),
            Language(value=enums.Language.EN),
            OriginalTitle(value="SMELL"),
            Rating(value=enums.Rating.EXPLICIT),
            Tag(namespace="male", tag="dog boy"),
            Tag(namespace="male", tag="furry"),
            Tag(namespace="male", tag="males only"),
            Tag(namespace="male", tag="smell"),
            Tag(namespace="male", tag="yaoi"),
            Tag(namespace="other", tag="story arc"),
            Title(value="Smell ch.01 - ch.06"),
            URL("https://exhentai.org/g/2771624/43108ee23b"),
            World(name="original"),
        ]
    )


def test_exhentai_non_h():
    scraper = Scraper(
        ExHentaiHandler(),
        """
        {
            "gid": 1025913,
            "token": "fdaabef1a2",
            "thumb": "https://s.exhentai.org/t/51/17/5117cde63cc14436c5ad7f2dd06abb52c86aff65-23642001-2866-4047-png_250.jpg",
            "title": "(C91) [Animachine (Shimahara)] Iya na Kao Sarenagara Opantsu Misete Moraitai Manga | A manga about girl showing you her panties while making a disgusted face [English] [葛の寺]",
            "title_jpn": "(C91) [アニマルマシーン (40原)] 嫌な顔されながらおパンツ見せてもらいたい漫画 [英訳]",
            "eh_category": "Non-H",
            "uploader": "葛の寺",
            "date": "2017-02-04 04:25:00",
            "parent": "https://exhentai.org/g/1025875/cfe6adccb8/",
            "expunged": false,
            "language": "English",
            "filesize": 0,
            "filecount": "23",
            "favorites": "1088",
            "rating": "4.74",
            "torrentcount": "1",
            "lang": "en",
            "tags": [
                "language:english",
                "language:translated",
                "parody:iya na kao sare nagara opantsu misete moraitai",
                "group:animachine",
                "artist:shimahara",
                "female:femdom",
                "female:schoolgirl uniform",
                "other:full color"
            ],
            "category": "exhentai",
            "subcategory": "gallery"
        }
        """,  # noqa: E501
    )

    assert set(scraper.collect()) == set(
        [
            Artist(name="shimahara"),
            Censorship(value=enums.Censorship.NONE),
            Circle(name="animachine"),
            Date(value=date(2017, 2, 4)),
            Language(value=enums.Language.EN),
            OriginalTitle(value="嫌な顔されながらおパンツ見せてもらいたい漫画"),
            Rating(value=enums.Rating.QUESTIONABLE),
            Tag(namespace="female", tag="femdom"),
            Tag(namespace="female", tag="schoolgirl uniform"),
            Tag(namespace="other", tag="full color"),
            Title(
                value="A manga about girl showing you her panties while making a disgusted face"  # noqa: E501
            ),
            URL("https://exhentai.org/g/1025913/fdaabef1a2"),
            World(name="iya na kao sare nagara opantsu misete moraitai"),
        ]
    )


@pytest.mark.parametrize(
    "text, sanitized",
    [
        ("(foo) Title", "Title"),
        ("[foo] {bar} =baz= Title", "Title"),
        ("Foreign Title | Localized Title", "Localized Title"),
    ],
    ids=[
        "parens at beginning",
        "bracket-likes",
        "split titles",
    ],
)
def test_exhentai_sanitizes(text, sanitized):
    assert exhentai_sanitize(text, split=True) == sanitized


@pytest.mark.parametrize(
    "data, expect",
    [
        (
            {"category": "doujinshi"},
            set(
                [
                    Category(value=enums.Category.DOUJINSHI),
                    Censorship(value=enums.Censorship.BAR),
                    Rating(value=enums.Rating.EXPLICIT),
                    Direction(value=enums.Direction.RIGHT_TO_LEFT),
                ]
            ),
        ),
        (
            {"eh_category": "doujinshi"},
            set(
                [
                    Category(value=enums.Category.DOUJINSHI),
                    Censorship(value=enums.Censorship.BAR),
                    Rating(value=enums.Rating.EXPLICIT),
                    Direction(value=enums.Direction.RIGHT_TO_LEFT),
                ]
            ),
        ),
        (
            {"category": "manga"},
            set(
                [
                    Category(value=enums.Category.MANGA),
                    Censorship(value=enums.Censorship.BAR),
                    Rating(value=enums.Rating.EXPLICIT),
                    Direction(value=enums.Direction.RIGHT_TO_LEFT),
                ]
            ),
        ),
        (
            {"category": "western"},
            set(
                [
                    Censorship(value=enums.Censorship.NONE),
                    Rating(value=enums.Rating.EXPLICIT),
                ]
            ),
        ),
        (
            {"category": "artist cg"},
            set(
                [
                    Category(value=enums.Category.COMIC),
                    Censorship(value=enums.Censorship.BAR),
                    Rating(value=enums.Rating.EXPLICIT),
                ]
            ),
        ),
        (
            {"category": "game cg"},
            set(
                [
                    Category(value=enums.Category.GAME_CG),
                    Censorship(value=enums.Censorship.BAR),
                    Rating(value=enums.Rating.EXPLICIT),
                ]
            ),
        ),
        (
            {"category": "image set"},
            set(
                [
                    Category(value=enums.Category.IMAGE_SET),
                    Censorship(value=enums.Censorship.BAR),
                    Rating(value=enums.Rating.EXPLICIT),
                ]
            ),
        ),
        (
            {"category": "non-h"},
            set(
                [
                    Censorship(value=enums.Censorship.NONE),
                    Rating(value=enums.Rating.QUESTIONABLE),
                ]
            ),
        ),
        (
            {"category": "western", "tags": ["other:western non-h"]},
            set(
                [
                    Censorship(value=enums.Censorship.NONE),
                    Rating(value=enums.Rating.QUESTIONABLE),
                ]
            ),
        ),
    ],
    ids=[
        "category from category field",
        "category from eh_category field",
        "manga category",
        "western category",
        "artist cg category",
        "game cg category",
        "image set category",
        "non-h category",
        "western non-h tag",
    ],
)
def test_exhentai_parses(data, expect):
    scraper = Scraper(ExHentaiHandler(), json.dumps(data | {"gid": 1, "token": 1}))

    expect.add(URL(value="https://exhentai.org/g/1/1"))

    assert set(scraper.collect()) == expect


@pytest.mark.parametrize(
    "tag, parsed",
    [
        ("parody:foo", World(name="foo")),
        ("artist:foo", Artist(name="foo")),
        ("character:foo", Character(name="foo")),
        ("group:foo", Circle(name="foo")),
        ("other:artbook", Category(value=enums.Category.ARTBOOK)),
        ("other:non-h imageset", Category(value=enums.Category.IMAGE_SET)),
        ("other:western imageset", Category(value=enums.Category.IMAGE_SET)),
        ("other:comic", Category(value=enums.Category.COMIC)),
        ("other:variant set", Category(value=enums.Category.VARIANT_SET)),
        ("other:webtoon", Category(value=enums.Category.WEBTOON)),
        ("other:full censorship", Censorship(value=enums.Censorship.FULL)),
        ("other:mosaic censorship", Censorship(value=enums.Censorship.MOSAIC)),
        ("other:uncensored", Censorship(value=enums.Censorship.NONE)),
        ("generic", Tag(namespace=None, tag="generic")),
    ],
    ids=[
        "parody",
        "group",
        "artist",
        "character",
        "other:artbook",
        "other:image set",
        "other:western image set",
        "other:comic",
        "other:variant set",
        "other:webtoon",
        "other:full censorship",
        "other:mosaic censorship",
        "other:uncensored",
        "generic",
    ],
)
def test_exhentai_parses_tags(tag, parsed):
    scraper = Scraper(
        ExHentaiHandler(), json.dumps({"tags": [tag], "gid": 1, "token": 1})
    )
    expect = set([URL(value="https://exhentai.org/g/1/1"), parsed])

    assert set(scraper.collect()) > expect