# -*- coding: utf-8 -*-
import six
from syncano.exceptions import SyncanoValueError
from .classes import Class
from .incentives import Script, ScriptEndpoint
[docs]class CallType(object):
    """
    The type of the call object used in the custom socket;
    """
    SCRIPT = 'script'
 
[docs]class DependencyType(object):
    """
    The type of the dependency object used in the custom socket;
    """
    SCRIPT = 'script'
    CLASS = 'class'
 
[docs]class BaseCall(object):
    """
    Base class for call object.
    """
    call_type = None
    def __init__(self, name, methods):
        self.name = name
        self.methods = methods
[docs]    def to_dict(self):
        if self.call_type is None:
            raise SyncanoValueError('call_type not set.')
        return {
            'type': self.call_type,
            'name': self.name,
            'methods': self.methods
        }
  
[docs]class ScriptCall(BaseCall):
    """
    Script call object.
    The JSON format is as follows (to_dict in the base class)::
        {
            'type': 'script',
            'name': '<script_label>,
            'methods': [<method_list>],
        }
    methods can be as follows:
        * ['GET']
        * ['*'] - which will do a call on every request method;
    """
    call_type = CallType.SCRIPT
 
[docs]class Endpoint(object):
    """
    The object which stores metadata about endpoints in custom socket;
    The JSON format is as follows::
        {
            '<endpoint_name>': {
                'calls': [
                    <list of JSON format of Calls objects>
                ]
            }
        }
    """
    def __init__(self, name):
        self.name = name
        self.calls = []
[docs]    def add_call(self, call):
        self.calls.append(call)
 
[docs]    def to_endpoint_data(self):
        return {
            self.name: {
                'calls': [call.to_dict() for call in self.calls]
            }
        }
  
[docs]class BaseDependency(object):
    """
    Base dependency object;
    On the base of the fields attribute - the JSON format of the dependency is returned.
    The fields are taken from the dependency object - which can be Script (supported now).
    """
    fields = []
    dependency_type = None
    name = None
[docs]    def to_dependency_data(self):
        if self.dependency_type is None:
            raise SyncanoValueError('dependency_type not set.')
        dependency_data = {'type': self.dependency_type}
        dependency_data.update(self.get_dependency_data())
        return dependency_data
 
[docs]    def get_name(self):
        if self.name is not None:
            return {'name': self.name}
        return {'name': self.dependency_object.name}
 
[docs]    def get_dependency_data(self):
        raise NotImplementedError()
 
[docs]    def create_from_raw_data(self, raw_data):
        raise NotImplementedError()
 
    def _build_dict(self, instance):
        return {field_name: getattr(instance, field_name) for field_name in self.fields}
 
[docs]class ScriptDependency(BaseDependency):
    """
    Script dependency object;
    The JSON format is as follows::
        {
            'type': 'script',
            'runtime_name': '<runtime name defined in RuntimeChoices>',
            'source': '<source>',
            'name': '<name>'
        }
    """
    dependency_type = DependencyType.SCRIPT
    fields = [
        'runtime_name',
        'source'
    ]
    def __init__(self, script_or_script_endpoint, name=None):
        if not isinstance(script_or_script_endpoint, (Script, ScriptEndpoint)):
            raise SyncanoValueError('Script or ScriptEndpoint expected.')
        if isinstance(script_or_script_endpoint, Script) and not name:
            raise SyncanoValueError('Name should be provided.')
        self.dependency_object = script_or_script_endpoint
        self.name = name
[docs]    def get_dependency_data(self):
        if isinstance(self.dependency_object, ScriptEndpoint):
            script = Script.please.get(id=self.dependency_object.script,
                                       instance_name=self.dependency_object.instance_name)
        else:
            script = self.dependency_object
        dependency_data = self.get_name()
        dependency_data.update(self._build_dict(script))
        return dependency_data
 
    @classmethod
[docs]    def create_from_raw_data(cls, raw_data):
        return cls(**{
            'script_or_script_endpoint': Script(source=raw_data['source'], runtime_name=raw_data['runtime_name']),
            'name': raw_data['name'],
        })
  
[docs]class ClassDependency(BaseDependency):
    """
    Class dependency object;
    The JSON format is as follows::
        {
            'type': 'class',
            'name': '<class_name>',
            'schema': [
                {"name": "f1", "type": "string"},
                {"name": "f2", "type": "string"},
                {"name": "f3", "type": "integer"}
            ],
        }
    """
    dependency_type = DependencyType.CLASS
    fields = [
        'name',
        'schema'
    ]
    def __init__(self, class_instance):
        self.dependency_object = class_instance
        self.name = class_instance.name
[docs]    def get_dependency_data(self):
        data_dict = self._build_dict(self.dependency_object)
        data_dict['schema'] = data_dict['schema'].schema
        return data_dict
 
    @classmethod
[docs]    def create_from_raw_data(cls, raw_data):
        return cls(**{'class_instance': Class(**raw_data)})