from py.xml import html
import pytest
import subprocess
import xmlrpc
import time


stderr = list()
stdout = list()
apperrs = list()


def pytest_html_results_summary(prefix, summary, postfix):
    prefix.extend([html.h3("stderr")])
    prefix.extend([html.pre("".join([line for line in stderr]))])
    prefix.extend([html.h3("stdout")])
    prefix.extend([html.pre("".join([line for line in stdout]))])
    prefix.extend([html.h3("App errors")])
    prefix.extend([html.pre("".join([line for line in apperrs]))])


@pytest.fixture
def process_alive(app, process, process_communicate):
    def func():
        try:
            global apperrs
            # We need to call something on the rpc server
            # the process might still be running but frozen or only almost dead
            apperrs += app.getErrors()
        except Exception as e:
            # Process probably died
            pass
        finally:
            process_communicate()
            exit_code = process.poll()
            if exit_code is not None:
                print(f"Process exited with {exit_code}")
            return exit_code is None

    yield func


@pytest.fixture
def process_communicate(process, report_summary):
    def func():
        try:
            output_b, errors_b = process.communicate(timeout=0.1)
            output = output_b.decode("utf-8")
            errors = errors_b.decode("utf-8")
            print(errors)
            print(output)
            report_summary[0].append(output)
            report_summary[1].append(errors)
        except subprocess.TimeoutExpired:
            pass

    yield func


@pytest.fixture
def report_summary():
    yield stdout, stderr


@pytest.fixture
def process():
    filenames = [
        "./output/bin/qfield",
        "./output/bin/Release/qfield.exe",
        "./output/bin/Debug/qfield.exe",
        "./output/bin/qfield.app/qfield.exe",
        "./output/bin/qfield.app/Contents/MacOS/qfield",
    ]
    for filename in filenames:
        try:
            process = subprocess.Popen(
                filename, stdout=subprocess.PIPE, stderr=subprocess.PIPE
            )
            yield process
            break
        except FileNotFoundError:
            pass
    else:
        assert False, f"No qfield executable found in {filenames}"


@pytest.fixture
def app(process, process_communicate):
    """
    Starts a qfield process and connects an xmlrpc client to it.
    Returns the xmlrpc client that can send commands to the running process.

    Will wait up to 30 seconds for the process to start and return as soon
    as either the process is started and QML initialized, the process exits
    or the timeout occurs.

    Also makes sure it cleans up properly after running the app.
    """
    app = xmlrpc.client.ServerProxy("http://localhost:9000")
    app.process = process

    start = time.time()
    while True:
        try:
            exit_code = process.poll()
            if exit_code is not None:
                process_communicate(process)
                assert exit_code is None
            app.existsAndVisible("mainWindow")
            yield app
            break
        except (ConnectionRefusedError, OSError) as e:
            if time.time() - start > 30:
                process_communicate(process)
                assert False  # Could not start app after 30 seconds
            print(str(e))
            time.sleep(0.2)
        except Exception as e:
            process_communicate(process)
            assert False  # Unexcpected exception while starting up app

    try:
        app.quit()
        app.quit()
    except Exception:
        print("Exception while trying to exit app. The process probably died.")
        process_communicate()
        raise
    timeout = 5
    try:
        process.wait(timeout)
    except subprocess.TimeoutExpired:
        print(f"Process did not quit after {timeout} second. Killing it.")
        process.kill()
    process_communicate()
