""" Tests for exercise nodes, questions, and files """
import os
import pytest
import re
import uuid
import tempfile
from le_utils.constants import licenses, content_kinds, exercises
from ricecooker.classes.nodes import *
from ricecooker.classes.questions import BaseQuestion, PerseusQuestion
from ricecooker.config import STORAGE_DIRECTORY
from test_videos import _clear_ricecookerfilecache

TESTS_DIR = os.path.dirname(os.path.abspath(__file__))
TESTCONTENT_DIR = os.path.join(TESTS_DIR, 'testcontent')


""" *********** EXERCISE FIXTURES *********** """
@pytest.fixture
def exercise_id():
    return "exercise-id"

@pytest.fixture
def channel_internal_domain():
    return "learningequality.org".encode('utf-8')

@pytest.fixture
def topic_node_id():
    return 'some-node-id'

@pytest.fixture
def exercise_content_id(channel_internal_domain, exercise_id):
    return uuid.uuid5(channel_internal_domain, exercise_id)

@pytest.fixture
def exercise_node_id(topic_node_id, exercise_content_id):
    return uuid.uuid5(topic_node_id, exercise_content_id.hex)

@pytest.fixture
def exercise_data(exercise_id):
    return {
    	"title": "exercise node test",
    	"description": None,
        "id" : exercise_id,
        "author": None,
        "license": licenses.PUBLIC_DOMAIN,
    }

@pytest.fixture
def exercise(exercise_data, channel_internal_domain, topic_node_id):
    node = ExerciseNode(
		source_id=exercise_data['id'],
		# description=exercise_data['description'],
		title=exercise_data['title'],
		author=exercise_data['author'],
		license=exercise_data['license'],
	)
    # node.set_ids(channel_internal_domain, topic_node_id)
    return node

@pytest.fixture
def exercise_json(exercise_data, exercise_content_id, exercise_node_id):
    return {
        "id" : exercise_data['id'],
        "title": exercise_data['title'],
        "description": "",
        "node_id": exercise_node_id.hex,
        "content_id": exercise_content_id.hex,
        "author": "",
        "children": [],
        "files": [],
        "kind": exercises.PERSEUS_QUESTION,
        "license": exercise_data['license'],
    }


""" *********** EXERCISE TESTS *********** """
def test_exercise_created(exercise):
    assert exercise is not None

def test_exercise_validate(exercise, exercise_data):
    assert exercise.source_id == exercise_data['id']
    assert exercise.title == exercise_data['title']
    # assert exercise.description == exercise_data['description']
    # assert exercise.author == exercise_data['author']
    # assert exercise.license == exercise_data['license']
    # assert exercise.kind == exercises.PERSEUS_QUESTION
#
# def test_exercise_to_dict(exercise):
#     assert exercise.default_preset == exercises.PERSEUS_QUESTION
#
# def test_exercise_add_question(exercise):
#     assert exercise.default_preset == exercises.PERSEUS_QUESTION
#
# def test_exercise_process_file(exercise):
#     assert exercise.default_preset == exercises.PERSEUS_QUESTION
#
# def test_exercise_process_exercise_data(exercise):
#     assert exercise.default_preset == exercises.PERSEUS_QUESTION


""" *********** BASE64FILE TESTS *********** """
def test_base64_process_file():
    assert True

def test_base64_validate():
    assert True

def test_base64_to_dict():
    assert True

def test_base64_convert_base64_to_file():
    assert True


""" *********** EXERCISEBASE64FILE TESTS *********** """
def test_exercisebase64_process_file():
    assert True

def test_exercisebase64_validate():
    assert True

def test_exercisebase64_to_dict():
    assert True

def test_exercisebase64_get_replacement_str():
    assert True


""" *********** EXERCISEIMAGEFILE TESTS *********** """
def test_exerciseimage_process_file():
    assert True

def test_exerciseimage_validate():
    assert True

def test_exerciseimage_to_dict():
    assert True

def test_exerciseimage_get_replacement_str():
    assert True


""" *********** EXERCISEGRAPHIEFILE TESTS *********** """
def test_exercisegraphie_process_file():
    assert True

def test_exercisegraphie_validate():
    assert True

def test_exercisegraphie_to_dict():
    assert True

def test_exercisegraphie_get_replacement_str():
    assert True

def test_exercisegraphie_generate_graphie_file():
    assert True


""" *********** QUESTION TESTS *********** """
def test_question_to_dict():
    assert True

def test_question_create_answer():
    assert True

def test_question_process_question():
    assert True

def test_question_set_images():
    assert True

def test_question_parse_html():
    assert True

def test_question_set_image():
    assert True

def test_question_validate():
    assert True


""" *********** PERSEUSQUESTION TESTS *********** """
def test_perseusquestion_to_dict():
    assert True

def test_perseusquestion_validate():
    assert True

def test_perseusquestion_process_question():
    assert True

def test_perseusquestion_process_image_field():
    assert True


""" *********** MULTIPLESELECTQUESTION TESTS *********** """
def test_multipleselectquestion_to_dict():
    assert True

def test_multipleselectquestion_validate():
    assert True


""" *********** SINGLESELECTQUESTION TESTS *********** """
def test_singleselectquestion_to_dict():
    assert True

def test_singleselectquestion_validate():
    assert True


""" *********** INPUTQUESTION TESTS *********** """
def test_inputquestion_to_dict():
    assert True

def test_inputquestion_validate():
    assert True




################################################################################
# Perseus image asset processing and image loading tests
################################################################################


# Regex tests
################################################################################

@pytest.fixture
def graphie_strings_and_rawpath():
    """
    Return patterns that should match the 
    WEB_GRAPHIE_URL_REGEX = r'web\+graphie:(?P<rawpath>[^\)]+)'
    """
    test_data = {
        '![](web+graphie:somechunk)': 'somechunk',
        'alksjalksj ![](web+graphie:somechunk)': 'somechunk',
        '![](web+graphie:http://yahoo.com/path/url.png)': 'http://yahoo.com/path/url.png',
        '![graph](web+graphie://ka.s3.aws.com/fefe)': '//ka.s3.aws.com/fefe',
    }
    return test_data

def test_WEB_GRAPHIE_URL_REGEX_matches(graphie_strings_and_rawpath):
    from ricecooker.classes.questions import WEB_GRAPHIE_URL_REGEX
    pat = re.compile(WEB_GRAPHIE_URL_REGEX,  flags=re.IGNORECASE)
    for sample_str, expected_rawpath in graphie_strings_and_rawpath.items():
        m = pat.search(sample_str)
        rawpath = m.groupdict()['rawpath']
        assert m, 'WEB_GRAPHIE_URL_REGEX failed to match string ' + sample_str
        assert rawpath == expected_rawpath, 'found ' + rawpath + ' expected ' + expected_rawpath


@pytest.fixture
def markdown_link_strings_and_match():
    """
    Return patterns that should match the RE for markdown file/image includes:
    MARKDOWN_IMAGE_REGEX = r'!\[([^\]]+)?\]\(([^\)]+)\)'
    """
    test_data = {
        '![smth](path)': ('smth', 'path'),
        'blah ![smth](path) bof': ('smth', 'path'),
        '![smth](http://url.org/path/file.png)': ('smth', 'http://url.org/path/file.png'),
        '![smth](https://url.org/path/file.png)': ('smth', 'https://url.org/path/file.png'),
        '![smth](//url.org/path/file.png)': ('smth', '//url.org/path/file.png'),
        '![smth](web+graphie://ka.s3.aws.com/fefe)': ('smth', 'web+graphie://ka.s3.aws.com/fefe'),
    }
    return test_data

def test_MARKDOWN_IMAGE_REGEX_matches(markdown_link_strings_and_match):
    from ricecooker.classes.questions import MARKDOWN_IMAGE_REGEX
    pat = re.compile(MARKDOWN_IMAGE_REGEX,  flags=re.IGNORECASE)
    for sample_str, expected_matches in markdown_link_strings_and_match.items():
        m = pat.search(sample_str)
        assert m, 'MARKDOWN_IMAGE_REGEX failed to match string ' + sample_str
        assert m.groups() == expected_matches, 'found ' + m.groups() + ' expected ' + expected_matches






## Tests to make sure BaseQuestion.set_image works correctly
################################################################################

@pytest.fixture
def image_texts_fixtures():
    """
    Return texts and corresponding content hashes for various types of image resources.
    """
    WEB_GRAPHIE_PREFIX = 'web+graphie:${☣ CONTENTSTORAGE}/'
    WEB_PREFIX = '${☣ CONTENTSTORAGE}/'
    
    test_data = [
        {
            'text': 'web+graphie://ka-perseus-graphie.s3.amazonaws.com/eb3f3bf7c317408ee90995b5bcf4f3a59606aedd',
            'replacement_str': WEB_GRAPHIE_PREFIX + 'eb3f3bf7c317408ee90995b5bcf4f3a59606aedd',
            'hash': 'ea2269bb5cf487f8d883144b9c06fbc7'
        },
        {
            'text': 'web+graphie://ka-perseus-graphie.s3.amazonaws.com/d8daa074ec7d09ce3819d6259b3e4670701d2540',
            'replacement_str': WEB_GRAPHIE_PREFIX + 'd8daa074ec7d09ce3819d6259b3e4670701d2540',
            'hash': 'db98ca9d35b2fb97cde378a1fabddd26'
        },
        {
            'text': 'https://learningequality.org/static/img/le-logo.svg',
            'replacement_str': WEB_PREFIX + '52b097901664f83e6b7c92ae1af1721b.svg',
            'hash': '52b097901664f83e6b7c92ae1af1721b',
         },
         {
            'text': 'https://learningequality.org/static/img/no-wifi.png',
            'replacement_str': WEB_PREFIX + '599aa896313be22dea6c0257772a464e.png',
            'hash': '599aa896313be22dea6c0257772a464e'
         },
         {  # slightly modified version of the above
            'text': os.path.relpath(os.path.join(TESTCONTENT_DIR, 'no-wifi.png')),
            'replacement_str': WEB_PREFIX + '599aa896313be22dea6c0257772a464e.png',
            'hash': '599aa896313be22dea6c0257772a464e'
         },
    ]
    return test_data


def test_base_question_set_image(image_texts_fixtures):
    """
    Create a test question and check that `set_image` method performs the right image string
    replacement logic.
    """

    for datum in image_texts_fixtures:
        # setup
        _clear_ricecookerfilecache()  # clear file cache each time to avoid test interactions
        text = datum['text']
        replacement_str = datum['replacement_str']

        
        # SIT ##################################################################
        testq = BaseQuestion(id='someid', question='somequestion', question_type='input', raw_data={})
        new_text, images = testq.set_image(text)

        # check 1
        assert new_text == replacement_str, 'Unexpected replacement text produced by set_image'

        # check 2
        assert len(images) == 1, 'Should find exactly one image'

        # check 3
        image_file = images[0]
        filename = image_file.get_filename()
        assert datum['hash'] in filename, 'wront content hash for file'
        # print('filename=', filename)
        if text.startswith('web+graphie:'):
            assert new_text.startswith('web+graphie:'), 'web+graphie: was lost'
            assert filename.endswith('.graphie'), 'wrong extension for web+graphie text'
        expected_storage_dir = os.path.join(STORAGE_DIRECTORY, filename[0], filename[1])
        expected_storage_path = os.path.join(expected_storage_dir, filename)
        assert os.path.exists(expected_storage_path), 'Image file not saved to ricecooker storage dir'


# Test _recursive_url_find method
################################################################################

def test_perseus__recursive_url_find(persues_question_json_fixtures):
    """
    Run _recursive_url_find to check it correctly recognizes and rewrites `url` fields.
    """
    # fixtures
    sample_data_with_backgroundImage_url =  {
        "question": {
            "content": "[[☃ interactive-graph 1]]\n\n",
            "images": {},
            "widgets": {
                "interactive-graph 1": {
                    "type": "interactive-graph",
                    "alignment": "default",
                    "static": False,
                    "graded": True,
                    "options": {
                        "step": [1,1],
                        "backgroundImage": {
                            "url": "https://learningequality.org/static/img/no-wifi.png",
                            "width": 184,
                            "height": 184
                        },
                        "markings": "graph",
                        "labels": ["x","y"],
                    }
                }
            }
        }
    }
    hash = '599aa896313be22dea6c0257772a464e'


    # setup
    image_files = []
    test_data = sample_data_with_backgroundImage_url

    # SIT
    testq = PerseusQuestion(id='someid', raw_data={})
    testq._recursive_url_find(test_data, image_files)

    # checks
    new_url = test_data['question']['widgets']['interactive-graph 1']['options']['backgroundImage']['url']
    assert '☣ CONTENTSTORAGE' in new_url, 'url replacement not done'
    assert hash in new_url, 'wrong url replacement'    
    assert len(image_files) == 1
    image_file = image_files[0]
    filename = image_file.get_filename()
    assert filename is not None, 'missing file'
    assert hash in filename, 'wrong file hash'




# Test PerseusQuestion process_image_field method
################################################################################

@pytest.fixture
def persues_contentimages_field_fixtures():
    """
    Returns a list of data needed to test the `process_image_field` method:
      - `field`: input sample data
      - `new_content`: what the content field should get rewritten to
      - `image_hashes`: content hash of image files that should get downloaded 
    """
    test_data = [
      # Known good test cases from KA English exercise
      {
         'field': { 'content': 'a ![graph](web+graphie://ka-perseus-graphie.s3.amazonaws.com/eb3f3bf7c317408ee90995b5bcf4f3a59606aedd)\nb',
                    'images': { 'web+graphie://ka-perseus-graphie.s3.amazonaws.com/eb3f3bf7c317408ee90995b5bcf4f3a59606aedd': {'width': 425, 'height': 425}},
                  },
          'new_content': 'a ![graph](web+graphie:${☣ CONTENTSTORAGE}/eb3f3bf7c317408ee90995b5bcf4f3a59606aedd)\nb',
          'image_hashes': ['ea2269bb5cf487f8d883144b9c06fbc7'],
      },
      {
          'field': {'content': 'The function $f$\n![graph](web+graphie://ka-perseus-graphie.s3.amazonaws.com/d8daa074ec7d09ce3819d6259b3e4670701d2540)',
                    'images': {'web+graphie://ka-perseus-graphie.s3.amazonaws.com/d8daa074ec7d09ce3819d6259b3e4670701d2540': {'width': 425, 'height': 425}},
                    'widgets': {}
                   },
          'new_content': 'The function $f$\n![graph](web+graphie:${☣ CONTENTSTORAGE}/d8daa074ec7d09ce3819d6259b3e4670701d2540)',
          'image_hashes': ['db98ca9d35b2fb97cde378a1fabddd26'],         
      },
      #
      # Same as above two but with missing images
      {
         'field': { 'content': 'a ![graph](web+graphie://ka-perseus-graphie.s3.amazonaws.com/eb3f3bf7c317408ee90995b5bcf4f3a59606aedd)\nb',
                    'images': {},
                  },
          'new_content': 'a ![graph](web+graphie:${☣ CONTENTSTORAGE}/eb3f3bf7c317408ee90995b5bcf4f3a59606aedd)\nb',
          'image_hashes': ['ea2269bb5cf487f8d883144b9c06fbc7'],
      },
      {
          'field': {'content': 'The function $f$\n![graph](web+graphie://ka-perseus-graphie.s3.amazonaws.com/d8daa074ec7d09ce3819d6259b3e4670701d2540)',
                    'images': {},
                    'widgets': {}
                   },
          'new_content': 'The function $f$\n![graph](web+graphie:${☣ CONTENTSTORAGE}/d8daa074ec7d09ce3819d6259b3e4670701d2540)',
          'image_hashes': ['db98ca9d35b2fb97cde378a1fabddd26'],         
      },
    ]
    return test_data


def test_persues_question_process_image_field(persues_contentimages_field_fixtures):
    """
    Create a PerseusQuestion test object and check that `process_image_field` method works
    correctly for differnet snippets of the form:
        data = {
            "content": "Some string possibly including image URLs like ![smth](URL-key)",
            "images": {
                "URL-key":  {"width": 425, "height": 425},
                "URL-key2": {"width": 425, "height": 425}
            }
        }
    """

    for fixture in persues_contentimages_field_fixtures:
        # setup
        _clear_ricecookerfilecache()  # clear file cache each time to avoid test interactions
        field = fixture['field']
        expected_new_content = fixture['new_content']
        expected_image_hashes = set(fixture['image_hashes'])

        # SIT
        testq = PerseusQuestion(id='x43bbec76d5f14f88_bg', raw_data={})
        new_images, image_files = testq.process_image_field(fixture['field'])

        # check 1
        assert field['content'] == expected_new_content, 'Image URL replacement failed'
        #
        # check 2
        for image_key, image_attrs in new_images.items():
            assert 'http' not in image_key, 'Images URLs not replace with local paths'
        #
        # check 3
        image_hashes = set()
        for image_file in image_files:
            assert image_file is not None, 'image_file should not be None'
            filehash, ext =  os.path.splitext(image_file.get_filename())
            image_hashes.add(filehash)
        assert image_hashes == expected_image_hashes, 'Unexpected image files set'



# Test PerseusQuestion process_question method
################################################################################

@pytest.fixture
def persues_question_json_fixtures():
    """
    Load entire perseus questions
    """
    test_data = []
    with open(os.path.join(TESTCONTENT_DIR, 'perseus_question_x43bbec76d5f14f88_en.json')) as inf:
        # ENGLISH JSON = KNOWN GOOD
        item_data_en = json.load(inf)
        datum = {
            'item': item_data_en,
            'image_hashes': ['ea2269bb5cf487f8d883144b9c06fbc7', 'db98ca9d35b2fb97cde378a1fabddd26']
        }
        test_data.append(datum)
    # Missing images in the KA BULGARIAN channel BUG
    # see https://github.com/learningequality/ricecooker/issues/178
    with open(os.path.join(TESTCONTENT_DIR, 'perseus_question_x43bbec76d5f14f88_bg.json')) as inf:
        
        item_data_bg = json.load(inf)
        datum = {
            'item': item_data_bg,
            'image_hashes': ['ea2269bb5cf487f8d883144b9c06fbc7', 'db98ca9d35b2fb97cde378a1fabddd26']
        }
        test_data.append(datum)

    return test_data


def test_perseus_process_question(persues_question_json_fixtures):
    """
    Process a persues question and check that it finds all images, and returns
    correcrt image files -- i.e not more, not less.
    """
    
    for datum in persues_question_json_fixtures:

        # setup
        perseus_question = datum['item']
        expected_image_hashes = set(datum['image_hashes'])
        _clear_ricecookerfilecache()  # clear file cache each time to avoid test interactions

        # SIT
        testq = PerseusQuestion(id='x43bbec76d5f14f88_en', raw_data=perseus_question)
        filenames = testq.process_question()

        # check 1
        assert len(filenames) == 2, 'wrong number of filenames found'

        # check 2
        image_hashes = set()
        for filename in filenames:
            filehash, ext =  os.path.splitext(filename)
            image_hashes.add(filehash)
        assert image_hashes == expected_image_hashes, 'Unexpected image file set'


# Test exercise images
################################################################################

def test_exercise_image_file(exercise_image_file, exercise_image_filename):
    filename = exercise_image_file.get_filename()
    assert filename == exercise_image_filename, 'wrong filename for _ExerciseImageFile'

def test_exercise_base64_image_file(exercise_base64_image_file, exercise_base64_image_filename):
    filename = exercise_base64_image_file.get_filename()
    assert filename == exercise_base64_image_filename, 'wrong filename for _ExerciseBase64ImageFile'

def test_exercise_graphie_filename(exercise_graphie_file, exercise_graphie_replacement_str, exercise_graphie_filename):
    filename = exercise_graphie_file.get_filename()
    assert filename == exercise_graphie_filename, 'wrong filename for _ExerciseGraphieFile'
    replacement_str = exercise_graphie_file.get_replacement_str()
    assert replacement_str == exercise_graphie_replacement_str, 'wrong replacement string for _ExerciseGraphieFile '
