import logging
import sys
import time

import aprslib
import click
from aprslib.exceptions import LoginError
from oslo_config import cfg

import aprsd
import aprsd.packets  # noqa : F401
from aprsd import (
    cli_helper,
    conf,  # noqa : F401
    packets,
    utils,
)
from aprsd.client.client import APRSDClient
from aprsd.main import cli
from aprsd.packets import collector
from aprsd.packets import log as packet_log
from aprsd.threads import tx

CONF = cfg.CONF
LOG = logging.getLogger('APRSD')


@cli.command()
@cli_helper.add_options(cli_helper.common_options)
@click.option(
    '--aprs-login',
    envvar='APRS_LOGIN',
    show_envvar=True,
    help='What callsign to send the message from. Defaults to config entry.',
)
@click.option(
    '--aprs-password',
    envvar='APRS_PASSWORD',
    show_envvar=True,
    help='the APRS-IS password for APRS_LOGIN. Defaults to config entry.',
)
@click.option(
    '--no-ack',
    '-n',
    is_flag=True,
    show_default=True,
    default=False,
    help="Don't wait for an ack, just sent it to APRS-IS and bail.",
)
@click.option(
    '--wait-response',
    '-w',
    is_flag=True,
    show_default=True,
    default=False,
    help='Wait for a response to the message?',
)
@click.option('--raw', default=None, help='Send a raw message.  Implies --no-ack')
@click.argument('tocallsign', required=True)
@click.argument('command', nargs=-1, required=True)
@click.pass_context
@cli_helper.process_standard_options
def send_message(
    ctx,
    aprs_login,
    aprs_password,
    no_ack,
    wait_response,
    raw,
    tocallsign,
    command,
):
    """Send a message to a callsign via APRS_IS."""
    global got_ack, got_response
    quiet = ctx.obj['quiet']

    if not aprs_login:
        if CONF.aprs_network.login == conf.client.DEFAULT_LOGIN:
            click.echo('Must set --aprs_login or APRS_LOGIN')
            ctx.exit(-1)
            return
        else:
            aprs_login = CONF.aprs_network.login

    if not aprs_password:
        if not CONF.aprs_network.password:
            click.echo('Must set --aprs-password or APRS_PASSWORD')
            ctx.exit(-1)
            return
        else:
            aprs_password = CONF.aprs_network.password

    LOG.info(f'Python version: {sys.version}')
    LOG.info(f'APRSD SEND_MESSAGE Started version: {aprsd.__version__}')
    utils.package.log_installed_extensions_and_plugins()
    if type(command) is tuple:
        command = ' '.join(command)
    if not quiet:
        if raw:
            LOG.info(f"L'{aprs_login}' R'{raw}'")
        else:
            LOG.info(f"L'{aprs_login}' To'{tocallsign}' C'{command}'")

    got_ack = False
    got_response = False

    def rx_packet(packet):
        global got_ack, got_response
        cl = APRSDClient()
        packet = cl.decode_packet(packet)
        collector.PacketCollector().rx(packet)
        packet_log.log(packet, tx=False)
        # LOG.debug("Got packet back {}".format(packet))
        if isinstance(packet, packets.AckPacket):
            got_ack = True
        else:
            got_response = True
            from_call = packet.from_call
            our_call = CONF.callsign.lower()
            tx.send(
                packets.AckPacket(
                    from_call=our_call,
                    to_call=from_call,
                    msgNo=packet.msgNo,
                ),
                direct=True,
            )

        if got_ack:
            if wait_response:
                if got_response:
                    sys.exit(0)
            else:
                sys.exit(0)

    try:
        APRSDClient().client  # noqa: B018
    except LoginError:
        sys.exit(-1)

    # Send a message
    # then we setup a consumer to rx messages
    # We should get an ack back as well as a new message
    # we should bail after we get the ack and send an ack back for the
    # message
    if raw:
        tx.send(
            packets.Packet(from_call='', to_call='', raw=raw),
            direct=True,
        )
        sys.exit(0)
    else:
        tx.send(
            packets.MessagePacket(
                from_call=aprs_login,
                to_call=tocallsign,
                message_text=command,
            ),
            direct=True,
        )

    if no_ack:
        sys.exit(0)

    try:
        # This will register a packet consumer with aprslib
        # When new packets come in the consumer will process
        # the packet
        aprs_client = APRSDClient()
        aprs_client.consumer(rx_packet, raw=False)
    except aprslib.exceptions.ConnectionDrop:
        LOG.error('Connection dropped, reconnecting')
        time.sleep(5)
        # Force the deletion of the client object connected to aprs
        # This will cause a reconnect, next time client.get_client()
        # is called
        aprs_client.reset()
