import contextlib
import json
import re

from .helpers.common import exec_command
from .helpers.marathon import app, pod
from .test_marathon import _list_tasks

list_regex = '/stuck-(?:sleep|pod)\W+[^Z]+Z\W+9\W+(?:True|False)' \
             '\W+\d\W+\d\W+[^Z]+Z\W+[^Z]+Z'


def test_debug_list():
    with _stuck_app():
        returncode, stdout, stderr = exec_command(
            ['dcos', 'marathon', 'debug', 'list'])

        assert returncode == 0
        assert stderr == b''

        decoded = stdout.decode()
        assert 'WAITING' in decoded
        assert '/stuck-sleep' in decoded
        """A line in the output looks like
        /stuck-sleep $since 9 True 4 3 $last_unsued_offer $last_used_offer
        Therefore `list_regex` tests this
        """
        assert re.search(list_regex, decoded) is not None


def test_debug_list_json():
    with _stuck_app():
        returncode, stdout, stderr = exec_command(
            ['dcos', 'marathon', 'debug', 'list', '--json'])

        assert returncode == 0
        assert stderr == b''

        decoded = stdout.decode()
        assert 'overdue' in decoded
        assert '/stuck-sleep' in decoded
        assert '"reason": "UnfulfilledConstraint"' in decoded


def test_debug_list_pod():
    with _stuck_pod():
        returncode, stdout, stderr = exec_command(
            ['dcos', 'marathon', 'debug', 'list'])

        assert returncode == 0
        assert stderr == b''

        decoded = stdout.decode()
        assert 'WAITING' in decoded
        assert '/stuck-pod' in decoded
        """A line in the output looks like
        /stuck-sleep $since 9 True 4 3 $last_unsued_offer $last_used_offer
        Therefore `list_regex` tests this
        """
        assert re.search(list_regex, decoded) is not None


def test_debug_list_pod_json():
    with _stuck_pod():
        returncode, stdout, stderr = exec_command(
            ['dcos', 'marathon', 'debug', 'list', '--json'])

        assert returncode == 0
        assert stderr == b''

        decoded = stdout.decode()
        assert 'overdue' in decoded
        assert '/stuck-pod' in decoded
        assert '"reason": "UnfulfilledConstraint"' in decoded


def test_debug_summary():
    with _stuck_app():
        returncode, stdout, stderr = exec_command(
            ['dcos', 'marathon', 'debug', 'summary', '/stuck-sleep'])

        assert returncode == 0
        assert stderr == b''

        decoded = stdout.decode()
        assert 'CONSTRAINTS' in decoded
        assert "[['hostname', 'UNIQUE']]" in decoded
        assert '0.00%' in decoded


def test_debug_summary_json():
    with _stuck_app():
        returncode, stdout, stderr = exec_command(
            ['dcos', 'marathon', 'debug', 'summary', '/stuck-sleep', '--json'])

        assert returncode == 0
        assert stderr == b''

        decoded = stdout.decode().replace(' ', '').replace('\n', '')
        assert '"reason":"UnfulfilledConstraint"' in decoded
        assert '{"declined":0,"processed":0,"reason":"InsufficientCpus"}' \
               in decoded


def test_debug_summary_pod():
    with _stuck_pod():
        returncode, stdout, stderr = exec_command(
            ['dcos', 'marathon', 'debug', 'summary', '/stuck-pod'])

        assert returncode == 0
        assert stderr == b''

        decoded = stdout.decode()
        assert 'CONSTRAINTS' in decoded
        assert "'operator': 'UNIQUE'" in decoded
        assert "'fieldName': 'hostname'" in decoded
        assert '0.00%' in decoded


def test_debug_summary_pod_json():
    with _stuck_pod():
        returncode, stdout, stderr = exec_command(
            ['dcos', 'marathon', 'debug', 'summary', '/stuck-pod', '--json'])

        assert returncode == 0
        assert stderr == b''

        decoded = stdout.decode().replace(' ', '').replace('\n', '')
        assert '"reason":"UnfulfilledConstraint"' in decoded
        assert '{"declined":0,"processed":0,"reason":"InsufficientCpus"}' \
               in decoded


def test_debug_details():
    with _stuck_app():
        returncode, stdout, stderr = exec_command(
            ['dcos', 'marathon', 'debug', 'details', '/stuck-sleep'])

        assert returncode == 0
        assert stderr == b''

        decoded = stdout.decode()
        """The public agent has all resources to launch this task,
        but not the matching role, therefore the output should be:
        ok        -        ok    ok   ok    ok
        To avoid formatting issues, whitespaces are ignored.
        """
        assert 'ok-okokokok' in decoded.replace(' ', '')

        returncode, stdout, stderr = exec_command(['dcos', 'node', '--json'])

        assert returncode == 0
        assert stderr == b''
        agent_count = len(
          [n for n in json.loads(stdout.decode('utf-8'))
           if n['type'] == 'agent']
        )

        """The extra two lines come from the heading and the empty line at the
        end of the table.
        """
        assert len(decoded.split('\n')) == agent_count + 2


def test_debug_details_json():
    with _stuck_app():
        returncode, stdout, stderr = exec_command(
            ['dcos', 'marathon', 'debug', 'details', '/stuck-sleep', '--json'])

        assert returncode == 0
        assert stderr == b''

        decoded = stdout.decode().replace(' ', '').replace('\n', '')
        assert '"reason":"UnfulfilledConstraint"' in decoded
        assert '{"declined":0,"processed":0,"reason":"InsufficientCpus"}' \
               in decoded


def test_debug_details_pod():
    with _stuck_pod():
        returncode, stdout, stderr = exec_command(
            ['dcos', 'marathon', 'debug', 'details', '/stuck-pod'])

        assert returncode == 0
        assert stderr == b''

        decoded = stdout.decode()
        """The public agent has all resources to launch this task,
        but not the matching role, therefore the output should be:
        ok        -        ok    ok   ok    ok
        To avoid formatting issues, whitespaces are ignored.
        """
        assert 'ok-okokokok' in decoded.replace(' ', '')

        returncode, stdout, stderr = exec_command(['dcos', 'node', '--json'])

        assert returncode == 0
        assert stderr == b''
        agent_count = len(
          [n for n in json.loads(stdout.decode('utf-8'))
           if n['type'] == 'agent']
        )

        """The extra two lines come from the heading and the empty line at the
        end of the table.
        """
        assert len(decoded.split('\n')) == agent_count + 2


def test_debug_details_pod_json():
    with _stuck_pod():
        returncode, stdout, stderr = exec_command(
            ['dcos', 'marathon', 'debug', 'details', '/stuck-pod', '--json'])

        assert returncode == 0
        assert stderr == b''

        decoded = stdout.decode().replace(' ', '').replace('\n', '')
        assert '"reason":"UnfulfilledConstraint"' in decoded
        assert '{"declined":0,"processed":0,"reason":"InsufficientCpus"}' \
               in decoded


@contextlib.contextmanager
def _stuck_app(max_count=300):
    with app('tests/data/marathon/apps/stuck_sleep.json',
             'stuck-sleep', False):
        count = 0
        while count < max_count:
            tasks = _list_tasks(app_id='stuck-sleep')
            if len(tasks) == 1:
                break
        yield


@contextlib.contextmanager
def _stuck_pod(max_count=300):
    with pod('tests/data/marathon/pods/stuck_sleep.json',
             '/stuck-pod', False):
        count = 0
        while count < max_count:
            returncode, stdout, stderr = exec_command(
                ['dcos', 'marathon', 'pod', 'list'])
            if '/stuck-pod1' in str(stdout).replace(' ', ''):
                break
        yield
