import os
import pytest
import shutil
import subprocess
import tango
import tempfile
import time

FILEDBDS_PATH = os.environ.get("FILEDBDS_PATH")
TEST_TANGO_HOST_PORT = os.environ.get("TEST_TANGO_HOST_PORT", "20000")
os.environ["TANGO_HOST"] = f"localhost:{TEST_TANGO_HOST_PORT}"

@pytest.fixture
def server_config():
    return {
        "dbdevice": "sys/database/2",
        "dbserver": "FileDbDs/2",
        "server": "exe/test",
        "class": "device_class",
        "devices": ["testing/tango_admin/1", "testing/tango_admin/2"]
    }

@pytest.fixture
def device_prop_config():
    return {
        "device": "testing/tango_admin/1",
        "name": "foo",
        "values": ["bar"],
    }

@pytest.fixture
def class_prop_config():
    return {
        "class": "device_class",
        "name": "foo",
        "values": ["bar"],
    }

@pytest.fixture
def free_prop_config():
    return {
        "object": "GreatFreeProperty",
        "name": "foo",
        "values": ["bar"],
    }


@pytest.fixture
def filedb_server():
    if FILEDBDS_PATH is None:
        raise RuntimeError("No FileDbDs path, did you mean to run me from ctest?")

    class BackgroundServer:
        def __init__(self, cmd):
            self.stdout = tempfile.TemporaryFile()
            self.stderr = tempfile.TemporaryFile()
            self.proc = subprocess.Popen(cmd, stdout=self.stdout, stderr=self.stderr)

        def wait(self, timeout):
            max_timestamp = time.time() + timeout
            self.stdout.seek(0)
            while True:
                line = self.stdout.readline()

                if line.startswith(b"Ready to accept request") and line.endswith(b"\n"):
                    break

                if time.time() > max_timestamp:
                    raise RuntimeError("Timeout waiting for ready string")

                if not line:
                    self.stdout.seek(0)
                    try:
                        self.proc.wait(0.1)
                        raise RuntimeError("Process stopped during startup")
                    except subprocess.TimeoutExpired:
                        continue



        def terminate(self, timeout):
            """
            Waits for a response from a process, blocking until it responds or times out.
            """
            self.proc.terminate()
            try:
                self.proc.wait(timeout)
            except subprocess.TimeoutExpired:
                self.proc.kill()
                self.proc.wait()

            #Reading outputs from the subprocess
            self.stdout.seek(0)
            outs = self.stdout.read()
            self.stderr.seek(0)
            errs = self.stderr.read()
            return outs, errs

    srv = None
    try:
        srv = BackgroundServer([FILEDBDS_PATH, "2", "-ORBendPoint", f"giop:tcp::{TEST_TANGO_HOST_PORT}"])
        srv.wait(10)

        yield srv.proc
    finally:
        if srv is not None:
            outs, errs = srv.terminate(10)
            width, _ = shutil.get_terminal_size(fallback=(80, 24))
            if outs:
                print("{:-^{width}}\n{}{:-^{width}}\n"
                      .format("FileDbDs stdout", outs.decode(errors="replace"), "", width=width))
            if errs:
                print("{:-^{width}}\n{}{:-^{width}}\n"
                      .format("FileDbDs stderr", errs.decode(errors="replace"), "", width=width))


@pytest.fixture
def filedb_server_with_server_added(filedb_server, server_config):
    db = tango.Database()
    dev_infos = []
    for dev in server_config["devices"]:
        info = tango.DbDevInfo()
        info.klass = server_config["class"]
        info.server = server_config["server"]
        info.name = dev
        dev_infos.append(info)
    info = tango.DbDevInfo()
    info.klass = "DServer"
    info.server = server_config["server"]
    info.name = f"dserver/{server_config['server']}"
    dev_infos.append(info)
    db.add_server(server_config["server"], dev_infos)
    yield filedb_server

@pytest.fixture
def filedb_server_with_prop_added(filedb_server_with_server_added, device_prop_config):
    db = tango.Database()
    db.put_device_property(device_prop_config["device"], {device_prop_config["name"]: device_prop_config["values"]})
    yield filedb_server_with_server_added
