import sys
import uuid
import click

from wurker.db import make_session
from wurker.models import WurkerControl, WurkerStatus


def update_status(db_conf:object, app_id:str, status:dict, **kwargs):
    """
    Insert or update 'wurker_status' for the given app ID and data from 'status' subcommand.
    Raises an Exception on error instead of returning True/False.
    """

    required = ["app_id", "name", "slug", "is_queen", "env",]
    allowed = ["private_ip", "public_ip", "server_domain",]

    # Normalize the data and throw exceptions for missing required keys
    data = {}
    for key in required:
        if key not in status:
            raise TypeError(f"Missing required key in 'status' parameter: '{key}'")

        data[key] = status[key]

    data.update({k:status[k] for k in allowed if k in status})

    # OK we can upsert, now
    session = make_session(db_conf, **kwargs)
    with session() as db:
        existing = db.query(WurkerStatus).filter(WurkerStatus.app_id == data["app_id"]).first()

        if existing is None:
            insert = WurkerStatus(**data)
            db.add(insert)
        else:
            for key, value in data.items():
                setattr(existing, key, value)

        db.commit()


def get_drone_status(db_conf:object, app_id:str, drones:list=[], **kwargs) -> list:
    """Obtain wurker_status records for the Drone(s) belonging to the given Queen app_id."""

    session = make_session(db_conf=db_conf, **kwargs)
    with session() as db:

        # Filter by Drone slug or ID?
        drone_ids = []
        for item in drones:
            drone_id = None
            try:
                uuid.UUID(item)
            except ValueError:
                try:
                    drone_id = db.query(WurkerControl).join(
                        WurkerStatus, WurkerStatus.app_id == WurkerControl.app_id
                    ).filter(
                        WurkerStatus.slug == item
                    ).one().app_id

                except:
                    raise Exception(f"Drone slug '{item}' not found for this Queen")

            else:
                try:
                    drone_id = db.query(WurkerControl).filter(WurkerControl.app_id == item).one().app_id
                except:
                    raise Exception(f"ERROR: Drone ID '{item}' not found for this Queen")

            if not drone_id:
                raise Exception(f"Drone identifier '{item}' not recognized", file=sys.stderr)

            drone_ids.append(drone_id)

        # ... then use those IDs to filter our query ...
        query = db.query(WurkerControl).filter(WurkerControl.app_id != app_id)
        if len(drone_ids):
            query = query.filter(WurkerControl.app_id.in_(drone_ids))

        drone_control = query.all()
        if not len(drone_control):
            click.echo(f"WARNING: Queen ({app_id}) has no Drones in her Colony", err=True)
            return []

        # ... and finally use the filtered controls to get drone statuses from DB
        drone_status = db.query(WurkerStatus).filter(
            WurkerStatus.app_id.in_([c.app_id for c in drone_control]),
            WurkerStatus.is_queen != True
        ).all()

        if not len(drone_status):
            return []

        return drone_status
