import logging
import logging.handlers
import os
import subprocess
import time

import requests
from behave import *

formats = {
    "default": '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$http_x_forwarded_for"',
    "upstream-time": '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$http_x_forwarded_for" $upstream_response_time',
    "upstream-connect-time": '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$http_x_forwarded_for" $upstream_response_time $upstream_connect_time',
    "request-time": '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$http_x_forwarded_for" $request_time'
}

bin_name = './prometheus-nginxlog-exporter'


@given('a running exporter listening on "{filename}" with format')
def run_exporter_impl(context, filename):
    filename = '.behave-sandbox/%s' % filename
    p = subprocess.Popen([bin_name, '--format', context.text, filename],
                         stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    time.sleep(.5)

    if p.returncode is not None:
        raise Exception(
            'exporter exited too soon with exit code %d' % p.returncode)

    context.process = p


@given('a running exporter listening on "{filename}" with {format} format')
def run_exporter_impl(context, filename, format):
    filename = '.behave-sandbox/%s' % filename
    p = subprocess.Popen([bin_name, '--format', formats[format], filename],
                         stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    time.sleep(.5)

    if p.returncode is not None:
        raise Exception(
            'exporter exited too soon with exit code %d' % p.returncode)

    context.process = p


@given(u'a running exporter listening with configuration file "{config}"')
def run_exporter_configfile_impl(context, config):
    p = subprocess.Popen([bin_name, '-config-file', 'features/%s' % config],
                         stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    time.sleep(.5)

    if p.returncode is not None:
        raise Exception(
            'exporter exited too soon with exit code %d' % p.returncode)

    context.process = p


@when(u'the following HTTP request is logged to "{filename}"')
@when(u'the following HTTP requests are logged to "{filename}"')
def step_impl(context, filename):
    filename = '.behave-sandbox/%s' % filename
    with open(filename, 'a') as f:
        f.write("%s\n" % context.text)
    time.sleep(.5)


@when(u'the following HTTP request is logged to syslog on port {port}')
@when(u'the following HTTP requests are logged to syslog on port {port}')
def step_impl(context, port):
    log = logging.getLogger('test')
    log_handler = logging.handlers.SysLogHandler(
        ("localhost", int(port)), logging.handlers.SysLogHandler.LOG_USER)
    log.addHandler(log_handler)

    lines = [l for l in context.text.split("\n") if l != ""]
    for l in lines:
        log.info(l)

    time.sleep(.5)


@when(u'the following HTTP request is logged to syslog on socket {socket}')
@when(u'the following HTTP requests are logged to syslog on socket {socket}')
def step_impl(context, socket: str):
    log = logging.getLogger('test')
    log_handler = logging.handlers.SysLogHandler(
        address=socket, facility=logging.handlers.SysLogHandler.LOG_USER)
    log.addHandler(log_handler)

    lines = [l for l in context.text.split("\n") if l != ""]
    for l in lines:
        log.info(l)

    time.sleep(.5)


@then(u'the exporter should report value {val} for metric {metric}')
@then(u'the exporter should on "{endpoint}" report value {val} for metric {metric}')
def step_impl(context, val, metric, endpoint='/metrics'):
    endpoint = endpoint.lstrip("/")
    while True:
        try:
            url = 'http://localhost:4040/%s' % endpoint
            print(url)
            response = requests.get(url)
            text = response.text
            break
        except requests.ConnectionError:
            continue

    lines = [l.strip("\n") for l in text.split("\n")]
    if not "%s %s" % (metric, val) in lines:
        raise AssertionError(
            'expected metric "%s" could not be verified. Actual metrics:\n%s' % (metric, text))


@then("the process should be running")
def step_impl(context):
    rc = context.process.poll()
    if rc is not None:
        raise AssertionError(f"The process has exited with return code {rc}")

def current_process(context) -> subprocess.Popen:
    return context.process

@when("the exporter is stopped")
def step_impl(context):
    p = current_process(context)
    p.terminate()
    p.wait(10)

    if p.poll() is None:
        raise AssertionError("The process is still running")

@then("the socket {socket} should not exist")
def step_impl(context, socket):
    if os.path.exists(socket):
        raise AssertionError(f"The socket {socket} exists")
