import abc
import asyncio
import logging
import os
import random

from saas.library.python.deploy_manager_api import SaasService

from saas.tools.devops.canary.reader.core.discovery import SearchProxyDiscovery
from saas.tools.devops.canary.reader.core.state_tracker import ReadStateTracker
from saas.tools.devops.canary.reader.core.tvm import TvmClient


class DocReader(metaclass=abc.ABCMeta):
    class Error:
        DOCUMENT_NOT_FOUND = 'document-not-found'
        UNKNOWN = 'unknown'

    def __init__(
        self,
        service: SaasService,
        sp_discovery: SearchProxyDiscovery,
        state_tracker: ReadStateTracker,
        tvm_client: TvmClient
    ) -> None:
        self._service: SaasService = service
        self._sp_discovery: SearchProxyDiscovery = sp_discovery
        self._state_tracker: ReadStateTracker = state_tracker
        self._tvm_client: TvmClient = tvm_client
        self._hostname: str = os.environ.get('HOSTNAME')

    @abc.abstractmethod
    async def _check_document(self, endpoint: str, text: str) -> None:
        raise NotImplementedError

    async def _get_sp_endpoint(self) -> str:
        endpoints = self._sp_discovery.get_sp_endpoints(ctype=self._service.ctype)
        if not endpoints:
            raise ValueError(
                f'Unable to get a searchproxy endpoint for {self._service.name} {self._service.ctype}'
            )

        return random.choice(endpoints)

    async def check_document(self, text: str):
        endpoint = await self._get_sp_endpoint()
        text = f'{self._hostname}_{text}' if self._hostname else text

        try:
            await self._check_document(endpoint, text)
        except DocumentCheckFailed as e:
            logging.error('Document check failed for #%s, error = %s [%s]', text, e.error, endpoint)
            self._on_document_check_failed()
        except asyncio.CancelledError:
            raise
        except:
            logging.exception('Unexpected error occurred during checking document #%s [%s]', text, endpoint)
            self._on_document_check_failed()
        else:
            self._on_document_check_success()

    def _on_document_check_failed(self) -> None:
        self._state_tracker.on_error()

    def _on_document_check_success(self) -> None:
        self._state_tracker.on_success()


class DocumentCheckFailed(Exception):
    def __init__(self, error: str) -> None:
        self._error: str = error

    @property
    def error(self) -> str:
        return self._error
