# -*- coding: utf-8 -*-

#    Copyright 2014 Mirantis, Inc.
#
#    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.

from __future__ import unicode_literals

import abc
import logging
import os

from os.path import join as join_path

from fuel_plugin_builder.actions import BaseAction
from fuel_plugin_builder import errors
from fuel_plugin_builder import utils
from fuel_plugin_builder.validators import ValidatorManager
from fuel_plugin_builder import version_mapping


logger = logging.getLogger(__name__)


class BaseBuildPlugin(BaseAction):

    release_tmpl_src_path = None

    @abc.abstractproperty
    def requires(self):
        """Should return a list of commands which
        are required for the builder
        """

    @abc.abstractproperty
    def result_package_mask(self):
        """Should return mask for built package
        """

    @abc.abstractmethod
    def make_package(self):
        """Method should be implemented in child classes
        """

    def __init__(self, plugin_path):
        self.plugin_path = plugin_path

        self.pre_build_hook_cmd = './pre_build_hook'
        self.meta = utils.parse_yaml(
            join_path(self.plugin_path, 'metadata.yaml')
        )
        self.build_dir = join_path(self.plugin_path, '.build')
        self.build_src_dir = join_path(self.build_dir, 'src')
        self.checksums_path = join_path(self.build_src_dir, 'checksums.sha1')
        self.name = self.meta['name']

        self.plugin_version, self.full_version = utils.version_split_name_rpm(
            self.meta['version'])

    def run(self):
        logger.debug('Start plugin building "%s"', self.plugin_path)
        self.clean()
        self.run_pre_build_hook()
        self.check()
        self.build_repos()
        self.add_checksums_file()
        self.make_package()

    def clean(self):
        utils.remove(self.build_dir)
        utils.create_dir(self.build_dir)
        utils.remove_by_mask(self.result_package_mask)

    def run_pre_build_hook(self):
        if utils.which(join_path(self.plugin_path, self.pre_build_hook_cmd)):
            utils.exec_cmd(self.pre_build_hook_cmd, self.plugin_path)

    def add_checksums_file(self):
        utils.create_checksums_file(self.build_src_dir, self.checksums_path)

    def build_repos(self):
        utils.create_dir(self.build_src_dir)

        utils.copy_files_in_dir(
            join_path(self.plugin_path, '*'),
            self.build_src_dir)

        releases_paths = {}
        for release in self.meta['releases']:
            releases_paths.setdefault(release['os'], [])
            releases_paths[release['os']].append(
                join_path(self.build_src_dir, release['repository_path']))

        self.build_ubuntu_repos(releases_paths.get('ubuntu', []))
        self.build_centos_repos(releases_paths.get('centos', []))

    def build_ubuntu_repos(self, releases_paths):
        for repo_path in releases_paths:
            utils.exec_piped_cmds(
                ['dpkg-scanpackages -m .', 'gzip -c9 > Packages.gz'],
                cwd=repo_path)
            if self.release_tmpl_src_path:
                release_path = join_path(repo_path, 'Release')
                utils.render_to_file(
                    self.release_tmpl_src_path,
                    release_path,
                    {'plugin_name': self.meta['name'],
                     'major_version': self.plugin_version})

    @classmethod
    def build_centos_repos(cls, releases_paths):
        for repo_path in releases_paths:
            repo_packages = join_path(repo_path, 'Packages')
            utils.create_dir(repo_packages)
            utils.move_files_in_dir(
                join_path(repo_path, '*.rpm'),
                repo_packages)
            utils.exec_cmd('createrepo -o {0} {0}'.format(repo_path))

    def check(self):
        self._check_requirements()
        self._check_structure()

    def _check_requirements(self):
        not_found = filter(lambda r: not utils.which(r), self.requires)

        if not_found:
            raise errors.FuelCannotFindCommandError(
                'Cannot find commands "{0}", '
                'install required commands and try again'.format(
                    ', '.join(not_found)))

    def _check_structure(self):
        ValidatorManager(self.plugin_path).get_validator().validate()


class BuildPluginV1(BaseBuildPlugin):

    requires = ['rpm', 'createrepo', 'dpkg-scanpackages']
    release_tmpl_src_path = os.path.abspath(join_path(
        os.path.dirname(__file__),
        '..',
        'templates/v1/build/Release.mako'))

    @property
    def result_package_mask(self):
        return join_path(self.plugin_path, '{0}-*.fp'.format(self.name))

    def make_package(self):
        full_name = '{0}-{1}'.format(self.meta['name'],
                                     self.meta['version'])
        tar_name = '{0}.fp'.format(full_name)
        tar_path = join_path(
            self.plugin_path,
            tar_name)

        utils.make_tar_gz(self.build_src_dir, tar_path, full_name)


class BuildPluginV2(BuildPluginV1):

    requires = ['rpmbuild', 'rpm', 'createrepo', 'dpkg-scanpackages']

    rpm_spec_src_path = 'templates/v2/build/plugin_rpm.spec.mako'

    def __init__(self, *args, **kwargs):
        super(BuildPluginV2, self).__init__(*args, **kwargs)

        self.rpm_path = os.path.abspath(
            join_path(self.plugin_path, '.build', 'rpm'))

        self.rpm_src_path = join_path(self.rpm_path, 'SOURCES')
        self.full_name = '{0}-{1}'.format(
            self.meta['name'], self.plugin_version)

        tar_name = '{0}.fp'.format(self.full_name)
        self.tar_path = join_path(self.rpm_src_path, tar_name)

        fpb_dir = join_path(os.path.dirname(__file__), '..')

        self.spec_src = os.path.abspath(join_path(
            fpb_dir, self.rpm_spec_src_path))

        self.spec_dst = join_path(self.rpm_path, 'plugin_rpm.spec')

        self.rpm_packages_mask = join_path(
            self.rpm_path, 'RPMS', 'noarch', '*.rpm')

    @property
    def result_package_mask(self):
        return join_path(
            self.plugin_path, '{0}-*.noarch.rpm'.format(self.name))

    def make_package(self):
        """Builds rpm package
        """
        utils.create_dir(self.rpm_src_path)

        utils.make_tar_gz(self.build_src_dir, self.tar_path, self.full_name)
        utils.render_to_file(
            self.spec_src,
            self.spec_dst,
            self._make_data_for_template())

        utils.exec_cmd(
            'rpmbuild -vv --nodeps --define "_topdir {0}" '
            '-bb {1}'.format(self.rpm_path, self.spec_dst))
        utils.copy_files_in_dir(self.rpm_packages_mask, self.plugin_path)

    def _make_data_for_template(self):
        """Generates data for spec template

        :returns: dictionary with required data
        """
        return {
            'name': self.full_name,
            'version': self.full_version,
            'summary': self.meta['title'],
            'description': self.meta['description'],
            'license': ' and '.join(self.meta.get('licenses', [])),
            'homepage': self.meta.get('homepage'),
            'vendor': ', '.join(self.meta.get('authors', [])),
            'year': utils.get_current_year()}


class BuildPluginV3(BuildPluginV2):

    rpm_spec_src_path = 'templates/v3/build/plugin_rpm.spec.mako'

    def _make_data_for_template(self):
        data = super(BuildPluginV3, self)._make_data_for_template()

        uninst = utils.read_if_exist(
            join_path(self.plugin_path, "uninstall.sh"))

        preinst = utils.read_if_exist(
            join_path(self.plugin_path, "pre_install.sh"))

        postinst = utils.read_if_exist(
            join_path(self.plugin_path, "post_install.sh"))

        plugin_build_version = str(self.meta.get('build_version', '1'))

        data.update(
            {'postinstall_hook': postinst,
             'preinstall_hook': preinst,
             'uninstall_hook': uninst,
             'build_version': plugin_build_version}
        )

        return data


class BuildPluginV4(BuildPluginV3):
    pass


class BuildPluginV5(BuildPluginV4):
    pass


def make_builder(plugin_path):
    """Creates build object.

    :param str plugin_path: path to the plugin
    :returns: specific version of builder object
    """
    builder = version_mapping.get_version_mapping_from_plugin(
        plugin_path)['builder']

    return builder(plugin_path)
