from apimas.errors import ValidationError
from apimas_django.handlers import Nothing, DjangoProcessorFactory


def check_flags(name, spec, value, full, instance, toplevel=False):
    flags = spec.get('flags', [])
    default = spec.get('default', Nothing)

    if 'nowrite' in flags and not toplevel:
        if value is not Nothing:
            raise ValidationError("'%s': Field is not writable" % name)
        return Nothing

    if full and value is Nothing:
        value = Nothing if default is Nothing else default()

    if full and value is Nothing:
        raise ValidationError("'%s': Field is required" % name)

    if value is None and 'nullable' not in flags:
        raise ValidationError("'%s': Field is not nullable" % name)

    if value is not Nothing and 'noupdate' in flags and instance is not None:
        source = spec['source']
        stored_value = getattr(instance, source)
        if value != stored_value:
            raise ValidationError("'%s': Field is not updatable" % name)

    return value


def load_data_fields(subspecs, data, full, instance):
    loaded = {}
    for field_name, field_spec in subspecs.iteritems():
        value = data.get(field_name, Nothing)
        value = check_flags(field_name, field_spec, value, full, instance)
        source = field_spec['source']
        if value is not Nothing:
            loaded[source] = value

    return loaded


def load_data_subcollections(spec, data, full, instance):
    loaded = {}
    for subname, subspec in spec['subcollections'].iteritems():
        subsource = subspec['source']
        subdata = data.get(subname, Nothing)
        if subdata is Nothing:
            continue
        loaded[subsource] = [load_data(subname, subspec, elem, full, None)
                             for elem in subdata]
    return loaded


def load_data_substructs(spec, data, full, instance):
    loaded = {}
    for subname, subspec in spec['substructs'].iteritems():
        subsource = subspec['source']
        subdata = data.get(subname, Nothing)
        loaded_substruct = load_data(subname, subspec, subdata, full, instance)
        if loaded_substruct is not Nothing:
            loaded[subsource] = loaded_substruct
    return loaded


def load_data(name, spec, data, full, instance, toplevel=False):
    data = check_flags(name, spec, data, full, instance, toplevel=toplevel)
    if data is Nothing:
        return Nothing

    if data is None:
        return None

    subfields = load_data_fields(spec['subfields'], data, full, instance)
    substructs = load_data_substructs(spec, data, full, instance)
    subcollections = load_data_subcollections(spec, data, full, instance)
    loaded = {}
    loaded.update(subfields)
    loaded.update(substructs)
    loaded.update(subcollections)
    return loaded


class LoadData(DjangoProcessorFactory):
    def __init__(self, collection_loc, action_name, spec, loaddata_full):
        self.collection_loc = collection_loc
        self.collection_name = collection_loc[-1]
        self.spec = spec
        self.full = loaddata_full is None or loaddata_full

    def process(self, runtime_data):
        data = runtime_data['imported/content']
        instance = runtime_data.get('backend/instance')
        loaded = load_data(
            self.collection_name, self.spec, data, self.full, instance,
            toplevel=True)
        return {'backend/input': loaded}
