import re
from abc import ABC, abstractmethod
from typing import List, Union

from pyutils import exc
from pyutils.proc.bench import Benchmark
from pyutils.proc.task import Task

from evowluator.data.ontology import Syntax
from evowluator.reasoner.java import JavaReasoner
from evowluator.reasoner.base import MetaArgs, OutputFormat, Reasoner, ReasoningTask
from evowluator.reasoner.results import ReasoningResults, ResultsParser
from evowluator.evaluation.mode import EvaluationMode


class TinyME(Reasoner, ABC):
    """TinyME reasoner template."""

    @classmethod
    def is_template(cls) -> bool:
        return cls == TinyME

    @property
    @abstractmethod
    def name(self) -> str:
        pass

    @property
    @abstractmethod
    def path(self) -> str:
        pass

    @property
    def classification_output_format(self) -> OutputFormat:
        return OutputFormat.TEXT

    @property
    def supported_tasks(self) -> List[ReasoningTask]:
        return ReasoningTask.all()

    @property
    def results_parser(self) -> ResultsParser:
        return TinyMEResultsParser()

    def args(self, task: ReasoningTask, mode: EvaluationMode) -> List[str]:
        return _get_args(task=task, mode=mode)


class TinyMEC(TinyME):

    @property
    def name(self) -> str:
        return 'Tiny-ME C'

    @property
    def path(self) -> str:
        return 'TinyMEC/tinyme'

    @property
    def supported_syntaxes(self) -> List[Syntax]:
        return [Syntax.FUNCTIONAL]


class TinyMEObjC(TinyME):

    @property
    def name(self) -> str:
        return 'Tiny-ME ObjC'

    @property
    def path(self) -> str:
        return 'TinyMEObjC/tinyme'

    @property
    def supported_syntaxes(self) -> List[Syntax]:
        return [Syntax.FUNCTIONAL]


class TinyMEJava(JavaReasoner):

    @property
    def name(self) -> str:
        return 'Tiny-ME Java'

    @property
    def path(self) -> str:
        return 'TinyMEJava/tinyme-jre-1.0.0.jar'

    @property
    def supported_tasks(self) -> List[ReasoningTask]:
        return ReasoningTask.all()

    @property
    def supported_syntaxes(self) -> List[Syntax]:
        return Syntax.all()

    @property
    def classification_output_format(self) -> OutputFormat:
        return OutputFormat.TEXT

    @property
    def results_parser(self) -> ResultsParser:
        return TinyMEResultsParser()

    @property
    def vm_opts(self):
        return ['-Xmx16g', '-DentityExpansionLimit=1000000000']

    def args(self, task: ReasoningTask, mode: EvaluationMode) -> List[str]:
        return _get_args(task=task, mode=mode)


# Private


def _get_args(task: ReasoningTask, mode: EvaluationMode) -> List[str]:
    """'args' method implementation for Tiny-ME."""
    if task == ReasoningTask.CLASSIFICATION:
        args = ['classification', '-i', MetaArgs.INPUT]
    elif task == ReasoningTask.CONSISTENCY:
        args = ['coherence', '-i', MetaArgs.INPUT]
    else:
        args = ['abduction-contraction', '-i', MetaArgs.INPUT, '-r', MetaArgs.REQUEST]

    if task != ReasoningTask.CONSISTENCY:
        if mode == EvaluationMode.CORRECTNESS:
            args.extend(['-o', MetaArgs.OUTPUT])
        else:
            args.append('-q')

    args.append('-b')

    return args


class TinyMEResultsParser(ResultsParser):
    """Parser for Tiny-ME results."""

    def parse_consistency_results(self, task: Union[Task, Benchmark]) -> ReasoningResults:
        results = self._parse_reasoning_stats(task)

        result = re.search(r'The ontology is (.*)\.', task.stdout)
        exc.raise_if_falsy(result=result)
        output = 'consistent' if result.group(1) == 'coherent' else 'not consistent'

        return results.with_output(output, is_file=False)
