# Copyright (C) 2018-2019 Apple Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1.  Redistributions of source code must retain the above copyright
#     notice, this list of conditions and the following disclaimer.
# 2.  Redistributions in binary form must reproduce the above copyright
#     notice, this list of conditions and the following disclaimer in the
#     documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

from __future__ import unicode_literals

import logging

from django.db import models
from ews.config import ERR_UNEXPECTED, SUCCESS
from ews.models.build import Build
from ews.models.buildbotinstance import BuildbotInstance
import ews.common.util as util

_log = logging.getLogger(__name__)


class Step(models.Model):
    uid = models.TextField(primary_key=True)
    build_uid = models.ForeignKey(Build, on_delete=models.CASCADE, db_column='build_uid', db_constraint=False)
    result = models.IntegerField(null=True, blank=True)
    state_string = models.TextField()
    started_at = models.IntegerField(null=True, blank=True)
    complete_at = models.IntegerField(null=True, blank=True)
    created = models.DateTimeField(auto_now_add=True)
    modified = models.DateTimeField(auto_now=True)

    def __str__(self):
        return str(self.uid)

    @classmethod
    def save_step(cls, hostname, step_id, build_id, result, state_string, started_at, complete_at=None):
        if not Step.is_valid_result(step_id, build_id, result, state_string, started_at, complete_at):
            return ERR_UNEXPECTED

        if state_string is None:
            state_string = ''

        step_uid = BuildbotInstance.get_uid(hostname, step_id)
        build_uid = BuildbotInstance.get_uid(hostname, build_id)

        step = Step.get_existing_step(step_uid)
        if step:
            # If the step data is already present in database, update it, e.g.: step complete event.
            return Step.update_step(step, step_uid, build_uid, result, state_string, started_at, complete_at)

        # Save the new step data, e.g.: step start event.
        Step(step_uid, build_uid, result, state_string, started_at, complete_at).save()
        _log.debug('Saved step {} in database for build: {}'.format(step_uid, build_uid))
        return SUCCESS

    @classmethod
    def update_step(cls, step, step_uid, build_uid, result, state_string, started_at, complete_at):
        if step.uid != step_uid:
            _log.error('step_uid {} does not match with step_uid {}. Ignoring new data.'.format(step.uid, step_uid))
            return ERR_UNEXPECTED
        step.result = result
        step.state_string = state_string
        step.started_at = started_at
        step.complete_at = complete_at
        step.save(update_fields=['result', 'state_string', 'started_at', 'complete_at', 'modified'])
        _log.debug('Updated step {} in database for build: {}'.format(step_uid, build_uid))
        return SUCCESS

    @classmethod
    def get_existing_step(cls, uid):
        try:
            return Step.objects.get(uid=uid)
        except Step.DoesNotExist:
            return None

    @classmethod
    def is_valid_result(cls, step_id, build_id, result, state_string, started_at, complete_at):
        if not (util.is_valid_id(step_id) and util.is_valid_id(build_id)):
            return False

        return True
