import os
import pytest
import shutil
import subprocess
import tango

TANGO_ADMIN_PATH = os.environ.get("TANGO_ADMIN_PATH")

def run_tango_admin(args):
    if TANGO_ADMIN_PATH is None:
        raise RuntimeError("No tango_admin path, did you mean to run me from ctest?")

    args = [TANGO_ADMIN_PATH] + args

    print(f'Running "{" ".join(args)}"')

    result = subprocess.run(args, capture_output=True)

    width, _ = shutil.get_terminal_size(fallback=(80, 24))
    if result.stdout:
        print("{:-^{width}}\n{}{:-^{width}}\n"
                .format("tango_admin stdout", result.stdout.decode(errors="replace"), "", width=width))
    if result.stderr:
        print("{:-^{width}}\n{}{:-^{width}}\n"
                .format("tango_admin stderr", result.stderr.decode(errors="replace"), "", width=width))

    return result

def test_help():
    result = run_tango_admin(['--help'])

    assert result.returncode == 0

def test_ping_database(filedb_server):
    result = run_tango_admin(["--ping-database"])

    assert result.returncode == 0

def test_ping_database_with_timeout(filedb_server):
    result = run_tango_admin(["--ping-database", "1.0"])

    assert result.returncode == 0

def test_ping_database_with_wrong_timeout_param_type(filedb_server):
    result = run_tango_admin(["--ping-database", "blah"])

    assert result.returncode == 2

def test_ping_device(filedb_server, server_config):
    result = run_tango_admin(["--ping-device", server_config["dbdevice"]])

    assert result.returncode == 0

def test_ping_device_with_timeout(filedb_server, server_config):
    result = run_tango_admin(["--ping-device", server_config["dbdevice"], "1.0"])

    assert result.returncode == 0

def test_ping_device_no_device_param(filedb_server, server_config):
    result = run_tango_admin(["--ping-device"])

    assert result.returncode == 2

def test_ping_device_with_wrong_timeout_param_type(filedb_server, server_config):
    result = run_tango_admin(["--ping-device", server_config["dbdevice"], "blah"])

    assert result.returncode == 2

def test_ping_device_with_timeout_wrong_param_order(filedb_server, server_config):
    result = run_tango_admin(["--ping-device", "1.0", server_config["dbdevice"]])

    assert result.returncode == 2

def test_ping_network():
    result = run_tango_admin(['--ping-network'])

    assert result.returncode == 0

def test_ping_network_verbose():
    result = run_tango_admin(['--ping-network', '-v'])

    assert result.returncode == 0

def test_ping_network_with_timeout():
    result = run_tango_admin(['--ping-network', '1.0'])

    assert result.returncode == 0

def test_ping_network_verbose_with_timeout():
    result = run_tango_admin(['--ping-network', '1.0', '-v'])

    assert result.returncode == 0

def test_ping_network_with_wrong_timeout_param_type():
    result = run_tango_admin(['--ping-network', 'blah'])

    assert result.returncode == 2

def test_ping_network_verbose_with_timeout_wrong_param_order():
    result = run_tango_admin(['--ping-network', '-v', '1.0'])

    assert result.returncode == 2

def test_ping_network_verbose_with_wrong_timeout_param_type():
    result = run_tango_admin(['--ping-network', 'blah', '-v'])

    assert result.returncode == 2

def test_add_server(filedb_server, server_config):
    args = ["--add-server", server_config["server"], server_config["class"], ",".join(server_config["devices"])]

    result = run_tango_admin(args)

    assert result.returncode == 0

    db = tango.Database()
    servers = sorted([str(s) for s in db.get_server_list().value_string])
    assert servers == sorted([server_config["server"], server_config["dbserver"]])
    devices = sorted([str(d) for d in db.get_device_name("*", server_config["class"]).value_string])
    assert devices == sorted(server_config["devices"])

def test_check_server(filedb_server, server_config):
    args = ["--check-server", server_config["server"]]

    result = run_tango_admin(args)

    assert result.returncode == 1

    args = ["--check-server", server_config["dbserver"]]

    result = run_tango_admin(args)

    assert result.returncode == 0

def test_delete_server(filedb_server_with_server_added, server_config):
    args = ["--delete-server", server_config["server"]]

    result = run_tango_admin(args)

    assert result.returncode == 0

    db = tango.Database()
    for name in db.get_server_list().value_string:
        assert str(name) != server_config["server"]

def test_delete_server_with_properties(filedb_server_with_prop_added, server_config, device_prop_config):
    args = ["--delete-server", server_config["server"], "--with-properties"]

    result = run_tango_admin(args)

    assert result.returncode == 0

    db = tango.Database()
    for name in db.get_server_list().value_string:
        assert str(name) != server_config["server"]

    properties = db.get_device_property(device_prop_config["device"], device_prop_config["name"])
    values = [str(p) for p in properties[device_prop_config["name"]]]
    assert values == []

def test_check_class(filedb_server_with_server_added, server_config):
    result = run_tango_admin(['--check-class', 'ThisClassNotExist'])

    assert result.returncode == 1

    result = run_tango_admin(['--check-class', server_config["class"]])

    assert result.returncode == 0

def test_check_device(filedb_server, server_config):
    result = run_tango_admin(['--check-device', server_config["devices"][0]])

    assert result.returncode == 1

    result = run_tango_admin(['--check-device', server_config["dbdevice"]])

    assert result.returncode == 0

def test_check_device_exported(filedb_server_with_server_added, server_config):
    result = run_tango_admin(['--check-device-exported', server_config["devices"][0]])

    assert result.returncode == 1

    result = run_tango_admin(['--check-device-exported', server_config["dbdevice"]])

    assert result.returncode == 0

def test_check_device_ready(filedb_server_with_server_added, server_config):
    result = run_tango_admin(['--check-device-ready', server_config["devices"][0]])

    assert result.returncode == 1

    result = run_tango_admin(['--check-device-ready', server_config["dbdevice"]])

    assert result.returncode == 0

def test_server_list(filedb_server_with_server_added, server_config):
    result = run_tango_admin(['--server-list'])

    assert result.returncode == 0

    servers = sorted(result.stdout.decode(errors="replace").strip().split(" "))
    expected = sorted([server_config["server"].split("/")[0], server_config["dbserver"].split("/")[0]])
    assert servers == expected

def test_server_instance_list(filedb_server_with_server_added, server_config):
    result = run_tango_admin(['--server-instance-list', server_config["server"].split("/")[0]])

    assert result.returncode == 0

    servers = result.stdout.decode(errors="replace").strip().split(" ")
    assert servers == [server_config["server"].split("/")[1]]

def test_server_instance_list_no_match(filedb_server_with_server_added, server_config):
    result = run_tango_admin(['--server-instance-list', "notExisting"])

    assert result.returncode == 1

    output = result.stderr.decode(errors="replace").strip()
    assert output =="No server with a name that starts with \"notExisting\" is defined in TangoDB."

def test_add_device_property(filedb_server_with_server_added, device_prop_config):
    result = run_tango_admin(['--add-property', device_prop_config["device"], device_prop_config["name"], ",".join(device_prop_config["values"])])

    assert result.returncode == 0

    db = tango.Database()
    properties = db.get_device_property(device_prop_config["device"], device_prop_config["name"])
    values = [str(p) for p in properties[device_prop_config["name"]]]
    assert values == device_prop_config["values"]

def test_delete_device_property(filedb_server_with_prop_added, device_prop_config):
    result = run_tango_admin(['--delete-property', device_prop_config["device"], device_prop_config["name"]])

    assert result.returncode == 0

    db = tango.Database()
    properties = db.get_device_property(device_prop_config["device"], device_prop_config["name"])
    values = [str(p) for p in properties[device_prop_config["name"]]]
    assert values == []

def test_add_class_property(filedb_server_with_server_added, class_prop_config):
    result = run_tango_admin(['--add-class-property', class_prop_config["class"], class_prop_config["name"], ",".join(class_prop_config["values"])])

    assert result.returncode == 0

    db = tango.Database()
    properties = db.get_class_property(class_prop_config["class"], class_prop_config["name"])
    values = [str(p) for p in properties[class_prop_config["name"]]]
    assert values == class_prop_config["values"]

def test_delete_class_property(filedb_server_with_prop_added, class_prop_config):
    result = run_tango_admin(['--delete-class-property', class_prop_config["class"], class_prop_config["name"]])

    assert result.returncode == 0

    db = tango.Database()
    properties = db.get_class_property(class_prop_config["class"], class_prop_config["name"])
    values = [str(p) for p in properties[class_prop_config["name"]]]
    assert values == []

def test_add_free_property(filedb_server_with_server_added, free_prop_config):
    result = run_tango_admin(['--add-free-property', free_prop_config["object"], free_prop_config["name"], ",".join(free_prop_config["values"])])

    assert result.returncode == 0

    db = tango.Database()
    properties = db.get_property(free_prop_config["object"], free_prop_config["name"])
    values = [str(p) for p in properties[free_prop_config["name"]]]
    assert values == free_prop_config["values"]

def test_delete_free_property(filedb_server_with_prop_added, free_prop_config):
    result = run_tango_admin(['--delete-free-property', free_prop_config["object"], free_prop_config["name"]])

    assert result.returncode == 0

    db = tango.Database()
    properties = db.get_property(free_prop_config["object"], free_prop_config["name"])
    values = [str(p) for p in properties[free_prop_config["name"]]]
    assert values == []

def test_unexport_device(filedb_server, server_config):
    result = run_tango_admin(['--unexport-device', server_config["dbdevice"]])

    assert result.returncode == 0

    db = tango.Database()
    info = db.import_device(server_config["dbdevice"])
    assert not info.exported

def test_tac_enabled(filedb_server):
    result = run_tango_admin(['--tac-enabled'])

    # TODO: The logic here seems backwards
    assert result.returncode == 0

    db = tango.Database()
    db.put_property("CtrlSystem", {"Services": ["AccessControl/tango:not/areal/device"]} )

    result = run_tango_admin(['--tac-enabled'])

    assert result.returncode == 1
