# passerelle - uniform access to multiple data sources and services
# Copyright (C) 2019 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.


import io
import json
import uuid
import zipfile

import pytest

from passerelle.utils.zip import (
    ZipTemplate,
    ZipTemplateDoesNotExist,
    ZipTemplateError,
    ZipTemplateSyntaxError,
)


@pytest.fixture
def templates_path(tmpdir, settings):
    path = tmpdir.mkdir('templates')
    settings.TEMPLATES = settings.TEMPLATES[:]
    settings.TEMPLATES[0] = settings.TEMPLATES[0].copy()
    settings.TEMPLATES[0].setdefault('DIRS', [])
    settings.TEMPLATES[0]['DIRS'].insert(0, str(path))
    zip_templates_path = path.mkdir('zip_templates')
    return zip_templates_path


@pytest.fixture
def tpl_builder(templates_path):
    def make(name_template, template_parts=(), content_parts=()):
        manifest_name = '%s.json' % uuid.uuid4().hex
        manifest_path = templates_path / manifest_name
        d = {
            'name_template': name_template,
        }
        if template_parts or content_parts:
            d['part_templates'] = []
            for name_template, content in template_parts:
                name = '%s.xml' % uuid.uuid4().hex
                with (templates_path / name).open('w') as fd:
                    fd.write(content)
                d['part_templates'].append(
                    {
                        'name_template': name_template,
                        'template_path': name,
                    }
                )
            for name_template, content in content_parts:
                d['part_templates'].append(
                    {
                        'name_template': name_template,
                        'content_expression': content,
                    }
                )
        with manifest_path.open('w') as fd:
            json.dump(d, fd)
        return '%s/%s' % (templates_path.basename, manifest_name)

    return make


@pytest.fixture
def dest(tmpdir):
    return tmpdir.mkdir('dest')


def test_missing():
    with pytest.raises(ZipTemplateDoesNotExist):
        ZipTemplate('zip_templates/manifest1.json')


def test_invalid(templates_path):
    path = templates_path / 'invalid-manifest.json'
    with path.open(mode='w') as fd:
        fd.write('{')
    with pytest.raises(ZipTemplateError) as exc_info:
        ZipTemplate(str(path))
    assert 'invalid' in exc_info.value.args[0]

    with path.open(mode='w') as fd:
        fd.write('{}')
    with pytest.raises(ZipTemplateError):
        ZipTemplate(str(path))
    assert 'invalid' in exc_info.value.args[0]


def test_syntax_error(tpl_builder, dest):
    zip_template = ZipTemplate(
        tpl_builder('{{ name -{{ counter }}.zip'), ctx={'name': 'coucou', 'counter': 10}
    )
    with pytest.raises(ZipTemplateSyntaxError):
        zip_template.render_to_path(dest)

    zip_template = ZipTemplate(
        tpl_builder('{{ name }}-{{ counter }}.zip', template_parts=[('part1.xml', '{{ name {{ }}')]),
        ctx={'name': 'coucou', 'counter': 10},
    )
    with pytest.raises(ZipTemplateSyntaxError):
        zip_template.render_to_path(dest)


def test_no_parts(tpl_builder, dest):
    z = ZipTemplate(tpl_builder('{{ name }}-{{ counter }}.zip'), ctx={'name': 'coucou', 'counter': 10})
    z.render_to_path(dest)

    full_path = dest / 'coucou-10.zip'
    with full_path.open('rb') as fd:
        with zipfile.ZipFile(fd) as zi:
            assert zi.namelist() == []


def test_with_parts(tpl_builder, dest):
    z = ZipTemplate(
        tpl_builder(
            '{{ name }}-{{ counter }}.zip',
            template_parts=[
                ('{{ name }}-{{ counter }}-part1.xml', '<?xml version="1.0"?><body>{{ bo_dy|lower }}</body>')
            ],
            content_parts=[('{{ name }}-{{ counter }}-dôc.xml', 'doc-content')],
        ),
        ctx={'name': 'coucou', 'counter': 10, 'bo_dy': 'blabla', 'doc-content': '<a>Héllo World!</a>'},
    )
    z.render_to_path(dest)
    for part in z.parts:
        str(part)

    full_path = dest / 'coucou-10.zip'
    with full_path.open('rb') as fd:
        with zipfile.ZipFile(fd) as zi:
            assert zi.namelist() == ['coucou-10-part1.xml', 'coucou-10-dôc.xml']
            with zi.open('coucou-10-part1.xml') as zfd:
                assert zfd.read().decode('utf-8') == '<?xml version="1.0"?><body>blabla</body>'
            with zi.open('coucou-10-dôc.xml') as zfd:
                assert zfd.read().decode('utf-8') == '<a>Héllo World!</a>'

    with io.BytesIO(z.render_to_bytes()) as fd:
        with zipfile.ZipFile(fd) as zi:
            assert zi.namelist() == ['coucou-10-part1.xml', 'coucou-10-dôc.xml']
            with zi.open('coucou-10-part1.xml') as zfd:
                assert zfd.read().decode('utf-8') == '<?xml version="1.0"?><body>blabla</body>'
            with zi.open('coucou-10-dôc.xml') as zfd:
                assert zfd.read().decode('utf-8') == '<a>Héllo World!</a>'


def test_xml_error(tpl_builder, dest):
    z = ZipTemplate(
        tpl_builder(
            'rien.zip',
            content_parts=[('rien.xml', 'doc-content')],
        ),
        ctx={'doc-content': '<a>Héllo World!'},
    )
    with pytest.raises(ZipTemplateSyntaxError) as exc_info:
        z.render_to_bytes()
    assert 'XML syntax error' in exc_info.value.args[0]
