#!/usr/bin/env python
#
# Copyright (C) 2015 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.


import pytest
import json
import os
import tempfile

from gns3.registry.appliance import Appliance, ApplianceError
from gns3.registry.registry import Registry


@pytest.fixture
def registry(images_dir):
    return Registry([os.path.join(images_dir, "QEMU")])


@pytest.fixture
def microcore_appliance(registry):
    """
    An instance of microcore Appliance object
    """
    return Appliance(registry, os.path.abspath("tests/registry/appliances/microcore-linux.gns3a"))


def test_check_config(tmpdir, registry):

    test_path = str(tmpdir / "test.gns3a")

    with open(test_path, "w+", encoding="utf-8") as f:
        f.write("")

    with pytest.raises(ApplianceError):
        Appliance(registry, "jkhj")

    with pytest.raises(ApplianceError):
        Appliance(registry, test_path)

    with open(test_path, "w+", encoding="utf-8") as f:
        f.write("{}")

    with pytest.raises(ApplianceError):
        Appliance(registry, test_path)

    with pytest.raises(ApplianceError):
        with open(test_path, "w+", encoding="utf-8") as f:
            f.write('{"registry_version": 42}')
        Appliance(registry, test_path)

    # Will raise an error due to JSON schema invalid
    with open("tests/registry/appliances/microcore-linux.gns3a", encoding="utf-8") as f:
        config = json.load(f)
        config["images"][0]["md5sum"] = "broken"
        with open(test_path, "w+", encoding="utf-8") as f:
            json.dump(config, f)
    with pytest.raises(ApplianceError):
        Appliance(registry, test_path)

    Appliance(registry, os.path.abspath("tests/registry/appliances/microcore-linux.gns3a"))


def test_resolve_version(tmpdir):

    with open("tests/registry/appliances/microcore-linux.gns3a", encoding="utf-8") as f:
        config = json.load(f)

    hda = config["images"][0]

    new_config = Appliance(registry, os.path.abspath("tests/registry/appliances/microcore-linux.gns3a"))
    assert new_config["versions"][0]["images"] == {"hda_disk_image": hda}


def test_resolve_docker(tmpdir):

    with open("tests/registry/appliances/openvswitch.gns3a", encoding="utf-8") as f:
        config = json.load(f)

    new_config = Appliance(registry, os.path.abspath("tests/registry/appliances/openvswitch.gns3a"))


def test_resolve_version_dynamips(tmpdir):

    with open("tests/registry/appliances/cisco-3745.gns3a", encoding="utf-8") as f:
        config = json.load(f)

    hda = config["images"][0]
    hda["idlepc"] = "0x60aa1da0"

    new_config = Appliance(registry, os.path.abspath("tests/registry/appliances/cisco-3745.gns3a"))
    assert new_config["versions"][0]["images"] == {"image": hda}


def test_resolve_version_invalid_file(tmpdir):

    with pytest.raises(ApplianceError):
        Appliance(registry, "tests/registry/appliances/broken-microcore-linux.gns3a")


def test_resolve_version_ova(tmpdir):

    with open("tests/registry/appliances/juniper-vsrx.gns3a", encoding="utf-8") as f:
        config = json.load(f)

    hda = config["images"][0]
    hda["filename"] = os.path.join("junos-vsrx-12.1X47-D10.4-domestic.ova", "junos-vsrx-12.1X47-D10.4-domestic-disk1.vmdk")

    new_config = Appliance(registry, os.path.abspath("tests/registry/appliances/juniper-vsrx.gns3a"))

    assert new_config["versions"][0]["images"] == {
        "hda_disk_image": hda
    }


def test_search_images_for_version(linux_microcore_img, microcore_appliance):

    detected = microcore_appliance.search_images_for_version("3.4.1")
    assert detected["name"] == "Micro Core Linux 3.4.1"
    assert detected["images"][0]["type"] == "hda_disk_image"
    assert detected["images"][0]["path"] == linux_microcore_img
    assert detected["images"][0]["md5sum"] == "5d41402abc4b2a76b9719d911017c592"
    assert detected["images"][0]["filesize"] == 5


def test_search_images_for_version_no_md5(linux_microcore_img, microcore_appliance):

    microcore_appliance._appliance['versions'][0]['images']['hda_disk_image'].pop('filesize')
    microcore_appliance._appliance['versions'][0]['images']['hda_disk_image'].pop('md5sum')
    detected = microcore_appliance.search_images_for_version("3.4.1")
    assert detected["name"] == "Micro Core Linux 3.4.1"
    assert detected["images"][0]["type"] == "hda_disk_image"
    assert detected["images"][0]["path"] == linux_microcore_img
    assert detected["images"][0]["md5sum"] == "5d41402abc4b2a76b9719d911017c592"
    assert detected["images"][0]["filesize"] == 5


def test_search_images_for_version_unknow_version(microcore_appliance):

    with pytest.raises(ApplianceError):
        microcore_appliance.search_images_for_version("42")


def test_search_images_for_version_missing_file(microcore_appliance):

    with pytest.raises(ApplianceError):
        microcore_appliance.search_images_for_version("4.0.2")


def test_is_version_installable(linux_microcore_img, microcore_appliance):

    assert microcore_appliance.is_version_installable("3.4.1")
    assert not microcore_appliance.is_version_installable("4.0.2")


def test_create_new_version():
    appliance_path = os.path.join(
        os.path.dirname(__file__), "appliances", "microcore-linux.gns3a")

    a = Appliance(registry, appliance_path)
    new_version = {'images': {'hda_disk_image': {'filename': 'linux-microcore-42.0.img', 'version': '42.0'}}, 'name': '42.0'}
    a.create_new_version(new_version)
    v = a['versions'][-1:][0]
    assert v == {
        'images':
        {
            'hda_disk_image':
                {
                    'filename': 'linux-microcore-42.0.img',
                    'version': '42.0'
                }
        },
        'name': '42.0'
    }

    # tests what happens without versions in file
    wrong_appliance_fp, wrong_appliance_file = tempfile.mkstemp()

    with open(appliance_path, encoding='utf-8') as f:
        appliance = json.loads(f.read())
        del appliance['versions']
        os.write(wrong_appliance_fp, json.dumps(appliance).encode())
        os.close(wrong_appliance_fp)

    a = Appliance(registry, wrong_appliance_file)
    with pytest.raises(ApplianceError):
        a.create_new_version("42.0")

    os.remove(wrong_appliance_file)


def test_template_type():
    assert Appliance(registry, os.path.abspath("tests/registry/appliances/microcore-linux.gns3a")).template_type() == "qemu"
    assert Appliance(registry, os.path.abspath("tests/registry/appliances/cisco-iou-l3.gns3a")).template_type() == "iou"


def test_checksum_in_appliance_format_v8(registry, images_dir):

    path_empty_8g = os.path.join(images_dir, "QEMU", "empty8G.qcow2")
    with open(path_empty_8g, 'w+') as f:
        f.write("hello")

    appliance = Appliance(registry, os.path.abspath("tests/registry/appliances/empty-vm-v8.gns3a"))
    appliance._appliance['versions'][0]['images']['hda_disk_image']['filesize'] = 5
    appliance._appliance['versions'][0]['images']['hda_disk_image']['md5sum'] = "5d41402abc4b2a76b9719d911017c592"

    detected = appliance.search_images_for_version("8G")
    assert detected["images"][0]["md5sum"] == "5d41402abc4b2a76b9719d911017c592"
    assert detected["images"][0]["filesize"] == 5

    path_empty_30g = os.path.join(images_dir, "QEMU", "empty30G.qcow2")
    os.rename(path_empty_8g, path_empty_30g)
    appliance._appliance['versions'][1]['images']['hda_disk_image']['filesize'] = 5
    appliance._appliance['versions'][1]['images']['hda_disk_image']['checksum'] = "5d41402abc4b2a76b9719d911017c592"
    detected = appliance.search_images_for_version("30G")
    assert detected["images"][0]["md5sum"] == "5d41402abc4b2a76b9719d911017c592"
    assert detected["images"][0]["filesize"] == 5

    with pytest.raises(ApplianceError):
        appliance.search_images_for_version("100G")


def test_multiple_different_template_types_found():

    appliance_path = "tests/registry/appliances/empty-vm-v8.gns3a"
    wrong_appliance_fp, wrong_appliance_file = tempfile.mkstemp()

    with open(appliance_path, encoding='utf-8') as f:
        appliance = json.loads(f.read())
        appliance["settings"][0]["template_type"] = "dynamips"
        os.write(wrong_appliance_fp, json.dumps(appliance).encode())
        os.close(wrong_appliance_fp)

    with pytest.raises(ApplianceError):
        Appliance(registry, wrong_appliance_file).template_type()


def test_appliance_without_settings():

    appliance_path = "tests/registry/appliances/empty-vm-v8.gns3a"
    wrong_appliance_fp, wrong_appliance_file = tempfile.mkstemp()

    with open(appliance_path, encoding='utf-8') as f:
        appliance = json.loads(f.read())
        del appliance["settings"]
        os.write(wrong_appliance_fp, json.dumps(appliance).encode())
        os.close(wrong_appliance_fp)

    with pytest.raises(ApplianceError):
        Appliance(registry, wrong_appliance_file).template_type()
