"""Process descriptor."""
import collections
import re

# Processor type validation regexp.
PROCESSOR_TYPE_RE = re.compile(r'^data:[a-z0-9:]+$')


class SchedulingClass:
    """Process scheduling class."""

    INTERACTIVE = 'interactive'
    BATCH = 'batch'


class ValidationError(Exception):
    """Process descriptor validation error."""

    pass


class ProcessDescriptor:
    """Process descriptor.

    The process descriptor is a result of parsing a process definition. It contains
    everything that is required for registering the process into the database.
    """

    class Metadata:
        """General process metadata."""

        slug = None
        name = None
        process_type = None
        description = None
        version = None
        category = None
        scheduling_class = None
        requirements = None
        data_name = None
        entity = None

        def __repr__(self):
            """Return string representation."""
            return repr(self.__dict__)

    def __init__(self, source=None):
        """Construct a new process descriptor."""
        self.metadata = ProcessDescriptor.Metadata()
        self.inputs = collections.OrderedDict()
        self.outputs = collections.OrderedDict()
        self.source = source

    def validate(self):
        """Validate process descriptor."""
        required_fields = ('slug', 'name', 'process_type', 'version')
        for field in required_fields:
            if getattr(self.metadata, field, None) is None:
                raise ValidationError("process '{}' is missing required meta attribute: {}".format(
                    self.metadata.slug or '<unknown>', field))

        if not PROCESSOR_TYPE_RE.match(self.metadata.process_type):
            raise ValidationError("process '{}' has invalid type: {}".format(
                self.metadata.slug, self.metadata.process_type))

    def to_schema(self):
        """Return process schema for this process."""
        process_type = self.metadata.process_type
        if not process_type.endswith(':'):
            process_type = '{}:'.format(process_type)

        schema = {
            'slug': self.metadata.slug,
            'name': self.metadata.name,
            'type': process_type,
            'version': self.metadata.version,
            'data_name': '',
            'requirements': {
                'executor': {
                    'docker': {
                        'image': 'resolwe/base:ubuntu-18.10',
                    },
                },
            },
        }

        if self.metadata.description is not None:
            schema['description'] = self.metadata.description
        if self.metadata.category is not None:
            schema['category'] = self.metadata.category
        if self.metadata.scheduling_class is not None:
            schema['scheduling_class'] = self.metadata.scheduling_class
        if self.metadata.requirements is not None:
            schema['requirements'] = self.metadata.requirements
        if self.metadata.data_name is not None:
            schema['data_name'] = self.metadata.data_name
        if self.metadata.entity is not None:
            schema['entity'] = self.metadata.entity

        if self.inputs:
            schema['input'] = []
            for field in self.inputs.values():
                schema['input'].append(field.to_schema())

        if self.outputs:
            schema['output'] = []
            for field in self.outputs.values():
                schema['output'].append(field.to_schema())

        schema['run'] = {
            'language': 'python',
            'program': self.source or '',
        }

        return schema

    def __repr__(self):
        """Return string representation."""
        return '<ProcessDescriptor metadata={} inputs={} outputs={}>'.format(
            repr(self.metadata),
            repr(dict(self.inputs)),
            repr(dict(self.outputs)),
        )
