# Copyright 2019 Red Hat, Inc.
#
# This work is licensed under the GNU GPLv2 or later.
# See the COPYING file in the top-level directory.

import os
import random
import re
import string
import tempfile

from ..logger import log
from .. import progress
from . import urlfetcher


class _CloudInitConfig:
    """
    Helper class to create cloud-init configuration file.

    @destfile: Name of the cloud-init configuration file to be included
        in cloud-init ISO file.
    @content: Function to create the content of @destfile. It should return str
        to create the configuration file or None to not create the file.
    @config: Path to configuration file user provided to virt-install, if provided
        @content is ignored.
    @scratchdir: Directory where to place temporary files.
    """

    def __init__(self, destfile, content, config, scratchdir):
        self._destfile = destfile
        self._content = content
        self._config = config
        self._scratchdir = scratchdir

    def _create_file(self):
        content = self._content()

        if content is None:
            return None

        fileobj = tempfile.NamedTemporaryFile(
            prefix="virtinst-", suffix=f"-{self._destfile}", dir=self._scratchdir, delete=False
        )
        with open(fileobj.name, "w+") as f:
            f.write(content)
        return fileobj.name

    def _fetch_file(self):
        log.debug("Using '%s' content from path='%s'", self._destfile, self._config)
        meter = progress.make_meter(quiet=True)
        fetcher = urlfetcher.DirectFetcher(None, self._scratchdir, meter)
        return fetcher.acquireFile(self._config)

    def add_filepair(self, filepairs):
        if self._config:
            file = self._fetch_file()
        else:
            file = self._create_file()

        if not file:
            return

        filepairs.append((file, self._destfile))


class CloudInitData:
    def __init__(self):
        self.disable = None
        self.root_password_generate = None
        self.root_password_file = None
        self.generated_root_password = None
        self.root_ssh_key = None
        self.clouduser_ssh_key = None
        self.user_data = None
        self.meta_data = None
        self.network_config = None

    def _generate_password(self):
        if not self.generated_root_password:
            self.generated_root_password = ""
            for dummy in range(16):
                self.generated_root_password += random.choice(string.ascii_letters + string.digits)
        return self.generated_root_password

    def _get_password(self, pwdfile):
        with open(pwdfile, "r") as fobj:
            return fobj.readline().rstrip("\n\r")

    def get_password_if_generated(self):
        if self.root_password_generate:
            return self._generate_password()

    def get_root_password(self):
        if self.root_password_file:
            return self._get_password(self.root_password_file)
        return self.get_password_if_generated()

    def get_root_ssh_key(self):
        if self.root_ssh_key:
            return self._get_password(self.root_ssh_key)

    def get_clouduser_ssh_key(self):
        if self.clouduser_ssh_key:
            return self._get_password(self.clouduser_ssh_key)

    def _create_metadata_content(self):
        return ""

    def _create_userdata_content(self):
        content = "#cloud-config\n"

        if self.root_password_generate or self.root_password_file:
            rootpass = self.get_root_password()
            content += "chpasswd:\n"
            content += "  list: |\n"
            content += "    root:%s\n" % rootpass

        if self.root_password_generate:
            content += "  expire: True\n"
        elif self.root_password_file:
            content += "  expire: False\n"

        if self.root_ssh_key:
            rootkey = self.get_root_ssh_key()
            content += "users:\n"
            content += "  - default\n"
            content += "  - name: root\n"
            content += "    ssh_authorized_keys:\n"
            content += "      - %s\n" % rootkey

        if self.clouduser_ssh_key:
            userkey = self.get_clouduser_ssh_key()
            content += "ssh_authorized_keys:\n"
            content += "  - %s\n" % userkey

        if self.disable:
            content += "runcmd:\n"
            content += '- echo "Disabled by virt-install" > /etc/cloud/cloud-init.disabled\n'

        clean_content = re.sub(r"root:(.*)", "root:[SCRUBBLED]", content)
        if "VIRTINST_TEST_SUITE_PRINT_CLOUDINIT" in os.environ:
            print(clean_content)

        log.debug("Generated cloud-init userdata: \n%s", clean_content)
        return content

    def _create_network_config_content(self):
        return None

    def create_files(self, scratchdir):
        meta_data = _CloudInitConfig(
            "meta-data", self._create_metadata_content, self.meta_data, scratchdir
        )
        user_data = _CloudInitConfig(
            "user-data", self._create_userdata_content, self.user_data, scratchdir
        )
        network_config = _CloudInitConfig(
            "network-config", self._create_network_config_content, self.network_config, scratchdir
        )

        filepairs = []
        try:
            meta_data.add_filepair(filepairs)
            user_data.add_filepair(filepairs)
            network_config.add_filepair(filepairs)
        except Exception:  # pragma: no cover
            for filepair in filepairs:
                os.unlink(filepair[0])
            raise

        return filepairs
