from os.path import isdir
from typing import OrderedDict
from behave import *
import os
import stat
from pathlib import Path

try: 
    from steps.common import Case, ThumbValidator
except ModuleNotFoundError:
    from common import Case, ThumbValidator


@then(u'the path to the {size} thumb is returned')
def step_path_to_thumb_is_returned(context, size):
    case: Case = context.case
    result = case.result
    assert result is not None
    expected_dir = os.path.join(
        context.case.thumb_dir,
        size,
        context.case.thumb_name
    )
    assert result.stdout.strip() == expected_dir, "Unexpected amt output.\nactual: {}\nexpected: {}".format(result.stdout.strip(), expected_dir)


@then(u'the path to the input file is returned')
def step_impl(context):
    case: Case = context.case
    result = case.result
    assert result is not None
    assert result.stdout.strip() == case.infile, "Unexpected amt output.\nactual: {}\nexpected: {}".format(result.stdout.strip(), case.infile)


@then(u'nothing is returned on stdout')
def step_nothing_is_returned(context):
    case: Case = context.case
    result = case.result
    assert result is not None
    assert result.stdout.strip() == "", "expected no stdout, but '{}' has been returned".format(context.result.stdout)

@then(u'allmytoes terminates with exit code {code}')
def step_terminates_with_exit_code(context, code):
    case: Case = context.case
    result = case.result
    assert result is not None
    assert result.returncode == int(code.strip()), "exit code was expected to be {} but is {}".format(code, result.returncode)

@then(u'the thumbs have not been changed')
def step_thumbs_have_not_been_changed(context):
    for path, mtime in context.case.prepared_thumbs_mtimes.items():
        assert context.case.prepared_thumbs_mtimes[path] == os.path.getmtime(path)


@then(u'valid thumbs for all sizes exist')
def step_valid_thumbs_exist(context):
    case: Case = context.case
    ThumbValidator.validate_case(case)


def _assert_permissions_of_nodes(root_dir: str, check_directories: bool, expected_permission):
    """
    For a given root directory check _either_ all directories _or_ all files beneath (recursively)
    to have a certain permission set.
    """
    base = Path(root_dir)
    assert os.path.exists(base) 
    deviations = OrderedDict()
    for child_name in base.glob('**/*'):
        child_path = os.path.join(base, child_name)
        if os.path.isdir(child_path) == check_directories:
            actual_permisson = stat.S_IMODE(os.stat(child_path).st_mode)
            if actual_permisson != expected_permission:
                deviations[child_path] = actual_permisson
    assert len(deviations) == 0, "Not all {} have the expected permission {}. Deviations:\n{}".format(
                "directories" if check_directories else "files",
                oct(expected_permission),
                "\n".join(["  {}: {}".format(k, oct(v)) for k, v in deviations.items()])
            )

@then(u'the cache directory and all sub-directories have permissions 700')
def step_directories_with_permission_700_exist(context):
    case: Case = context.case
    _assert_permissions_of_nodes(
        root_dir = case.cache_dir,
        check_directories = True,
        expected_permission = 0o700
    )
         

@then(u'all the files within the thumb-directories have permissions 600')
def step_files_have_permission_600(context):
    case: Case = context.case
    _assert_permissions_of_nodes(
        root_dir = case.cache_dir,
        check_directories = False,
        expected_permission = 0o600
    )
