from __future__ import unicode_literals
from __future__ import print_function
from __future__ import division
from __future__ import absolute_import
from future import standard_library
standard_library.install_aliases()

from pkg_resources import resource_filename
import shutil
import os
import logging

from kubernetes import client, config

from .models.build import Build, BuildSpec, BuildSpecArgument, BuildSpecTemplate
from .models.build_template import BuildTemplate, BuildTemplateSpec, BuildTemplateSpecParameter, BuildTemplateSpecStep
from fairing.builders import BuilderInterface, dockerfile
from fairing import utils

logger = logging.getLogger(__name__)
DEFAULT_IMAGE_NAME = 'fairing-job'


class KnativeBuilder(BuilderInterface):
    def __init__(self,
                 repository,
                 image_name=DEFAULT_IMAGE_NAME,
                 image_tag=None,
                 base_image=None,
                 dockerfile_path=None):

        self.repository = repository
        self.image_name = image_name
        self.base_image = base_image
        self.dockerfile_path = dockerfile_path
        self.namespace = self.get_current_namespace()

        if image_tag is None:
            self.image_tag = utils.get_unique_tag()
        else:
            self.image_tag = image_tag
        
        # Unique build_id to avoid conflicts
        self._build_id = utils.get_unique_tag()

    def execute(self):
        dockerfile.write_dockerfile(
            dockerfile_path=self.dockerfile_path,
            base_image=self.base_image
        )
        self.copy_src_to_mount_point()
        self.build_and_push()


    def generate_pod_spec(self):
        """return a V1PodSpec initialized with the proper container"""
        return client.V1PodSpec(
            containers=[client.V1Container(
                name='model',
                image=utils.get_image_full_name(
                    self.repository, 
                    self.image_name, 
                    self.image_tag
                ),
            )],
            restart_policy='Never'
        )

    def copy_src_to_mount_point(self):
        context_dir = os.getcwdu()
        dst = os.path.join(self.get_mount_point(), self._build_id)
        shutil.copytree(context_dir, dst)

    def get_mount_point(self):
        return os.path.join(os.environ['HOME'], '.fairing/build-contexts/')

    def build_and_push(self):
        logger.warn(
            'Building docker image {repository}/{image}:{tag}...'.format(
                repository=self.repository,
                image=self.image_name,
                tag=self.image_tag
            )
        )
        self.authenticate()

        build_template = self.generate_build_template_resource()
        build_template.maybe_create()

        build = self.generate_build_resource()
        build.create_sync()
        # TODO: clean build?

    def authenticate(self):
        if utils.is_running_in_k8s():
            config.load_incluster_config()
        else:
            config.load_kube_config()

    def get_current_namespace(self):
        if not utils.is_running_in_k8s():
            logger.debug("""Fairing does not seem to be running inside  
                Kubernetes, cannot infer namespace. Using namespaces 'fairing'
                instead""")
            return 'fairing'
        return utils.get_current_k8s_namespace()

    def generate_build_template_resource(self):
        metadata = client.V1ObjectMeta(
            name='fairing-build', namespace=self.namespace)

        params = [BuildTemplateSpecParameter(name='IMAGE', description='The name of the image to push'),
                  BuildTemplateSpecParameter(name='TAG',
                                             description='The tag of the image to push',
                                             default='latest'),
                  BuildTemplateSpecParameter(name='DOCKERFILE',
                                             description='Path to the Dockerfile to build',
                                             default='/src/Dockerfile')]
        volume_mount = client.V1VolumeMount(
            name='src', mount_path='/src')
        steps = [BuildTemplateSpecStep(name='build-and-push',
                                       image='gcr.io/kaniko-project/executor:v0.5.0',
                                       args=['--dockerfile=${DOCKERFILE}',
                                             '--destination=${IMAGE}:${TAG}',
                                             '--context=${CONTEXT}'],
                                       volume_mounts=[volume_mount])]

        pvc = client.V1PersistentVolumeClaimVolumeSource(
            claim_name='fairing-build')
        volume = client.V1Volume(
            name='src', persistent_volume_claim=pvc)
        spec = BuildTemplateSpec(
            parameters=params, steps=steps, volumes=[volume])
        return BuildTemplate(metadata=metadata, spec=spec)

    def generate_build_resource(self):
        metadata = client.V1ObjectMeta(
            name='fairing-build-{}'.format(self.image_name),
            namespace=self.namespace)

        mount_path = '/src/{build_id}'.format(build_id=self._build_id)

        args = [BuildSpecArgument(name='IMAGE', value=utils.get_image(
                self.repository,
                self.image_name)),
            BuildSpecArgument(name='TAG', value=self.image_tag),
            BuildSpecArgument(name='DOCKERFILE', value=os.path.join(
                mount_path, 'Dockerfile')),
            BuildSpecArgument(name='CONTEXT', value=mount_path)]

        template = BuildSpecTemplate(name='fairing-build', arguments=args)
        spec = BuildSpec(sa_name='fairing-build', template=template)
        return Build(metadata=metadata, spec=spec)
