import json
import click

from wurker.settings import conf
from wurker.db import get_drone_status
from wurker.util import DateTimeEncoder
from wurker.cli.base import BaseCommand
from wurker.core import Wurkman
from wurker.drone import make_drone_clients
from wurker.models import WurkerBee, WurkerCron, WurkerCommand


class ShowCommand(BaseCommand):
    """Encapsulate show command."""

    def __init__(self, logger=None):
        BaseCommand.__init__(self, logger=logger, check_schema=True)

        # We might need drone API auth
        self.drone_api_auth = conf.ApiAuth


    def run(self, option:str, key:str='', drones:list=[], widowed:bool=False, return_result:bool=False):
        """Show Wurker definitions."""

        # Run run() on remote Drone(s) API?
        if drones:
            return self.run_drone(option, key, drones, widowed=widowed, return_result=return_result)

        # Method validation
        if option in ["command", "cron", "bee"]:
            run_option = f"show_{option}"
            run_method = getattr(self, run_option)
            return run_method(key=key, widowed=widowed, return_result=return_result)

        elif option in ["scheduled", "supervised", "ondemand", "hive", "all"]:
            return self.show_sched_typed(option, return_result=return_result)

        else:
            raise ValueError(f"Invalid 'option' value: '{option}'")


    def show_command(self, key:str='', widowed:bool=False, return_result:bool=False):
        """Show Wurker Command definitions."""

        with self.RemoteSession() as db:
            query = db.query(WurkerCommand).filter(WurkerCommand.app_id == self.app_id)
            if key:
                query = query.filter(WurkerCommand.slug == key)

            commands = []
            for command in query.all():
                if widowed and len(command.bees):
                    continue
                commands.append(self.mk_command_cfg(command))

            if return_result:
                return commands

            click.echo(json.dumps(commands, indent=2, cls=DateTimeEncoder))


    def show_cron(self, key:str='', widowed:bool=False, return_result:bool=False):
        """Show Wurker Cron definitions."""

        with self.RemoteSession() as db:
            query = db.query(WurkerCron).filter(WurkerCron.app_id == self.app_id)
            if key:
                query = query.filter(WurkerCron.slug == key)

            crons = []
            for cron in query.all():
                if widowed and len(cron.bees):
                    continue
                crons.append(self.mk_cron_cfg(cron))

            if return_result:
                return crons

            click.echo(json.dumps(crons, indent=2, cls=DateTimeEncoder))


    def show_bee(self, key:str='', return_result:bool=False, **kwargs):
        """Show Wurker Bee definitions."""

        with self.RemoteSession() as db:
            query = db.query(WurkerBee).filter(WurkerBee.app_id == self.app_id)
            if key:
                query = query.filter(WurkerBee.slug == key)

            bees = []
            for bee in query.all():
                bees.append(self.mk_bee_cfg(bee))

            if return_result:
                return bees

            click.echo(json.dumps(bees, indent=2, cls=DateTimeEncoder))


    def show_sched_typed(self, option:str, return_result:bool=False):
        """Show Wurker Bee definitions for schedule-typed Bees (ondemand, scheduled, supervised)."""

        wurkman = Wurkman(session=self.RemoteSession, logger=self.logger)
        bees, _ = wurkman.get_bees(show_cmd=True)

        sched_bees = {}
        if option == "all":
            sched_bees = bees
            sched_bees["hive"] = wurkman.get_hive(incl_duration=True)
        elif option == "hive":
            sched_bees = wurkman.get_hive(incl_duration=True)
        elif option in bees:
            sched_bees = bees[option]

        if return_result:
            return sched_bees

        click.echo(json.dumps(sched_bees, sort_keys=True, indent=2, cls=DateTimeEncoder))


    def run_drone(self, option:str, key:str, drones:list, widowed:bool=False, return_result:bool=False):
        """Run via Drone API(s)."""

        # 1. Get the DB status of the drone(s)
        try:
            drone_status = get_drone_status(self.remote_db_conf, self.app_id, drones=drones)
        except Exception as ex:
            click.echo(f"ERROR: {ex}", err=True)
            return

        if not drone_status:
            click.echo(f"WARNING: Queen ({self.app_id}) Drones have no 'wurker_status' data", err=True)
            return

        # 2. Make Drone API clients for the DB OK drone(s)
        try:
            drone_clients = make_drone_clients(drone_status, auth=self.drone_api_auth)
        except Exception as ex:
            click.echo(f"ERROR: {ex}", err=True)
            return

        # 3. Get results for the given command from the remote API
        results = {}
        for app_id, drone in drone_clients.items():
            result = drone.get_show(option=option, key=key)

            if not result:
                click.echo(f"WARNING: No 'show' result from Drone '{drone.slug}' ({drone.app_id})", err=True)
                continue

            results[app_id] = result

        if not results:
            click.echo(f"WARNING: No 'show' results from Drones", err=True)
            return

        if return_result:
            return results

        # OK, everything else would've printed to stderr... this is the final successful output
        click.echo(json.dumps(results, sort_keys=True, indent=2))


    def mk_bee_cfg(self, bee, cron=None):
        """Returns a Bee config dict (for now)."""

        if not cron and len(bee.crons):
            cron = bee.crons[0]

        cfg = {
            "id": bee.id,
            "app_id": str(bee.app_id),
            "name": bee.name,
            "slug": bee.slug,
            "description": bee.description,
            "command_id": bee.command_id,
            "command_slug": bee.command.slug,
            "args": bee.args,
            "quiet_restart": bee.quiet_restart,
            "enabled": bee.enabled,
            "module": None,
            "command": None,
        }

        cmd_key = "module" if bee.command.is_module else "command"
        cfg[cmd_key] = bee.command.command

        if cron is not None:
            cfg["cron_id"] = cron.id
            cfg["cron"] = cron.cron

        return cfg


    def mk_cron_cfg(self, cron):
        """Returns a Cron config dict (for now)."""

        return {
            "id": cron.id,
            "app_id": str(cron.app_id),
            "name": cron.name,
            "slug": cron.slug,
            "cron": cron.cron,
            "overrun": cron.overrun,
        }


    def mk_command_cfg(self, command):
        """Returns a Command config dict (for now)."""

        return {
            "id": command.id,
            "app_id": str(command.app_id),
            "name": command.name,
            "slug": command.slug,
            "description": command.description,
            "command": command.command,
            "is_module": command.is_module,
        }
