from pathlib import Path

import click

from commands.generate_timestamps import generate_timestamps
from commands.get_courses import get_courses
from commands.get_images import get_images
from commands.get_lessons import get_lessons
from commands.get_words import get_words
from commands.library_overview import overview
from commands.markdown import markdown
from commands.merge import merge
from commands.patch import patch_audios
from commands.post import PAIRING_STRATEGIES, post
from commands.post_yt_playlist import post_yt_playlist
from commands.reindex import reindex
from commands.replace import replace
from commands.resplit import resplit
from commands.show import show_course, show_my, show_status
from commands.sort_lessons import sort_lessons
from commands.stats import stats
from commands.yomitan import yomitan

DEFAULT_OUT_PATH = Path("downloads")
DEFAULT_OUT_WORDS_PATH = DEFAULT_OUT_PATH / "lingqs"
DEFAULT_AUDIOS_FOLDER = None
DEFAULT_TEXTS_FOLDER = None


def opath_option():  # noqa: ANN201
    return click.option(
        "--opath",
        "-o",
        default=DEFAULT_OUT_PATH,
        show_default=True,
        type=click.Path(path_type=Path),
        help="Output path.",
    )


@click.group()
@click.version_option(package_name="lingq")
def cli() -> None:
    """Lingq command line scripts.

    You can always get more details about a command with the --help flag.
    """


@cli.command("setup")
@click.argument("apikey")
def setup_cli(apikey: str) -> None:
    """Creates or updates an .env file with your LingQ API key.

    You can find you key here: https://www.lingq.com/accounts/apikey/
    """
    env_file = Path(".env")

    if env_file.exists():
        with env_file.open("r") as file:
            lines = file.readlines()

        # Update the API_KEY if it exists, otherwise add a new line
        with env_file.open("w") as file:
            api_key_found = False
            for line in lines:
                if line.startswith("APIKEY="):
                    file.write(f"APIKEY={apikey}\n")
                    api_key_found = True
                else:
                    file.write(line)

            if not api_key_found:
                file.write(f"APIKEY={apikey}\n")

        print(".env file has been updated.")
    else:
        with env_file.open("w") as file:
            file.write(f"APIKEY={apikey}\n")

        print(".env file has been created.")


@cli.group()
def show() -> None:
    """Show commands."""


@show.command("my")
@click.argument("lang")
@click.option("-s", "--shared", is_flag=True, default=False, show_default=True)
@click.option("-c", "--codes", is_flag=True, default=False, show_default=True)
@click.option("-v", "--verbose", is_flag=True, default=False, show_default=True)
def show_my_cli(lang: str, shared: bool, codes: bool, verbose: bool) -> None:
    """Show my collections in a given language."""
    show_my(lang, shared=shared, codes=codes, verbose=verbose)


@show.command("course")
@click.argument("lang")
@click.argument("course_id")
@click.option("-s", "--shared", is_flag=True, default=False, show_default=True)
@click.option("-c", "--codes", is_flag=True, default=False, show_default=True)
@click.option("-v", "--verbose", is_flag=True, default=False, show_default=True)
def show_course_cli(
    lang: str,
    course_id: int,
    shared: bool,
    codes: bool,
    verbose: bool,
) -> None:
    """Show lessons in a course."""
    show_course(lang, course_id, shared=shared, codes=codes, verbose=verbose)


@show.command("status")
@click.argument("lang")
def show_status_cli(lang: str) -> None:
    """Show pending and refused lessons in a language."""
    show_status(lang)


@cli.group()
def get() -> None:
    """Get commands."""


@get.command("images")
@click.argument("lang")
@click.argument("course_id")
@opath_option()
def get_images_cli(lang: str, course_id: int, opath: Path) -> None:
    """Get images."""
    get_images(lang, course_id, opath)


@get.command("words")
@click.argument("langs", nargs=-1)
@opath_option()
def get_words_cli(langs: list[str], opath: Path) -> None:
    """Get words (LingQs)."""
    get_words(langs, opath)


@cli.command("yomitan")
@click.argument("langs", nargs=-1)
@click.option(
    "--ipath",
    "-i",
    default=DEFAULT_OUT_WORDS_PATH,
    show_default=True,
    type=click.Path(exists=True, path_type=Path),
    help="Input path.",
)
def yomitan_cli(langs: list[str], ipath: Path) -> None:
    """Make a Yomitan dictionary from a dump generated by 'get_words'.

    If no language codes are given, use all languages.
    """
    yomitan(langs, ipath)


@get.command("lessons")
@click.argument("lang")
@click.argument("course_id")
@opath_option()
@click.option(
    "--skip-downloaded",
    "-s",
    is_flag=True,
    default=False,
    show_default=True,
    help="Skip already downloaded lessons.",
)
@click.option("--download-audio", is_flag=True, default=False, help="If set, also download audio.")
@click.option(
    "--download-timestamps", is_flag=True, default=False, help="If set, also download timestamps."
)
@click.option("--with-index", is_flag=True, default=False, help="If set, add index to the title.")
def get_lessons_cli(
    lang: str,
    course_id: int,
    opath: Path,
    skip_downloaded: bool,
    download_audio: bool,
    download_timestamps: bool,
    with_index: bool,
) -> None:
    """Get every lesson from a course id.

    CAREFUL: This reorders your 'Continue studying' shelf.
    """
    get_lessons(
        lang,
        course_id,
        opath,
        skip_downloaded=skip_downloaded,
        download_audio=download_audio,
        download_timestamps=download_timestamps,
        write=True,
        with_index=with_index,
    )


@get.command("courses")
@click.argument("langs", nargs=-1)
@click.option("--download-audio", is_flag=True, default=False, help="If set, also download audio.")
@click.option(
    "--download-timestamps", is_flag=True, default=False, help="If set, also download timestamps."
)
@click.option(
    "--skip-downloaded",
    "-s",
    is_flag=True,
    default=False,
    show_default=True,
    help="Skip already downloaded lessons.",
)
@click.option(
    "--batch-size",
    "-b",
    type=int,
    default=1,
    show_default=True,
    help="Number of courses to download simultanously. "
    "Increasing this too much may incur in throttling. Suggested: 1 or 2.",
)
@opath_option()
def get_courses_cli(
    langs: list[str],
    opath: Path,
    download_audio: bool,
    download_timestamps: bool,
    skip_downloaded: int,
    batch_size: int,
) -> None:
    """Get every course from a list of languages.

    CAREFUL: This reorders your 'Continue studying' shelf.

    If no language codes are given, use all languages.
    """
    get_courses(
        langs,
        opath,
        download_audio=download_audio,
        download_timestamps=download_timestamps,
        skip_downloaded=skip_downloaded,
        batch_size=batch_size,
    )


@cli.command("post")
@click.argument("lang")
@click.argument("course_id")
@click.option(
    "--texts-folder",
    "-t",
    default=DEFAULT_TEXTS_FOLDER,
    type=click.Path(exists=True, path_type=Path),
    help="Texts folder path.",
)
@click.option(
    "--audios-folder",
    "-a",
    default=DEFAULT_AUDIOS_FOLDER,
    type=click.Path(exists=True, path_type=Path),
    help="Audios folder path.",
)
@click.option(
    "--pairing-strategy",
    type=click.Choice(PAIRING_STRATEGIES),
    default="fuzzy",
    show_default=True,
)
def post_cli(
    lang: str,
    course_id: int,
    texts_folder: Path | None,
    audios_folder: Path | None,
    pairing_strategy: str,
) -> None:
    """Upload lessons.

    When no texts are given, LingQ will use whisper to transcribe.
    """
    post(
        lang,
        course_id,
        texts_folder,
        audios_folder,
        pairing_strategy,
    )


@cli.command("postyt")
@click.argument("lang")
@click.argument("course_id")
@click.argument("playlist_url")
@click.option("--skip-uploaded", default=True, show_default=True)
def post_yt_playlist_cli(
    lang: str,
    course_id: int,
    playlist_url: str,
    skip_uploaded: bool,
) -> None:
    """Post a youtube playlist."""
    post_yt_playlist(
        lang,
        course_id,
        playlist_url,
        skip_uploaded=skip_uploaded,
        skip_no_cc=True,
    )


@cli.command("merge")
@click.argument("lang")
@click.argument("fr_course_id")
@click.argument("to_course_id")
def merge_cli(lang: str, fr_course_id: int, to_course_id: int) -> None:
    """Merge courses.

    Merge all the lessons in course FR into course TO.
    """
    merge(lang, fr_course_id, to_course_id)


@cli.command("reindex")
@click.argument("lang")
@click.argument("course_id")
def reindex_cli(lang: str, course_id: int) -> None:
    """Reindex titles in a course."""
    reindex(lang, course_id)


@cli.group()
def patch() -> None:
    """Patch commands."""


@patch.command("audios")
@click.argument("lang")
@click.argument("course_id")
@click.option(
    "--audios-folder",
    "-a",
    default=DEFAULT_AUDIOS_FOLDER,
    type=click.Path(exists=True, path_type=Path),
    help="Audios folder path.",
)
def patch_audios_cli(lang: str, course_id: int, audios_folder: str) -> None:
    """Patch a course audio."""
    patch_audios(lang, course_id, audios_folder)


@patch.command("texts")
def patch_texts_cli() -> None:
    """Not implemented."""
    raise NotImplementedError()


@cli.command("replace")
@click.argument("course_id")
@click.argument("choice", type=click.Choice(["fst", "snd"]))
def replace_ja_cli(course_id: int, choice: str) -> None:
    """Replace words in a course (only for japanese).

    This is a hack to fix a bug on LingQ's side. Do not use.
    """

    # TODO: delete this... I'm tired to fix their own issues...
    _to_ignore = "『』「」"
    repl_ja = {k: f"DUMMY{idx}" for idx, k in enumerate(_to_ignore)}
    repl_ja_inv = {v: k for k, v in repl_ja.items()}
    replacements = repl_ja if choice == "fst" else repl_ja_inv
    replace("ja", course_id, replacements)


@cli.command("resplit")
@click.argument("course_id")
def resplit_ja_cli(course_id: int) -> None:
    """Resplit words in a course (only for japanese)."""
    resplit(course_id)


@cli.command("stats")
@click.argument("lang")
def stats_cli(lang: str) -> None:
    """Show some simple stats."""
    stats(lang)


@cli.command("markdown")
@click.argument("langs", nargs=-1)
@click.option(
    "--select-courses",
    default="all",
    show_default=True,
    type=click.Choice(["all", "mine", "shared"]),
    help="Select which courses to include.",
)
@click.option(
    "--include-views",
    is_flag=True,
    default=False,
    show_default=True,
    help="Include the number of views in the markdown.",
)
@opath_option()
def markdown_cli(
    langs: list[str],
    select_courses: str,
    include_views: bool,
    opath: Path,
) -> None:
    """Generate markdown files for the given language codes.

    If no language codes are given, use all languages.
    """
    markdown(langs, select_courses, include_views, opath)


@cli.command("timestamp")
@click.argument("lang")
@click.argument("course_id")
@click.option("--skip-timestamped", default=True)
def generate_timestamps_cli(lang: str, course_id: int, skip_timestamped: bool) -> None:
    """Generate timestamps for a course."""
    generate_timestamps(lang, course_id, skip_timestamped)


@cli.command("overview")
@click.argument("lang")
def overview_cli(lang: str) -> None:
    """Library overview."""
    overview(lang)


@cli.command("sort")
@click.argument("lang")
@click.argument("course_id")
def sort_lessons_cli(lang: str, course_id: int) -> None:
    """Sort all lessons from a course."""
    sort_lessons(lang, course_id)


if __name__ == "__main__":
    cli()
