# -*- coding: utf-8 -*-
import os
import imp
import pprint
import inspect

from ppms import ppms
from ppms.migration import BasePacket, PacketCategory, \
    get_module_from_path, get_uuid_from_attributes


__all__ = ['FillMigrationruleTable']

# Drag these into the local namespace for sake of readability
join = os.path.join
abspath = os.path.abspath

MIGRATION_FOLDER = 'migration'
POSSIBLE_PYTHON_FILE_SUFFIXES = tuple([item[0] for item in imp.get_suffixes()])


class FillMigrationruleTable(BasePacket):
    """A special packet that fills the migrationrule table from the filesystem

    This packet walks down the /migration/ path to find packet classes.
    If you want your packets to be found automatically
    you need to save them somewhere in that location.

    The modules in there are imported and then the members analyzed if they
    are a instance of BasePacket. If you don't want your packet to be found
    for some reason you can hide them from this packet by giving your packet
    a name with double underlines, f.e. __HiddenPacket__
    """
    category = PacketCategory.CATEGORY_HELPER
    special_snowflake = True

    def fix(self, runner):
        failures = 0

        self.log_heading('Traversing %s' % abspath(MIGRATION_FOLDER))
        for dirpath, dirnames, filenames in os.walk(MIGRATION_FOLDER):
            # TODO: contextlib.suppress
            if '__pycache__' in dirnames:
                dirnames.remove('__pycache__')

            for file_name in filter(lambda x: x.endswith(POSSIBLE_PYTHON_FILE_SUFFIXES), filenames):
                file_name_without_extension = os.path.splitext(file_name)[0]
                file_path_without_extension = join(dirpath, file_name_without_extension)

                self.log_heading('Looking at file "%s"' % abspath(join(dirpath, file_name)))
                module_path = file_path_without_extension.replace(os.sep, '.')

                try:
                    module = get_module_from_path(module_path)
                except BaseException as err:
                    self.log('Importing module "%s" failed!' % module_path)
                    self.log(err)

                    failures += 1
                    continue

                self.log('Looking for possible migration packets in module')
                packets = self.get_potential_migration_packets_from_module(module=module)

                if packets:
                    self.log('Found these packets: %s' % packets)

                for packet_cls, name in packets:
                    python_file_path_for_db = module_path + '.' + name

                    if not self.store_migration_packet_in_database(packet_cls=packet_cls,
                                                                   python_file_path_for_db=python_file_path_for_db,
                                                                   name=name):
                        failures += 1

        if failures:
            return self.fail('Encountered %i errors!' % failures)

        return self.success()

    def get_potential_migration_packets_from_module(self, module):
        """Return a list of potential migration packets from a module"""
        packets = []
        for attribute in dir(module):
            # This is primarily to reduce logfile clutter, but I
            # guess you could also hide your packets this way
            if attribute.startswith('__') and attribute.endswith('__'):
                continue

            # Ignore FailedPacket imports
            if attribute == 'FailedPacket':
                continue

            inst = getattr(module, attribute)
            try:
                if self.is_migration_packet(inst):
                    found_packet = True
                else:
                    found_packet = False

            except (TypeError, AttributeError) as err:
                self.log('Received an exception while checking "%s"' % attribute)
                self.log(err)
                found_packet = False

            if found_packet:
                packets.append((inst, attribute))

        return packets

    def store_migration_packet_in_database(self, packet_cls, python_file_path_for_db, name):
        """Try to store a migration packet in the database

        Returns True if the packet could be stored or False if it couldn't
        """
        self.log('Checking if the migration packet %s already exists' % name)
        if get_uuid_from_attributes(packet_cls, python_file_path_for_db) is None:
            attributes = {'pythonpath': python_file_path_for_db,
                          'systemstart': packet_cls.run_on_startup}

            self.log('Negative! Creating one with the following attributes:\n%s' % pprint.pformat(attributes))
            record = ppms.create_record(314, attributes)
            if record is None:
                self.log('Could not create record for packet "%s" ("%s")' % (packet_cls, python_file_path_for_db))
                return False

            if not record.save():
                text = 'Could not create a migrationrule record with the following attributes:\n%s'
                self.log(text % pprint.pformat(attributes))
                return False

            ppms.db_commit()

        return True

    @staticmethod
    def is_migration_packet(instance):
        return inspect.isclass(instance) and \
               not inspect.isabstract(instance) and \
               issubclass(instance, BasePacket)