import sys
import click
import logging
from click_datetime import Datetime
from pprint import pformat as pf

if sys.version_info < (3, 4):
    print("To use this script you need python 3.4 or newer! got %s" %
          sys.version_info)
    sys.exit(1)

from pyHS100 import (SmartDevice,
                     SmartPlug,
                     SmartBulb,
                     SmartStrip,
                     Discover)  # noqa: E402

pass_dev = click.make_pass_decorator(SmartDevice)


@click.group(invoke_without_command=True)
@click.option('--ip', envvar="PYHS100_IP", required=False,
              help='The IP address of the device to connect to. This option '
              'is deprecated and will be removed in the future; use --host '
              'instead.')
@click.option('--host', envvar="PYHS100_HOST", required=False,
              help='The host name or IP address of the device to connect to.')
@click.option('--alias', envvar="PYHS100_NAME", required=False,
              help='The device name, or alias, of the device to connect to.')
@click.option('--debug/--normal', default=False)
@click.option('--bulb', default=False, is_flag=True)
@click.option('--plug', default=False, is_flag=True)
@click.option('--strip', default=False, is_flag=True)
@click.pass_context
def cli(ctx, ip, host, alias, debug, bulb, plug, strip):
    """A cli tool for controlling TP-Link smart home plugs."""
    if debug:
        logging.basicConfig(level=logging.DEBUG)
    else:
        logging.basicConfig(level=logging.INFO)

    if ctx.invoked_subcommand == "discover":
        return

    if ip is not None and host is None:
        host = ip

    if alias is not None and host is None:
        click.echo("Alias is given, using discovery to find host %s" %
                   alias)
        host = find_host_from_alias(alias=alias)
        if host:
            click.echo("Found hostname is {}".format(host))
        else:
            click.echo("No device with name {} found".format(alias))
            return

    if host is None:
        click.echo("No host name given, trying discovery..")
        ctx.invoke(discover)
        return
    else:
        if not bulb and not plug and not strip:
            click.echo("No --strip nor --bulb nor --plug given, discovering..")
            dev = Discover.discover_single(host)
        elif bulb:
            dev = SmartBulb(host)
        elif plug:
            dev = SmartPlug(host)
        elif strip:
            dev = SmartStrip(host)
        else:
            click.echo(
                "Unable to detect type, use --strip or --bulb or --plug!")
            return
        ctx.obj = dev

    if ctx.invoked_subcommand is None:
        ctx.invoke(state)


@cli.command()
@click.option('--timeout', default=3, required=False)
@click.option('--discover-only', default=False)
@click.pass_context
def discover(ctx, timeout, discover_only):
    """Discover devices in the network."""
    click.echo("Discovering devices for %s seconds" % timeout)
    found_devs = Discover.discover(timeout=timeout).items()
    if not discover_only:
        for ip, dev in found_devs:
            ctx.obj = dev
            ctx.invoke(state)
            print()

    return found_devs


def find_host_from_alias(alias, timeout=1, attempts=3):
    """Discover a device identified by its alias"""
    host = None
    click.echo("Trying to discover %s using %s attempts of %s seconds" %
               (alias, attempts, timeout))
    for attempt in range(1, attempts):
        click.echo("Attempt %s of %s" % (attempt, attempts))
        found_devs = Discover.discover(timeout=timeout).items()
        for ip, dev in found_devs:
            if dev.alias.lower() == alias.lower():
                host = dev.host
                return host
    return None


@cli.command()
@pass_dev
def sysinfo(dev):
    """Print out full system information."""
    click.echo(click.style("== System info ==", bold=True))
    click.echo(pf(dev.sys_info))


@cli.command()
@pass_dev
@click.pass_context
def state(ctx, dev):
    """Print out device state and versions."""
    click.echo(click.style("== %s - %s ==" % (dev.alias, dev.model),
                           bold=True))

    click.echo(click.style("Device state: %s" % "ON" if dev.is_on else "OFF",
                           fg="green" if dev.is_on else "red"))
    click.echo("Host/IP: %s" % dev.host)
    for k, v in dev.state_information.items():
        click.echo("%s: %s" % (k, v))
    click.echo(click.style("== Generic information ==", bold=True))
    click.echo("Time:         %s" % dev.time)
    click.echo("Hardware:     %s" % dev.hw_info["hw_ver"])
    click.echo("Software:     %s" % dev.hw_info["sw_ver"])
    click.echo("MAC (rssi):   %s (%s)" % (dev.mac, dev.rssi))
    click.echo("Location:     %s" % dev.location)

    ctx.invoke(emeter)


@cli.command()
@pass_dev
@click.argument('new_alias', required=False, default=None)
def alias(dev, new_alias):
    """Get or set the device alias."""
    if new_alias is not None:
        click.echo("Setting alias to %s" % new_alias)
        dev.alias = new_alias

    click.echo("Alias: %s" % dev.alias)


@cli.command()
@pass_dev
@click.option('--year', type=Datetime(format='%Y'),
              default=None, required=False)
@click.option('--month', type=Datetime(format='%Y-%m'),
              default=None, required=False)
@click.option('--erase', is_flag=True)
def emeter(dev, year, month, erase):
    """Query emeter for historical consumption."""
    click.echo(click.style("== Emeter ==", bold=True))
    if not dev.has_emeter:
        click.echo("Device has no emeter")
        return

    if erase:
        click.echo("Erasing emeter statistics..")
        dev.erase_emeter_stats()
        return

    if year:
        click.echo("== For year %s ==" % year.year)
        emeter_status = dev.get_emeter_monthly(year.year)
    elif month:
        click.echo("== For month %s of %s ==" % (month.month, month.year))
        emeter_status = dev.get_emeter_daily(year=month.year,
                                             month=month.month)
    else:
        emeter_status = dev.get_emeter_realtime()
        click.echo("== Current State ==")

    if isinstance(emeter_status, list):
        for plug in emeter_status:
            click.echo("Plug %d: %s" % (emeter_status.index(plug) + 1, plug))
    else:
        click.echo("%s" % emeter_status)


@cli.command()
@click.argument("brightness", type=click.IntRange(0, 100), default=None,
                required=False)
@pass_dev
def brightness(dev, brightness):
    """Get or set brightness."""
    if not dev.is_dimmable:
        click.echo("This device does not support brightness.")
        return
    if brightness is None:
        click.echo("Brightness: %s" % dev.brightness)
    else:
        click.echo("Setting brightness to %s" % brightness)
        dev.brightness = brightness


@cli.command()
@click.argument("temperature", type=click.IntRange(2500, 9000), default=None,
                required=False)
@pass_dev
def temperature(dev, temperature):
    """Get or set color temperature. (Bulb only)"""
    if temperature is None:
        click.echo("Color temperature: %s" % dev.color_temp)
        if dev.valid_temperature_range != (0, 0):
            click.echo("(min: %s, max: %s)" % dev.valid_temperature_range)
    else:
        click.echo("Setting color temperature to %s" % temperature)
        dev.color_temp = temperature


@cli.command()
@click.argument("h", type=click.IntRange(0, 360), default=None, required=False)
@click.argument("s", type=click.IntRange(0, 100), default=None, required=False)
@click.argument("v", type=click.IntRange(0, 100), default=None, required=False)
@click.pass_context
@pass_dev
def hsv(dev, ctx, h, s, v):
    """Get or set color in HSV. (Bulb only)"""
    if h is None:
        click.echo("Current HSV: %s %s %s" % dev.hsv)
    elif s is None or v is None:
        raise click.BadArgumentUsage("Setting a color requires 3 values.", ctx)
    else:
        click.echo("Setting HSV: %s %s %s" % (h, s, v))
        dev.hsv = (h, s, v)


@cli.command()
@click.argument('state', type=bool, required=False)
@pass_dev
def led(dev, state):
    """Get or set led state. (Plug only)"""
    if state is not None:
        click.echo("Turning led to %s" % state)
        dev.led = state
    else:
        click.echo("LED state: %s" % dev.led)


@cli.command()
@pass_dev
def time(dev):
    """Get the device time."""
    click.echo(dev.time)


@cli.command()
@click.argument('index', type=int, required=False)
@pass_dev
def on(plug, index):
    """Turn the device on."""
    click.echo("Turning on..")
    if index is None:
        plug.turn_on()
    else:
        plug.turn_on(index=(index - 1))


@cli.command()
@click.argument('index', type=int, required=False)
@pass_dev
def off(plug, index):
    """Turn the device off."""
    click.echo("Turning off..")
    if index is None:
        plug.turn_off()
    else:
        plug.turn_off(index=(index - 1))


@cli.command()
@click.option("--delay", default=1)
@pass_dev
def reboot(plug, delay):
    """Reboot the device."""
    click.echo("Rebooting the device..")
    plug.reboot(delay)


if __name__ == "__main__":
    cli()
