#   Licensed to the Apache Software Foundation (ASF) under one
#   or more contributor license agreements.  See the NOTICE file
#   distributed with this work for additional information
#   regarding copyright ownership.  The ASF licenses this file
#   to you under the Apache License, Version 2.0 (the
#   "License"); you may not use this file except in compliance
#   with the License.  You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.

import sys


def get_file_text(path):
    """ Returns file text by path"""
    file_io = open(path, "r")
    text = file_io.read()
    file_io.close()
    return text


def get_file_output(encoding="utf-8", path=sys.argv[-1], arg_string=""):
    """
    Returns answer file output
    :param encoding: to decode output in python3
    :param path: path of file to execute
    :return: list of strings
    """
    import subprocess

    proc = subprocess.Popen([sys.executable, path], stdin=subprocess.PIPE, stdout=subprocess.PIPE,
                            stderr=subprocess.STDOUT)
    if arg_string:
        for arg in arg_string.split("\n"):
            proc.stdin.write(bytearray(str(arg) + "\n", encoding))
            proc.stdin.flush()

    return list(map(lambda x: str(x.decode(encoding)), proc.communicate()[0].splitlines()))


def test_file_importable():
    """ Tests there is no obvious syntax errors"""
    path = sys.argv[-1]
    if not path.endswith(".py"):
        import os

        parent = os.path.abspath(os.path.join(path, os.pardir))
        python_files = [f for f in os.listdir(parent) if os.path.isfile(os.path.join(parent, f)) and f.endswith(".py")]
        for python_file in python_files:
            if python_file == "tests.py":
                continue
            check_importable_path(os.path.join(parent, python_file))
        return
    check_importable_path(path)


def check_importable_path(path):
    """ Checks that file is importable.
        Reports failure otherwise.
    """
    saved_input = patch_input()
    try:
        import_file(path)
    except:
        failed("The file contains syntax errors", test_file_importable.__name__)
        return
    finally:
        revert_input(saved_input)

    passed(test_file_importable.__name__)


def patch_input():
    def mock_fun(_m=""):
        return "mock"

    if sys.version_info[0] == 3:
        import builtins
        save_input = builtins.input
        builtins.input = mock_fun
        return save_input
    elif sys.version_info[0] == 2:
        import __builtin__
        save_input = __builtin__.raw_input
        __builtin__.raw_input = mock_fun
        __builtin__.input = mock_fun
        return save_input


def revert_input(saved_input):
    if sys.version_info[0] == 3:
        import builtins
        builtins.input = saved_input
    elif sys.version_info[0] == 2:
        import __builtin__
        __builtin__.raw_input = saved_input
        __builtin__.input = saved_input


def import_file(path):
    """ Returns imported file """
    if sys.version_info[0] == 2 or sys.version_info[1] < 3:
        import imp

        return imp.load_source("tmp", path)
    elif sys.version_info[0] == 3:
        import importlib.machinery

        return importlib.machinery.SourceFileLoader("tmp", path).load_module("tmp")


def import_task_file():
    """ Returns imported file.
        Imports file from which check action was run
    """
    path = sys.argv[-1]
    return import_file(path)


def test_is_not_empty():
    """
        Checks that file is not empty
    """
    path = sys.argv[-1]
    file_text = get_file_text(path)

    if len(file_text) > 0:
        passed()
    else:
        failed("The file is empty. Please, reload the task and try again.")


def test_text_equals(text, error_text):
    """
        Checks that answer equals text.
    """
    path = sys.argv[-1]
    file_text = get_file_text(path)

    if file_text.strip() == text:
        passed()
    else:
        failed(error_text)


def test_answer_placeholders_text_deleted(
        error_text="Solution has empty answer prompt(s)."):
    """
        Checks that all answer placeholders are not empty
    """
    windows = get_answer_placeholders()

    for window in windows:
        if len(window) == 0:
            failed(error_text)
            return
    passed()


def set_congratulation_message(message):
    """ Overrides default 'Congratulations!' message """
    print("#educational_plugin CONGRATS_MESSAGE " + message)


def failed(message="Please, reload the task and try again.", name=None):
    """ Reports failure """
    if not name:
        name = sys._getframe().f_back.f_code.co_name
    print("#educational_plugin " + name + " FAILED + " + message)


def passed(name=None):
    """ Reports success """
    if not name:
        name = sys._getframe().f_back.f_code.co_name
    print("#educational_plugin " + name + " test OK")


def get_answer_placeholders():
    """
        Returns all answer placeholders text
    """
    prefix = "#educational_plugin_window = "
    path = sys.argv[-1]
    import os

    file_name_without_extension = os.path.splitext(path)[0]
    windows_path = file_name_without_extension + "_windows"
    windows = []
    f = open(windows_path, "r")
    window_text = ""
    first = True
    for line in f.readlines():
        if line.startswith(prefix):
            if not first:
                windows.append(window_text.strip())
            else:
                first = False
            window_text = line[len(prefix):]
        else:
            window_text += line

    if window_text:
        windows.append(window_text.strip())

    f.close()
    return windows


def check_samples(samples=()):
    """
      Check script output for all samples. Sample is a two element list, where the first is input and
      the second is output.
    """
    for sample in samples:
        if len(sample) == 2:
            output = get_file_output(arg_string=str(sample[0]))
            if "\n".join(output) != sample[1]:
                failed(
                    "Test from samples failed: \n \n"
                    "Input:\n{}"
                    "\n \n"
                    "Expected:\n{}"
                    "\n \n"
                    "Your result:\n{}".format(str.strip(sample[0]), str.strip(sample[1]), "\n".join(output)))
                return
        set_congratulation_message("All test from samples passed. Now we are checking your solution on Stepik server.")

    passed()


def run_common_tests(error_text="Please, reload file and try again"):
    test_is_not_empty()
    test_answer_placeholders_text_deleted()
    test_file_importable()
