import logging

from wurker.settings import conf
from wurker import (
    StatusCommand, ShowCommand, ControlCommand, AddCommand, EditCommand, RemoveCommand, KillCommand, ReportCommand
)
from wurker.ui.auth import api_auth_required

from flask import Blueprint, g, request, jsonify
from werkzeug.exceptions import abort


bp = Blueprint('api', __name__, url_prefix="/api")

# TODO
from wurker.util import init_logger
logger = init_logger(name="Wurker")


@bp.get('/status')
@bp.get('/drone/<drone_id>/status')
@api_auth_required
def status(drone_id:str=''):
    """Obtain the status of this Wurker (Queen or Drone)."""

    if drone_id and not conf.is_queen:
        return None, 404

    drones = [drone_id] if drone_id else []

    try:
        cmd = StatusCommand()
        status = cmd.run(drones=drones, return_result=True)
    except Exception as ex:
        return jsonify({"error": f"{ex}"}), 400

    info = (
        status if not drone_id
        else {} if not status or drone_id not in status
        else status[drone_id]
    )

    if not info:
        return jsonify({"error": f"Error obtaining status"}), 400

    for db_key in ["local_db", "remote_db"]:
        if db_key in info and info[db_key]:
            for obfuscate in ["password", "dsn", "url"]:
                if obfuscate in info[db_key] and info[db_key][obfuscate]:
                    info[db_key][obfuscate] = obfuscate.upper()

    return jsonify(info)


@bp.get('/show')
@bp.get('/show/<option>')
@bp.get('/show/<option>/<key>')
@bp.get('/drone/<drone_id>/show')
@bp.get('/drone/<drone_id>/show/<option>')
@bp.get('/drone/<drone_id>/show/<option>/<key>')
@api_auth_required
def show(option:str='', key:str='', drone_id:str=''):
    """Obtain the 'show' data for this Wurker (Queen or Drone)."""

    if drone_id and not conf.is_queen:
        return None, 404

    drones = [drone_id] if drone_id else []

    options = ["supervised", "scheduled", "ondemand", "hive", "command", "cron", "bee",]
    keyed = ["command", "cron", "bee",]
    if option:
        if option not in options:
            return jsonify({"error": f"Invalid option: '{option}'"}), 400
        if key and option not in keyed:
            return jsonify({"error": f"Option '{option}' does not accept arguments"}), 400

    if not option:
        option = "all"

    try:
        cmd = ShowCommand()
        show = cmd.run(option=option, key=key, drones=drones, return_result=True)
    except Exception as ex:
        return jsonify({"error": f"{ex}"}), 400

    return jsonify(items)


@bp.post('/control/<action>')
@bp.post('/drone/<drone_id>/control/<action>')
@api_auth_required
def control(action:str, drone_id:str=''):
    """Request a 'control' subcommand action for this Wurker (Queen or Drone)."""

    if drone_id and not conf.is_queen:
        return None, 404

    drones = [drone_id] if drone_id else []

    if action not in ["reload", "pause"]:
        return jsonify({"error": f"Invalid control action: '{action}'"}), 400

    try:
        cmd = ControlCommand()
        cmd.run(action, drones=drones)
    except Exception as ex:
        return jsonify({"error": f"{ex}"}), 400

    return jsonify(True)


@bp.post('/add/<option>')
@bp.post('/drone/<drone_id>/add/<option>')
@api_auth_required
def add(option:str, drone_id:str=''):
    """Request an 'add' subcommand with required option and JSON payload (Queen or Drone)."""

    if drone_id and not conf.is_queen:
        return None, 404

    drones = [drone_id] if drone_id else []

    data = request.json
    if not data:
        return jsonify({"error": f"Missing payload to add '{option}'"})

    try:
        cmd = AddCommand()
        cmd.run(option, data, drones=drones)
    except Exception as ex:
        return jsonify({"error": f"{ex}"}), 400

    return jsonify(True)


@bp.post('/edit/<option>/<key>')
@bp.post('/edit/<option>/<key>')
@bp.post('/drone/<drone_id>/edit/<option>/<key>')
@api_auth_required
def edit(option:str, key:str, drone_id:str=''):
    """Request an 'edit' subcommand with required option and at least one key and JSON (Queen or Drone)."""

    if drone_id and not conf.is_queen:
        return None, 404

    drones = [drone_id] if drone_id else []

    data = request.json
    if not data:
        return jsonify({"error": f"Missing payload to edit '{option}'"})

    try:
        cmd = EditCommand()
        cmd.run(option, key, data, drones=drones)
    except Exception as ex:
        return jsonify({"error": f"{ex}"}), 400

    return jsonify(True)


@bp.post('/remove/<option>/<key>')
@bp.post('/drone/<drone_id>/remove/<option>/<key>')
@api_auth_required
def remove(option:str, key:str, drone_id:str=''):
    """Request a 'remove' subcommand with required option and at least one key (Queen or Drone)."""

    if drone_id and not conf.is_queen:
        return None, 404

    drones = [drone_id] if drone_id else []

    try:
        cmd = RemoveCommand()
        cmd.run(option, key, drones=drones)
    except Exception as ex:
        return jsonify({"error": f"{ex}"}), 400

    return jsonify(True)


@bp.post('/kill')
@bp.post('/kill/<option>')
@bp.post('/kill/<option>/<key>')
@bp.post('/drone/<drone_id>/kill')
@bp.post('/drone/<drone_id>/kill/<option>')
@bp.post('/drone/<drone_id>/kill/<option>/<key>')
@api_auth_required
def kill(option:str='', key:str='', drone_id:str=''):
    """Request a 'kill' subcommand with optional option and key (Queen or Drone)."""

    if drone_id and not conf.is_queen:
        return None, 404

    drones = [drone_id] if drone_id else []

    if not option:
        option = "hive"
    if not key or option in ["hive", "supervised", "scheduled", "ondemand"]:
        key = ''

    try:
        cmd = KillCommand()
        cmd.run(option, key, drones=drones)
    except Exception as ex:
        return jsonify({"error": f"{ex}"}), 400

    return jsonify(True)


@bp.get('/report/<slug>')
@bp.get('/report/<slug>/daterange/<start>/<end>')
@bp.get('/drone/<drone_id>/report/<slug>')
@bp.get('/drone/<drone_id>/report/<slug>/daterange/<start>/<end>')
@api_auth_required
def report(slug:str, start:str='', end:str='', drone_id:str=''):
    """Obtain the 'report' data for this Wurker (Queen or Drone) and given Bee slug."""

    if drone_id and not conf.is_queen:
        return None, 404

    drones = [drone_id] if drone_id else []

    # Pagination and sorting
    page = int(request.args.get('page', 0))
    page_limit = int(request.args.get('limit', 20))
    sort = request.args.get('sort', 'id')
    sort_order = request.args.get('order', 'asc')

    try:
        cmd = ReportCommand()
        report = cmd.run(
            option="bee",
            key=slug,
            start=start,
            end=end,
            page=page,
            page_limit=page_limit,
            sort=sort,
            sort_order=sort_order,
            drones=drones,
            return_result=True
        )

    except Exception as ex:
        return jsonify({"error": f"{ex}"}), 400

    return jsonify(report)
