import click
import json as pyjson

from itertools import chain
from typing import Optional

from toot import api
from toot.cli.validators import validate_instance
from toot.entities import Instance, Status, from_dict, Account
from toot.exceptions import ApiError, ConsoleError
from toot.output import print_account, print_instance, print_search_results, print_status, print_timeline
from toot.cli.base import cli, json_option, pass_context, Context


@cli.command()
@json_option
@pass_context
def whoami(ctx: Context, json: bool):
    """Display logged in user details"""
    response = api.verify_credentials(ctx.app, ctx.user)

    if json:
        click.echo(response.text)
    else:
        account = from_dict(Account, response.json())
        print_account(account)


@cli.command()
@click.argument("account")
@json_option
@pass_context
def whois(ctx: Context, account: str, json: bool):
    """Display account details"""
    account_dict = api.find_account(ctx.app, ctx.user, account)

    # Here it's not possible to avoid parsing json since it's needed to find the account.
    if json:
        click.echo(pyjson.dumps(account_dict))
    else:
        account_obj = from_dict(Account, account_dict)
        print_account(account_obj)


@cli.command()
@click.argument("instance_url", required=False, callback=validate_instance)
@json_option
@pass_context
def instance(ctx: Context, instance_url: Optional[str], json: bool):
    """Display instance details"""
    default_url = ctx.app.base_url if ctx.app else None
    base_url = instance_url or default_url

    if not base_url:
        raise ConsoleError("Please specify an instance.")

    try:
        response = api.get_instance(base_url)
    except ApiError:
        raise ConsoleError(
            f"Instance not found at {base_url}.\n" +
            "The given domain probably does not host a Mastodon instance."
        )

    if json:
        print(response.text)
    else:
        instance = from_dict(Instance, response.json())
        print_instance(instance)


@cli.command()
@click.argument("query")
@click.option("-r", "--resolve", is_flag=True, help="Resolve non-local accounts")
@json_option
@pass_context
def search(ctx: Context, query: str, resolve: bool, json: bool):
    """Search for users or hashtags"""
    response = api.search(ctx.app, ctx.user, query, resolve)
    if json:
        print(response.text)
    else:
        print_search_results(response.json())


@cli.command()
@click.argument("status_id")
@json_option
@pass_context
def status(ctx: Context, status_id: str, json: bool):
    """Show a single status"""
    response = api.fetch_status(ctx.app, ctx.user, status_id)
    if json:
        print(response.text)
    else:
        status = from_dict(Status, response.json())
        print_status(status)


@cli.command()
@click.argument("status_id")
@json_option
@pass_context
def thread(ctx: Context, status_id: str, json: bool):
    """Show thread for a toot."""
    context_response = api.context(ctx.app, ctx.user, status_id)
    if json:
        print(context_response.text)
    else:
        toot = api.fetch_status(ctx.app, ctx.user, status_id).json()
        context = context_response.json()

        statuses = chain(context["ancestors"], [toot], context["descendants"])
        print_timeline(from_dict(Status, s) for s in statuses)