import logging.config
import os
from functools import cached_property
from typing import Optional

import yaml
import yandextank.common.util as yandextank
from enum import Enum, auto

from load.projects.cloud.tank_client.ammo import Ammo
from load.projects.cloud.tank_client.client import TankApiClient, AUTOSTOP_EXIT_CODES
from load.projects.cloud.tank_client.utils import Generator

LOGGER = logging.getLogger('job')


class AdditionalJobStatus(Enum):
    STATUS_UNSPECIFIED = 0
    STOPPED = 1
    FAILED = 2
    AUTOSTOPPED = 3


FINISHED_STATUSES = (
    str(yandextank.Status.TEST_FINISHED.decode('utf-8')),
    AdditionalJobStatus.STOPPED.name,
    AdditionalJobStatus.FAILED.name,
    AdditionalJobStatus.AUTOSTOPPED.name,
)


class _Status:
    def __init__(self, job_status_response):
        self.name, self.error, self.error_type = self.parse(job_status_response)

    @staticmethod
    def parse(job_status_response):
        error, error_type = TankApiClient.extract_error(job_status_response)
        if not error:
            if job_status_response.get('exit_code') in AUTOSTOP_EXIT_CODES:
                job_status = AdditionalJobStatus.AUTOSTOPPED.name
            else:
                job_status = job_status_response.get('status_code', AdditionalJobStatus.FAILED.name)
        else:
            job_status = AdditionalJobStatus.FAILED.name
        return job_status, error, error_type

    @property
    def finished(self):
        return self.name in FINISHED_STATUSES

    def add_error(self, error):
        if self.error:
            self.error = '; '.join([self.error, error])
        else:
            self.error = error


class Job:
    class Origin(Enum):
        LT_SERVER = auto()
        LOCAL = auto()

    def __init__(self, id_: str, origin: Origin, ammo: Ammo = None, log_group_id: str = None,
                 tank_job_id: str = None, job_config: dict = None, is_running=False):
        self.id = id_
        self.ammo = ammo
        self.log_group_id = log_group_id
        self.tank_job_id = tank_job_id
        self.origin = origin
        self.status: Optional[_Status] = None
        self.stop_signal_received = False
        self._job_config = job_config
        self.is_running = is_running

    def __repr__(self):
        return f'Job({self.id=}, {self.tank_job_id=}, {self.log_group_id=})'

    @cached_property
    def generator(self):
        return self._generator(self.job_config)

    @staticmethod
    def _generator(config):
        if config.get('pandora', {}).get('enabled', False):
            return Generator.PANDORA
        if config.get('phantom', {}).get('enabled', False):
            return Generator.PHANTOM
        LOGGER.error(f"Neither pandora nor phantom are enabled in the config: {config}")
        return Generator.UNKNOWN

    @property
    def test_dir(self):
        assert self.tank_job_id
        # TODO: tests directory should be gotten somehow from tankapi..
        return os.path.join('/var/lib/tankapi/tests/', self.tank_job_id)

    @staticmethod
    def _get_config(test_dir):
        try:
            with open(os.path.join(test_dir, 'configinitial.yaml'), 'r') as f:
                return yaml.safe_load(f)
        except (PermissionError, FileNotFoundError):
            return {}

    @property
    def job_config(self):
        if self._job_config:
            return self._job_config
        return self._get_config(self.test_dir)

    def update_status(self, status_response):
        self.status = _Status(status_response)
        if self.stop_signal_received:
            if self.status.name == str(yandextank.Status.TEST_FINISHED.decode('utf-8')):
                self.status.name = AdditionalJobStatus.STOPPED.name
            else:
                self.status.add_error('stopped job is still in progress')
