# Copyright (c) 2021 Huawei Technologies Co.,Ltd. All rights reserved.
#
# StratoVirt is licensed under Mulan PSL v2.
# You can use this software according to the terms and conditions of the Mulan
# PSL v2.
# You may obtain a copy of Mulan PSL v2 at:
#         http:#license.coscl.org.cn/MulanPSL2
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
# KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
# NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
# See the Mulan PSL v2 for more details.
"""conftest"""

import os
import uuid
import shutil
import tempfile
import time
import platform
from subprocess import run
import pytest
from virt.container import KataContainer
from virt.microvm import MicroVM
from virt.standvm import StandVM
from monitor.monitor_thread import MonitorThread
from utils.config import CONFIG

TIMESTAMP = time.strftime('%Y%m%d_%H%M%S', time.localtime(time.time()))
SESSION_PATH = os.path.join(CONFIG.test_dir, TIMESTAMP)
CONFIG.test_session_root_path = SESSION_PATH
if not os.path.exists(CONFIG.test_session_root_path):
    os.makedirs(CONFIG.test_session_root_path)

@pytest.fixture(autouse=True, scope='session')
def test_session_root_path():
    """Create a new test path in each session"""
    created_test_session_root_path = False

    delete_test_session = CONFIG.delete_test_session
    monitor_thread = MonitorThread()
    monitor_thread.start()
    if os.path.exists(CONFIG.stratovirt_rootfs):
        _cmd = "cp %s %s.bak" % (CONFIG.stratovirt_rootfs, CONFIG.stratovirt_rootfs)
        run(_cmd, shell=True, check=True)
    if os.path.exists(CONFIG.stratovirt_stand_rootfs):
        _cmd = "cp %s %s.bak" % (CONFIG.stratovirt_stand_rootfs, CONFIG.stratovirt_stand_rootfs)
        run(_cmd, shell=True, check=True)

    yield CONFIG.test_session_root_path

    if os.path.exists(CONFIG.stratovirt_rootfs):
        _cmd = "cp %s.bak %s" % (CONFIG.stratovirt_rootfs, CONFIG.stratovirt_rootfs)
        run(_cmd, shell=True, check=True)
    if os.path.exists(CONFIG.stratovirt_stand_rootfs):
        _cmd = "cp %s.bak %s" % (CONFIG.stratovirt_stand_rootfs, CONFIG.stratovirt_stand_rootfs)
        run(_cmd, shell=True, check=True)
    monitor_thread.stop()
    monitor_thread.join()
    if delete_test_session and created_test_session_root_path:
        shutil.rmtree(CONFIG.test_session_root_path)


@pytest.fixture
def test_session_tmp_path(test_session_root_path):
    """Generate a temporary directory on Setup. Remove on teardown."""
    # pylint: disable=redefined-outer-name
    # The pytest.fixture triggers a pylint rule.

    temp_path = tempfile.mkdtemp(prefix=test_session_root_path)
    yield temp_path
    shutil.rmtree(temp_path)


def init_microvm(root_path, bin_path=CONFIG.stratovirt_microvm_bin, **kwargs):
    """Auxiliary to init a microvm"""
    vm_uuid = str(uuid.uuid4())
    testvm = MicroVM(root_path, "microvm", vm_uuid,
                     bin_path=bin_path,
                     **kwargs
                     )

    return testvm

def init_standvm(root_path, bin_path=CONFIG.stratovirt_standvm_bin, **kwargs):
    """Auxiliary to init a standardvm"""
    vm_uuid = str(uuid.uuid4())
    if "aarch" in platform.machine():
        testvm = StandVM(root_path, "standvm", vm_uuid,
                         bin_path=bin_path, machine="virt",
                         **kwargs
                         )
    else:
        testvm = StandVM(root_path, "standvm", vm_uuid,
                         bin_path=bin_path, machine="q35",
                         **kwargs
                         )

    return testvm

def init_microvm_with_json(root_path, vm_config_json, vmtag):
    """Init a microvm from a json file"""
    vm_uuid = str(uuid.uuid4())
    vmname = "microvm" + "_" + vmtag
    testvm = MicroVM(root_path, vmname, vm_uuid, bin_path=CONFIG.stratovirt_microvm_bin,
                     vmconfig=vm_config_json)
    return testvm

def init_standvm_with_json(root_path, vm_config_json, vmtag):
    """Init a standvm from a json file"""
    vm_uuid = str(uuid.uuid4())
    vmname = "standvm" + "_" + vmtag
    if "aarch" in platform.platform():
        testvm = StandVM(root_path, vmname, vm_uuid, bin_path=CONFIG.stratovirt_standvm_bin,
                         machine="virt", vmconfig=vm_config_json)
    else:
        testvm = StandVM(root_path, vmname, vm_uuid, bin_path=CONFIG.stratovirt_standvm_bin,
                         machine="q35", vmconfig=vm_config_json)
    return testvm


def _gcc_compile(src_file, output_file):
    """Build a source file with gcc."""
    compile_cmd = 'gcc {} -o {} -O3'.format(
        src_file,
        output_file
    )
    run(
        compile_cmd,
        shell=True,
        check=True
    )


@pytest.fixture()
def nc_vsock_path(test_session_root_path):
    """Wget nc-vsock.c and build a nc-vsock app."""
    # pylint: disable=redefined-outer-name
    # The pytest.fixture triggers a pylint rule.
    path = os.path.realpath(os.path.dirname(__file__))
    nc_path = "{}/{}".format(
        path,
        "nc-vsock.c"
    )
    if not os.path.exists(nc_path):
        run(
            "wget https://gitee.com/EulerRobot/nc-vsock/raw/master/nc-vsock.c -O %s"
            % nc_path,
            shell=True,
            check=True
        )
    nc_vsock_bin_path = os.path.join(
        test_session_root_path,
        'nc-vsock'
    )
    _gcc_compile(
        'nc-vsock.c',
        nc_vsock_bin_path
    )
    yield nc_vsock_bin_path

@pytest.fixture(autouse=True, scope='session')
def container():
    """Instantiate a container"""
    # pylint: disable=redefined-outer-name
    # The pytest.fixture triggers a pylint rule.
    kata_container = KataContainer()
    backup_cmd = "cp %s/configuration.toml %s/configuration.toml-bak" % \
                (CONFIG.kata_config_path, CONFIG.kata_config_path)
    run(backup_cmd, shell=True, check=True)

    cmd = "cp %s/configuration-hydropper.toml %s/configuration.toml" % \
         (CONFIG.kata_config_path, CONFIG.kata_config_path)
    run(cmd, shell=True, check=True)

    yield kata_container

    resume_cmd = "cp %s/configuration.toml-bak %s/configuration.toml" % \
                (CONFIG.kata_config_path, CONFIG.kata_config_path)
    run(resume_cmd, shell=True, check=True)

@pytest.fixture()
def microvm(test_session_root_path):
    """Instantiate a microvm"""
    # pylint: disable=redefined-outer-name
    # The pytest.fixture triggers a pylint rule.
    testvm = init_microvm(test_session_root_path)
    yield testvm
    testvm.kill()


@pytest.fixture()
def standvm(test_session_root_path):
    """Instantiate a standardvm"""
    # pylint: disable=redefined-outer-name
    # The pytest.fixture triggers a pylint rule.
    testvm = init_standvm(test_session_root_path)
    yield testvm
    testvm.kill()


@pytest.fixture()
def microvm_with_tcp(test_session_root_path):
    """Init a microvm"""
    # pylint: disable=redefined-outer-name
    # The pytest.fixture triggers a pylint rule.
    testvm = init_microvm(test_session_root_path, socktype='tcp')
    yield testvm
    testvm.kill()


@pytest.fixture()
def microvms(test_session_root_path):
    """Init multi microvms"""
    # pylint: disable=redefined-outer-name
    # The pytest.fixture triggers a pylint rule.
    micro_vms = []
    for index in range(CONFIG.concurrent_quantity):
        tempvm = init_microvm_with_json(test_session_root_path,
                                        CONFIG.get_microvm_by_tag('initrd'),
                                        "initrd%d" % index)
        micro_vms.append(tempvm)

    yield micro_vms
    for tempvm in micro_vms:
        tempvm.kill()


@pytest.fixture()
def directvm(request):
    """Get vm fixture value"""
    return request.getfixturevalue(request.param)


TEST_MICROVM_CAP_FIXTURE_TEMPLATE = (
    "@pytest.fixture()\n"
    "def test_microvm_with_CAP(test_session_root_path):\n"
    "    microvm = init_microvm_with_json(test_session_root_path,\n"
    "                                     CONFIG.get_microvm_by_tag(\"CAP\"), \"CAP\")\n"
    "    yield microvm\n"
    "    microvm.kill()"
)

for capability in CONFIG.list_microvm_tags():
    test_microvm_cap_fixture = (
        TEST_MICROVM_CAP_FIXTURE_TEMPLATE.replace('CAP', capability)
    )

    exec(test_microvm_cap_fixture)

TEST_STANDVM_CAP_FIXTURE_TEMPLATE = (
    "@pytest.fixture()\n"
    "def test_standvm_with_CAP(test_session_root_path):\n"
    "    standvm = init_standvm_with_json(test_session_root_path,\n"
    "                                     CONFIG.get_standvm_by_tag(\"CAP\"), \"CAP\")\n"
    "    yield standvm\n"
    "    standvm.kill()"
)

for capability in CONFIG.list_standvm_tags():
    test_standvm_cap_fixture = (
        TEST_STANDVM_CAP_FIXTURE_TEMPLATE.replace('CAP', capability)
    )

    exec(test_standvm_cap_fixture)