import json
import click

from wurker.settings import conf
from wurker.db import get_drone_status
from wurker.cli.base import BaseCommand
from wurker.drone import make_drone_clients
from wurker.util import DateTimeEncoder, str_to_datetime
from wurker.models import WurkerBeeReport, WurkerBee, WurkerCron


REPORT_COLUMNS = [
    'id', 'bee_id', 'bee_slug', 'bee_name', 'cron_id', 'cron_slug', 'cron_name',
    'hive_pid', 'hive_timestamp', 'hive_runcount', 'hive_duration', 'status',
    'created_at', 'updated_at',
]


class ReportCommand(BaseCommand):
    """Encapsulate the report 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='',
            start:str='',
            end:str='',
            page:int=0,
            page_limit:int=20,
            sort:str='id',
            sort_order:str='asc',
            drones:list=[],
            return_result:bool=False
    ):
        """View Wurker Report."""

        # Run via Drone API?
        if drones:
            return self.run_drone(key, start, end, drones, return_result=return_result)

        # Validate start/end if set
        start_dt = None
        if start:
            start_dt = str_to_datetime(start)

        end_dt = None
        if end:
            end_dt = str_to_datetime(end)

        # Validate sort column
        if sort not in REPORT_COLUMNS:
            raise Exception(f"Unknown sort column: '{sort}'")

        # OK get on with it!
        data = []
        with self.RemoteSession() as db:

            # We know this much for sure...
            query = db.query(WurkerBeeReport).filter(
                WurkerBeeReport.app_id == self.app_id
            ).order_by(WurkerBeeReport.id.asc())

            # Filter the query?
            if option == "bee" and key:
                query = query.filter(WurkerBeeReport.bee.has(WurkerBee.slug == key))
            elif option == "cron" and key:
                query = query.filter(WurkerBeeReport.cron.has(WurkerCron.slug == key))

            # If set, query daterange. Use `created` because `hive_timestamp` may be NULL
            if start_dt and end_dt:
                query = query.filter(
                    WurkerBeeReport.created_at >= start_dt,
                    WurkerBeeReport.created_at < end_dt
                )

            # Paginate the query
            total = query.count()
            begin = page * page_limit
            end = begin + page_limit

            # Actually run the paginated query
            for row in query[begin:end]:
                data.append({
                    "id": row.id,
                    "bee_id": row.wurker_bee_id,
                    "bee_slug": row.bee.slug,
                    "bee_name": row.bee.name,
                    "cron_id": row.wurker_cron_id,
                    "cron_slug": row.cron.slug if row.wurker_cron_id else None,
                    "cron_name": row.cron.name if row.wurker_cron_id else None,
                    "hive_pid": row.hive_pid,
                    "hive_timestamp": row.hive_timestamp.isoformat() if row.hive_timestamp else None,
                    "hive_runcount": row.hive_runcount,
                    "hive_duration": row.hive_duration,
                    "status": row.status.name,
                    "status_info": row.status_info,
                    "created_at": row.created_at.isoformat(),
                    "updated_at": row.updated_at.isoformat(),
                })

        # Now sort the results
        reverse = (sort_order == "desc")
        data = sorted(data, key=lambda x: x[sort], reverse=reverse)

        # We have the data, now always structure for possible pagination
        result = {
            "total": total,
            "page": page,
            "limit": page_limit,
            "data": data,
        }

        if return_result:
            return result

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


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

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

        drone_clients = make_drone_clients(drone_status, auth=conf.ApiAuth)

        results = {}
        for app_id, drone in drone_clients.items():
            result = drone.get_report(slug=key, start=start, end=end)

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

            results[app_id] = result

        if not results:
            click.echo(f"No 'report' 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))
