from load.projects.tasklets.regression.proto import regression_tasklet
from ci.tasklet.common.proto import service_pb2 as ci
from tasklet.services.yav.proto import yav_pb2
from sandbox.common.rest import Client
from retry import retry

import sandbox.common.types.task as ctt
import sandbox.common.patterns as patterns
import logging
import time


GET_STATUS_TIMEOUT = 10
WAIT_STATUS_TIMEOUT = 5 * 60

BREAK_STATUSES = (
    ctt.Status.EXCEPTION,
    ctt.Status.NO_RES,
    ctt.Status.TIMEOUT,
    ctt.Status.STOPPED,
    ctt.Status.EXPIRED,
    ctt.Status.NOT_RELEASED,
    ctt.Status.FAILURE,
)

SUCCESS_STATUSES = (
    ctt.Status.SUCCESS,
    ctt.Status.RELEASED
)


SANDBOX = 'https://sandbox.yandex-team.ru'
TASK_TYPE = 'SHOOTING_REGRESSION'


class RegressionError(Exception):
    pass


class RegressionImpl(regression_tasklet.RegressionBase):
    def run(self):
        if not self.input.shooting.shooting_id:
            raise RegressionError("Wrong input parameters!")

        task_id = self.start_task(
            TASK_TYPE,
            self.input.sandbox.owner,
            {'shooting_id': self.input.shooting.shooting_id},
            self.input.sandbox.description or f'check regression for the shooting {self.input.shooting.shooting_id}',
        )

        progress_msg = ci.TaskletProgress()
        progress_msg.job_instance_id.CopyFrom(self.input.context.job_instance_id)
        progress_msg.id = 'ShootingRegression'
        progress_msg.module = 'SANDBOX'
        progress_msg.url = f'{SANDBOX}/task/{task_id}/view'
        progress_msg.status = ci.TaskletProgress.Status.RUNNING
        self.ctx.ci.UpdateProgress(progress_msg)

        startime = time.time()
        while time.time() - startime <= WAIT_STATUS_TIMEOUT:
            task_status = self.get_task_status(task_id)
            if task_status in SUCCESS_STATUSES:
                break
            elif task_status in BREAK_STATUSES:
                raise RegressionError("Regression sandbox task failed")

        progress_msg.progress = 1
        progress_msg.status = ci.TaskletProgress.Status.SUCCESSFUL
        self.ctx.ci.UpdateProgress(progress_msg)

        output = self.get_task_output(task_id)
        if output and output.get('status', False):
            progress_msg.module = 'LUNAPARK'
            progress_msg.url = output.get('regression')
            self.ctx.ci.UpdateProgress(progress_msg)
        else:
            raise RegressionError("Regression sandbox task has no status in the output parameters")

    @patterns.singleton_property
    def sandbox_client(self):
        spec = yav_pb2.YavSecretSpec(uuid=self.input.context.secret_uid, key=self.input.sandbox.token)
        return Client(auth=self.ctx.yav.get_secret(spec).secret)

    @retry(tries=3)
    def start_task(self, task_type, owner, params, description='regression'):
        task = self.sandbox_client.task.create(
            {
                'type': task_type,
                'description': description,
                'owner': owner,
                'custom_fields': [{'name': k, 'value': v} for k, v in params.items()]
            }
        )
        self.sandbox_client.batch.tasks.start.update([task['id']])
        return task['id']

    def get_task_output(self, task_id):
        try:
            return {output['name']: output.get('value', None) for output in self.sandbox_client.task[task_id].output.read()}
        except Exception:
            logging.exception('Wrong output from sandbox client')

    @retry(tries=3)
    def get_task_status(self, task_id):
        status = self.sandbox_client.task[task_id].read()['status']
        logging.info(f'Task {task_id} has status {status}')
        return status
