import os
import re
from collections import defaultdict

from .basic_archive import BasicArchive, BASIC_ENTITY_TYPES, InterfaceError, ArchiveError
from .lc_archive import LcArchive, LC_ENTITY_TYPES
from ..implementations import BasicImplementation, IndexImplementation


class AbstractIndex(object):
    def make_interface(self, iface):
        if iface == 'index':
            return IndexImplementation(self)
        elif iface == 'basic':
            return BasicImplementation(self)
        else:
            raise InterfaceError('Unable to create interface %s' % iface)


class BasicIndex(AbstractIndex, BasicArchive):
    _ns_uuid_required = None

    def serialize(self, characterizations=False, values=False, domesticate=False):
        return super(BasicIndex, self).serialize(characterizations=False, values=False, domesticate=domesticate)

    def write_to_file(self, filename, gzip=True, **kwargs):
        """
        created to make gzip=True by default for index archives
        :param filename:
        :param gzip:
        :param kwargs:
        :return:
        """
        return super(BasicIndex, self).write_to_file(filename, gzip=gzip, **kwargs)


class LcIndex(AbstractIndex, LcArchive):
    _ns_uuid_required = None

    def serialize(self, exchanges=False, characterizations=False, values=False, domesticate=False):
        return super(LcIndex, self).serialize(exchanges=False, characterizations=False, values=False,
                                              domesticate=domesticate)

    def write_to_file(self, filename, gzip=True, **kwargs):
        """
        created to make gzip=True by default for index archives
        :param filename:
        :param gzip:
        :param kwargs:
        :return:
        """
        return super(LcIndex, self).write_to_file(filename, gzip=gzip, **kwargs)


def index_archive(archive, source, ref=None, signifier='index', force=False, save=True):
    if source is None:
        if save is True:
            raise AssertionError('Source is required')
    else:
        if not bool(re.search('\.gz$', source)):
            source += '.gz'
        if source == archive.source:
            raise ArchiveError('Index must have a different source from original archive')
        if save and os.path.exists(source):
            if force:
                print('File exists: %s. Overwriting..' % source)
            else:
                raise FileExistsError(source)

    if ref is None or ref == archive.ref:
        ref = archive.construct_new_ref(signifier=signifier)

    if isinstance(archive, LcArchive):
        index = LcIndex(source, ref=ref, static=True, **archive.init_args)
        types = LC_ENTITY_TYPES
    else:
        index = BasicIndex(source, ref=ref, static=True, **archive.init_args)
        types = BASIC_ENTITY_TYPES
    for t in types:
        for e in archive.entities_by_type(t):
            index.add(e)

    # import original archives list of names
    names = defaultdict(list)
    for s, r in archive.names.items():
        names[r].append(s)

    index.load_from_dict({'catalogNames': names})

    if save:
        print('Saving index to file %s' % source)
        index.write_to_file(source, gzip=True)
    return index
