import contextlib
import os
import tempfile
from pathlib import Path
from typing import Callable
from typing import Dict
from typing import List
from unittest.mock import patch

import pytest
from resources import copy_file_fixture

from ytdl_sub.config.config_file import ConfigFile
from ytdl_sub.downloaders.downloader import Downloader
from ytdl_sub.entries.variables.kwargs import DESCRIPTION
from ytdl_sub.entries.variables.kwargs import EPOCH
from ytdl_sub.entries.variables.kwargs import EXT
from ytdl_sub.entries.variables.kwargs import EXTRACTOR
from ytdl_sub.entries.variables.kwargs import PLAYLIST_COUNT
from ytdl_sub.entries.variables.kwargs import PLAYLIST_ENTRY
from ytdl_sub.entries.variables.kwargs import PLAYLIST_INDEX
from ytdl_sub.entries.variables.kwargs import TITLE
from ytdl_sub.entries.variables.kwargs import UID
from ytdl_sub.entries.variables.kwargs import UPLOAD_DATE
from ytdl_sub.entries.variables.kwargs import WEBPAGE_URL


@pytest.fixture
def subscription_name(working_directory) -> str:
    name = "subscription_test"
    os.makedirs(Path(working_directory) / name, exist_ok=True)
    return name


@pytest.fixture
def config(working_directory) -> ConfigFile:
    return ConfigFile(
        name="config",
        value={"configuration": {"working_directory": working_directory}, "presets": {}},
    )


@pytest.fixture
def mock_downloaded_file_path(working_directory: str, subscription_name: str):
    def _mock_downloaded_file_path(file_name: str) -> Path:
        return Path(working_directory) / subscription_name / file_name

    return _mock_downloaded_file_path


@pytest.fixture
def mock_entry_dict_factory(mock_downloaded_file_path) -> Callable:
    def _mock_entry_dict_factory(
        uid: int,
        upload_date: str,
        playlist_index: int = 1,
        playlist_count: int = 1,
        is_youtube_channel: bool = False,
    ) -> Dict:
        entry_dict = {
            UID: uid,
            EPOCH: 1596878400,
            PLAYLIST_INDEX: playlist_index,
            PLAYLIST_COUNT: playlist_count,
            EXTRACTOR: "mock-entry-dict",
            TITLE: f"Mock Entry {uid}",
            EXT: "mp4",
            UPLOAD_DATE: upload_date,
            WEBPAGE_URL: f"https://{uid}.com",
            PLAYLIST_ENTRY: {"thumbnails": []},
            DESCRIPTION: "The Description",
        }

        if is_youtube_channel:
            entry_dict[PLAYLIST_ENTRY]["thumbnails"] = [
                {
                    "id": "avatar_uncropped",
                    "url": "https://avatar_uncropped.com",
                },
                {
                    "id": "banner_uncropped",
                    "url": "https://banner_uncropped.com",
                },
            ]

        # Create mock video file
        copy_file_fixture(
            fixture_name="sample_vid.mp4", output_file_path=mock_downloaded_file_path(f"{uid}.mp4")
        )
        copy_file_fixture(
            fixture_name="thumb.jpg", output_file_path=mock_downloaded_file_path(f"{uid}.jpg")
        )
        return entry_dict

    return _mock_entry_dict_factory


@pytest.fixture
def mock_download_collection_thumbnail(mock_downloaded_file_path):
    def _mock_download_thumbnail(output_path: str) -> bool:
        # mock_file_factory(file_name=output_path.split("/")[-1])
        output_name = os.path.basename(output_path)
        if "poster" in output_name or "show" in output_name:
            copy_file_fixture(fixture_name="poster.jpg", output_file_path=output_path)
            return True
        elif "fanart" in output_name:
            copy_file_fixture(fixture_name="fanart.jpeg", output_file_path=output_path)
            return True
        return False

    with patch.object(
        Downloader,
        "_download_thumbnail",
        new=lambda _, thumbnail_url, output_thumbnail_path: _mock_download_thumbnail(
            output_thumbnail_path
        ),
    ):
        yield  # TODO: create file here


@pytest.fixture
def mock_download_collection_entries(
    mock_download_collection_thumbnail, mock_entry_dict_factory: Callable, working_directory: str
):
    @contextlib.contextmanager
    def _mock_download_collection_entries_factory(is_youtube_channel: bool):
        def _(**kwargs):
            return mock_entry_dict_factory(**kwargs)

        def _write_entries_to_working_dir(*args, **kwargs) -> List[Dict]:
            if (len(args[0].collection.urls.list) == 1) or (
                "season.2" in kwargs["url"] and len(args[0].download_options.urls.list) > 1
            ):
                return [
                    _(
                        uid="21-1",
                        upload_date="20210808",
                        playlist_index=1,
                        playlist_count=4,
                        is_youtube_channel=is_youtube_channel,
                    ),  # 1
                    _(
                        uid="20-1",
                        upload_date="20200808",
                        playlist_index=2,
                        playlist_count=4,
                        is_youtube_channel=is_youtube_channel,
                    ),  # 2  98
                    _(
                        uid="20-2",
                        upload_date="20200808",
                        playlist_index=3,
                        playlist_count=4,
                        is_youtube_channel=is_youtube_channel,
                    ),  # 1  99
                    _(
                        uid="20-3",
                        upload_date="20200807",
                        playlist_index=4,
                        playlist_count=4,
                        is_youtube_channel=is_youtube_channel,
                    ),
                ]
            return [
                # 20-3 should resolve to collection 1 (which is season 2)
                _(
                    uid="20-3",
                    upload_date="20200807",
                    playlist_index=1,
                    playlist_count=5,
                    is_youtube_channel=is_youtube_channel,
                ),
                _(
                    uid="20-4",
                    upload_date="20200806",
                    playlist_index=2,
                    playlist_count=5,
                    is_youtube_channel=is_youtube_channel,
                ),
                _(
                    uid="20-5",
                    upload_date="20200706",
                    playlist_index=3,
                    playlist_count=5,
                    is_youtube_channel=is_youtube_channel,
                ),
                _(
                    uid="20-6",
                    upload_date="20200706",
                    playlist_index=4,
                    playlist_count=5,
                    is_youtube_channel=is_youtube_channel,
                ),
                _(
                    uid="20-7",
                    upload_date="20200606",
                    playlist_index=5,
                    playlist_count=5,
                    is_youtube_channel=is_youtube_channel,
                ),
            ]

        with patch.object(
            Downloader, "extract_info_via_info_json", new=_write_entries_to_working_dir
        ), patch.object(Downloader, "_extract_entry_info_with_retry", new=lambda _, entry: entry):
            # Stub out metadata. TODO: update this if we do metadata plugins
            yield

    return _mock_download_collection_entries_factory
