"""
    QuaO Project device.py Copyright © CITYNOW Co. Ltd. All rights reserved.
"""
import json
from abc import abstractmethod, ABC

from src.quao.data.job.job_response import JobResponse
from src.quao.enum.job_status import JobStatus
from src.quao.enum.media_type import MediaType
from src.quao.model.provider.provider import Provider


class Device(ABC):
    def __init__(self, provider: Provider, device_specification: str):
        self.provider = provider
        self.device = provider.get_backend(device_specification)

    def run_circuit(self, circuit, shots: int) -> JobResponse:
        """

        @param circuit:
        @param shots:
        """

        provider_job_id = ''
        content_type = None
        job_histogram = None

        try:
            job = self._create_job(circuit=circuit, shots=shots)
            provider_job_id = self._get_provider_job_id(job)
            job_status = self._get_job_status(job)
            job_result_dictionary = {}

            if self._is_simulator() or JobStatus.DONE.value.__eq__(job_status):
                job_result = job.result()
                job_result_dictionary = self._parse_job_result(job_result)

                content_type = MediaType.APPLICATION_JSON.value

                job_histogram = self._produce_histogram_data(job_result)

            statevector_devices = ['statevector_simulator']

            if statevector_devices.__contains__(self.device.name):
                job_result_dictionary = json.dumps(job_result_dictionary, default=str)

        except Exception as exception:
            job_result_dictionary = {
                "error": "Exception when invoke job on device: " + self.device.name,
                "exception": str(exception)
            }
            job_status = JobStatus.ERROR.value

        return JobResponse(
            provider_job_id=provider_job_id,
            job_status=job_status,
            job_result=job_result_dictionary,
            content_type=content_type,
            job_histogram=job_histogram
        )

    @abstractmethod
    def _create_job(self, circuit, shots):
        """

        @param circuit:
        @param shots:
        """
        pass

    @abstractmethod
    def _is_simulator(self) -> bool:
        """

        """
        pass

    @abstractmethod
    def _produce_histogram_data(self, job_result) -> dict:
        """

        @param job_result:
        """
        pass

    @abstractmethod
    def _get_provider_job_id(self, job) -> str:
        """

        """
        pass

    @abstractmethod
    def _get_job_status(self, job) -> str:
        """

        """
        pass

    @abstractmethod
    def _parse_job_result(self, job_result) -> dict:
        """

        """
        pass
