"""Photos commands."""

from __future__ import annotations

from itertools import islice
from pathlib import Path
from typing import Optional

import typer

from pyicloud.cli.context import CLIAbort, get_state, service_call
from pyicloud.cli.normalize import normalize_album, normalize_photo
from pyicloud.cli.options import (
    DEFAULT_LOG_LEVEL,
    DEFAULT_OUTPUT_FORMAT,
    HttpProxyOption,
    HttpsProxyOption,
    LogLevelOption,
    NoVerifySslOption,
    OutputFormatOption,
    SessionDirOption,
    UsernameOption,
    store_command_options,
)
from pyicloud.cli.output import console_table

app = typer.Typer(help="Browse and download iCloud Photos.")


@app.command("albums")
def photos_albums(
    ctx: typer.Context,
    username: UsernameOption = None,
    session_dir: SessionDirOption = None,
    http_proxy: HttpProxyOption = None,
    https_proxy: HttpsProxyOption = None,
    no_verify_ssl: NoVerifySslOption = False,
    output_format: OutputFormatOption = DEFAULT_OUTPUT_FORMAT,
    log_level: LogLevelOption = DEFAULT_LOG_LEVEL,
) -> None:
    """List photo albums."""

    store_command_options(
        ctx,
        username=username,
        session_dir=session_dir,
        http_proxy=http_proxy,
        https_proxy=https_proxy,
        no_verify_ssl=no_verify_ssl,
        output_format=output_format,
        log_level=log_level,
    )
    state = get_state(ctx)
    api = state.get_api()
    photos = service_call("Photos", lambda: api.photos, account_name=api.account_name)
    payload = [
        normalize_album(album)
        for album in service_call(
            "Photos",
            lambda: list(photos.albums),
            account_name=api.account_name,
        )
    ]
    if state.json_output:
        state.write_json(payload)
        return
    state.console.print(
        console_table(
            "Photo Albums",
            ["Name", "Full Name", "Count"],
            [(album["name"], album["full_name"], album["count"]) for album in payload],
        )
    )


@app.command("list")
def photos_list(
    ctx: typer.Context,
    album: Optional[str] = typer.Option(
        None, "--album", help="Album name. Defaults to all photos."
    ),
    limit: int = typer.Option(50, "--limit", min=1, help="Maximum photos to show."),
    username: UsernameOption = None,
    session_dir: SessionDirOption = None,
    http_proxy: HttpProxyOption = None,
    https_proxy: HttpsProxyOption = None,
    no_verify_ssl: NoVerifySslOption = False,
    output_format: OutputFormatOption = DEFAULT_OUTPUT_FORMAT,
    log_level: LogLevelOption = DEFAULT_LOG_LEVEL,
) -> None:
    """List photo assets."""

    store_command_options(
        ctx,
        username=username,
        session_dir=session_dir,
        http_proxy=http_proxy,
        https_proxy=https_proxy,
        no_verify_ssl=no_verify_ssl,
        output_format=output_format,
        log_level=log_level,
    )
    state = get_state(ctx)
    api = state.get_api()
    photos = service_call("Photos", lambda: api.photos, account_name=api.account_name)
    album_obj = service_call(
        "Photos",
        lambda: photos.albums.find(album) if album else photos.all,
        account_name=api.account_name,
    )
    if album and album_obj is None:
        raise CLIAbort(f"No album named '{album}' was found.")
    payload = [
        normalize_photo(item)
        for item in service_call(
            "Photos",
            lambda: list(
                islice(
                    album_obj.photos if album_obj is not None else photos.all.photos,
                    limit,
                )
            ),
            account_name=api.account_name,
        )
    ]
    if state.json_output:
        state.write_json(payload)
        return
    state.console.print(
        console_table(
            "Photos",
            ["ID", "Filename", "Type", "Created", "Size"],
            [
                (
                    photo["id"],
                    photo["filename"],
                    photo["item_type"],
                    photo["created"],
                    photo["size"],
                )
                for photo in payload
            ],
        )
    )


@app.command("download")
def photos_download(
    ctx: typer.Context,
    photo_id: str = typer.Argument(..., help="Photo asset id."),
    output: Path = typer.Option(..., "--output", help="Destination file path."),
    version: str = typer.Option(
        "original", "--version", help="Photo version to download."
    ),
    username: UsernameOption = None,
    session_dir: SessionDirOption = None,
    http_proxy: HttpProxyOption = None,
    https_proxy: HttpsProxyOption = None,
    no_verify_ssl: NoVerifySslOption = False,
    output_format: OutputFormatOption = DEFAULT_OUTPUT_FORMAT,
    log_level: LogLevelOption = DEFAULT_LOG_LEVEL,
) -> None:
    """Download a photo asset."""

    store_command_options(
        ctx,
        username=username,
        session_dir=session_dir,
        http_proxy=http_proxy,
        https_proxy=https_proxy,
        no_verify_ssl=no_verify_ssl,
        output_format=output_format,
        log_level=log_level,
    )
    state = get_state(ctx)
    api = state.get_api()
    photos = service_call("Photos", lambda: api.photos, account_name=api.account_name)
    try:
        photo = service_call(
            "Photos",
            lambda: photos.all[photo_id],
            account_name=api.account_name,
        )
    except KeyError as err:
        raise CLIAbort(f"No photo matched '{photo_id}'.") from err
    data = service_call(
        "Photos",
        lambda: photo.download(version=version),
        account_name=api.account_name,
    )
    if data is None:
        raise CLIAbort("No data was returned for that photo version.")
    output.parent.mkdir(parents=True, exist_ok=True)
    output.write_bytes(data)
    if state.json_output:
        state.write_json(
            {"photo_id": photo_id, "path": str(output), "version": version}
        )
        return
    state.console.print(str(output))
