import logging

from django.conf import settings
from django.contrib.postgres.fields import JSONField
from django.db import models
from django.utils.functional import cached_property

from db.models.jobs import Job, JobStatus
from libs.spec_validation import validate_build_spec_config
from polyaxon_schemas.run_exec import BuildConfig

logger = logging.getLogger('db.build_jobs')


class BuildJob(Job):
    """A model that represents the configuration for build job."""
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='+')
    project = models.ForeignKey(
        'db.Project',
        on_delete=models.CASCADE,
        related_name='build_jobs')
    config = JSONField(
        help_text='The compiled polyaxonfile for plugin job.',
        validators=[validate_build_spec_config])
    code_reference = models.ForeignKey(
        'db.CodeReference',
        on_delete=models.SET_NULL,
        blank=True,
        null=True,
        related_name='+')
    job_status = models.OneToOneField(
        'db.BuildJobStatus',
        related_name='+',
        blank=True,
        null=True,
        editable=True,
        on_delete=models.SET_NULL)

    class Meta:
        app_label = 'db'

    def __str__(self):
        return '{} build<{}, ref({})>'.format(self.project, self.image, self.code_reference)

    def save(self, *args, **kwargs):  # pylint:disable=arguments-differ
        if self.pk is None:
            last = BuildJob.objects.filter(project=self.project).last()
            self.sequence = 1
            if last:
                self.sequence = last.sequence + 1

        super(BuildJob, self).save(*args, **kwargs)

    @property
    def unique_name(self):
        return '{}.builds.{}'.format(self.project.unique_name, self.sequence)

    @cached_property
    def specification(self):
        return BuildConfig.from_dict(self.config)

    @cached_property
    def image(self):
        return self.specification.image

    @cached_property
    def build_steps(self):
        return self.specification.build_steps

    @cached_property
    def resources(self):
        return None

    @cached_property
    def node_selectors(self):
        return None

    @cached_property
    def env_vars(self):
        return self.specification.env_vars

    def set_status(self, status, message=None, details=None):  # pylint:disable=arguments-differ
        return self._set_status(status_model=BuildJobStatus,
                                logger=logger,
                                status=status,
                                message=message,
                                details=details)


class BuildJobStatus(JobStatus):
    """A model that represents build job status at certain time."""
    job = models.ForeignKey(
        'db.BuildJob',
        on_delete=models.CASCADE,
        related_name='statuses')

    class Meta(JobStatus.Meta):
        app_label = 'db'
        verbose_name_plural = 'Build Job Statuses'

@@ -175,16 +175,91 @@
 property
+%0Afrom polyaxon_schemas.polyaxonfile.specification import BuildSpecification
 %0A%0Afrom d
@@ -357,58 +357,8 @@
 nfig
-%0Afrom polyaxon_schemas.run_exec import BuildConfig
 %0A%0Alo
@@ -1913,14 +1913,21 @@
 uild
-Config
+Specification
 .fro
@@ -2023,16 +2023,22 @@
 ication.
+build.
 image%0A%0A 
@@ -2123,21 +2123,122 @@
 on.build
-_step
+.build_steps%0A%0A    @cached_property%0A    def env_vars(self):%0A        return self.specification.build.env_var
 s%0A%0A    @
@@ -2811,16 +2811,659 @@
 tails)%0A%0A
+    @staticmethod%0A    def create(user, project, config, code_reference):%0A        build_config = BuildSpecification.create_specification(config)%0A        try:%0A            job = BuildJob.objects.get(project=project,%0A                                       config=config,%0A                                       code_reference=code_reference)%0A        except BuildJob.DoesNotExist:%0A            job = BuildJob.objects.create(user=user,%0A                                          project=project,%0A                                          config=build_config,%0A                                          code_reference=code_reference)%0A        return job%0A%0A
 %0Aclass B

Add logic to check or create new build jobs