#!/usr/bin/env python
# -*- coding: utf8 -*-

# ============================================================================
#  Copyright (c) nexB Inc. http://www.nexb.com/ - All rights reserved.
#  Licensed 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 io
import os
import unittest

from testing_utils import get_test_loc
from testing_utils import get_temp_file

from attributecode import INFO
from attributecode import attrib
from attributecode import gen
from attributecode import model


class TemplateTest(unittest.TestCase):

    def test_check_template_simple_valid_returns_None(self):
        expected = None
        assert expected == attrib.check_template('template_string')

    def test_check_template_complex_valid_returns_None(self):
        template = '''
        {% for about in abouts -%}
            {{ about.name.value }}: {{ about.version.value }}
            {% for res in about.about_resource.value -%}
                resource: {{ res }}
            {% endfor -%}
        {% endfor -%}'''
        expected = None
        assert expected == attrib.check_template(template)

    def test_check_template_complex_invalid_returns_error(self):
        template = '''
        {% for about in abouts -%}
            {{ about.name.value }}: {{ about.version.value }}
            {% for res in about.about_ressdsdsdsdsdsdource.value -%}
                resource: {{] res }}
            {% endfor -%}
        {% endfor -%}'''
        expected = (5, "unexpected ']'")
        assert expected == attrib.check_template(template)

    def test_check_template_invalid_return_error_lineno_and_message(self):
        expected = 1, "unexpected end of template, expected 'end of print statement'."
        assert expected == attrib.check_template('{{template_string')

    def test_check_template_all_builtin_templates_are_valid(self):
        builtin_templates_dir = os.path.dirname(attrib.DEFAULT_TEMPLATE_FILE)
        for template in os.listdir(builtin_templates_dir):
            template_loc = os.path.join(builtin_templates_dir, template)
            with open(template_loc, 'r', encoding='utf-8', errors='replace') as tmpl:
                template = tmpl.read()
            try:
                assert None == attrib.check_template(template)
            except:
                raise Exception(template_loc)


class GenerateTest(unittest.TestCase):

    def test_generate_from_collected_inventory_wih_custom_temaplte(self):
        test_file = get_test_loc('test_attrib/gen_simple/attrib.ABOUT')
        errors, abouts = model.collect_inventory(test_file)
        assert not errors

        test_template = get_test_loc('test_attrib/gen_simple/test.template')
        with open(test_template) as tmpl:
            template = tmpl.read()

        expected = (
            'Apache HTTP Server: 2.4.3\n'
            'resource: httpd-2.4.3.tar.gz\n')

        license_dict = {}
        is_about_input = True
        min_license_score=0
        scancode = False
        error, result = attrib.generate(abouts, is_about_input, license_dict, scancode, min_license_score, template=template)
        assert expected == result
        assert not error

    def test_generate_with_default_template(self):
        test_file = get_test_loc('test_attrib/gen_default_template/attrib.ABOUT')
        errors, abouts = model.collect_inventory(test_file)
        assert not errors

        license_dict = {}
        is_about_input = True
        min_license_score=0
        scancode = False

        error, result = attrib.generate_from_file(abouts, is_about_input, license_dict, scancode, min_license_score)
        assert not error

        expected_file = get_test_loc(
            'test_attrib/gen_default_template/expected_default_attrib.html')
        with open(expected_file) as exp:
            expected = exp.read()

        # strip the timestamp: the timestamp is wrapped in italic block
        result = remove_timestamp(result)
        expected = remove_timestamp(expected)
        # Ignore all white spaces and newline
        result = result.replace('\n', '').replace(' ', '')
        expected = expected.replace('\n', '').replace(' ', '')
        assert expected == result

    def test_lic_key_name_sync(self):
        test_file = get_test_loc('test_attrib/gen_license_key_name_check/test.ABOUT')
        expected = get_test_loc('test_attrib/gen_license_key_name_check/expected/expected.html')
        template_loc = get_test_loc('test_attrib/gen_license_key_name_check/custom.template')
        output_file = get_temp_file()

        license_dict = {}
        is_about_input = True

        errors, abouts = model.collect_inventory(test_file)
        attrib.generate_and_save(abouts, is_about_input, license_dict, output_file, template_loc=template_loc)

        with open(output_file) as of:
            f1 = '\n'.join(of.readlines(False))
        with open(expected) as ef:
            f2 = '\n'.join(ef.readlines(False))

        assert f1 == f2

    def test_scancode_input(self):
        test_file = get_test_loc('test_attrib/scancode_input/clean-text-0.3.0-mod-lceupi.json')
        errors, abouts = gen.load_inventory(test_file, scancode=True)
        # No validation is done for the scancode input as it usually contains duplicated entry of
        # detected licenses which is not allow in the current spec.
        #expected_errors = [(40, 'Field about_resource: Unable to verify path: isc_lic.py: No base directory provided')]
        result = [(level, e) for level, e in errors if level > INFO]
        #assert expected_errors == result
        assert result == []

        lic_dict = {'isc': ['ISC License',
                            'isc.LICENSE',
                            'Permission to use, copy, modify, and/or distribute this software for any purpose\nwith or without fee is hereby granted, provided that the above copyright notice\nand this permission notice appear in all copies.\n\nTHE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND\nFITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS\nOF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER\nTORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF\nTHIS SOFTWARE.\n',
                            'https://scancode-licensedb.aboutcode.org/isc.LICENSE'],
                    'mit': ['MIT License',
                            'mit.LICENSE',
                            'Permission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n"Software"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.',
                            'https://scancode-licensedb.aboutcode.org/mit.LICENSE']}
        is_about_input = False
        scancode = True
        errors, result = attrib.generate_from_file(abouts, is_about_input, lic_dict, scancode, min_license_score=0)
        expected_errors = []
        #result = [(level, e) for level, e in errors if level > INFO]
        #assert expected_errors == result
        assert not errors

        expected_file = get_test_loc(
            'test_attrib/scancode_input/expect.html')
        with open(expected_file) as exp:
            expected = exp.read()

        # strip the timestamp: the timestamp is wrapped in italic block
        result = remove_timestamp(result)
        expected = remove_timestamp(expected)
        # For whatever reasons, the directly comparison between the result and the
        # expected doesn't work well, it works after removed all the newline and spaces
        #assert expected == result
        #assert expected.splitlines(False) == result.splitlines(False)
        assert expected.replace('\n','').replace(' ','').replace('\t','') == result.replace('\n','').replace(' ','').replace('\t','')

    def test_generate_with_csv(self):
        test_file = get_test_loc('test_attrib/default_template/simple_sample.csv')
        errors, abouts = gen.load_inventory(test_file)

        lic_dict = {'isc': ['ISC License',
                            'isc.LICENSE',
                            'Permission to use, copy, modify, and/or distribute this software for any purpose\nwith or without fee is hereby granted, provided that the above copyright notice\nand this permission notice appear in all copies.\n\nTHE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND\nFITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS\nOF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER\nTORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF\nTHIS SOFTWARE.\n',
                            'https://scancode-licensedb.aboutcode.org/isc.LICENSE']}
        is_about_input = False
        scancode = False

        error, result = attrib.generate_from_file(abouts, is_about_input, lic_dict, scancode, min_license_score=0)
        assert not error

        expected_file = get_test_loc(
            'test_attrib/default_template/expect.html')
        with open(expected_file) as exp:
            expected = exp.read()

        # strip the timestamp: the timestamp is wrapped in italic block
        result = remove_timestamp(result)
        expected = remove_timestamp(expected)
        #assert expected == result
        assert expected.replace('\n','').replace(' ','') == result.replace('\n','').replace(' ','')

def remove_timestamp(html_text):
    """
    Return the `html_text` generated attribution stripped from timestamps: the
    timestamp is wrapped in italic block in the default template.
    """
    return '\n'.join(x for x in html_text.splitlines() if not '<i>' in x)
