# -*- coding: utf-8 -*-
# Generated by Django 1.10.2 on 2016-12-11 23:14
from __future__ import unicode_literals

from django.db import migrations
from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist

import logging

logger = logging.getLogger(__name__)


def optional_dependencies():
    """
    Only require these migrations if the given app
    is installed. If the app isn't installed then
    the relevant AuthServicesInfo field will be LOST
    when the data migration is run.
    """
    installed_apps = settings.INSTALLED_APPS

    dependencies = []

    # Skip adding module dependencies if the settings specifies that services have been migrated
    if hasattr(settings, 'SERVICES_MIGRATED') and settings.SERVICES_MIGRATED:
        return dependencies

    if 'services.modules.xenforo' in installed_apps:
        dependencies.append(('xenforo', '0001_initial'))
    if 'services.modules.discord' in installed_apps:
        dependencies.append(('discord', '0001_initial'))
    if 'services.modules.discourse' in installed_apps:
        dependencies.append(('discourse', '0001_initial'))
    if 'services.modules.ipboard' in installed_apps:
        dependencies.append(('ipboard', '0001_initial'))
    if 'services.modules.ips4' in installed_apps:
        dependencies.append(('ips4', '0001_initial'))
    if 'services.modules.market' in installed_apps:
        dependencies.append(('market', '0001_initial'))
    if 'services.modules.openfire' in installed_apps:
        dependencies.append(('openfire', '0001_initial'))
    if 'services.modules.smf' in installed_apps:
        dependencies.append(('smf', '0001_initial'))
    if 'services.modules.teamspeak3' in installed_apps:
        dependencies.append(('teamspeak3', '0003_teamspeak3user'))
    if 'services.modules.mumble' in installed_apps:
        dependencies.append(('mumble', '0003_mumbleuser_user'))
    if 'services.modules.phpbb3' in installed_apps:
        dependencies.append(('phpbb3', '0001_initial'))

    return dependencies


class DatalossException(Exception):
    def __init__(self, field, app):
        message = "This migration would cause a loss of data for the %s field, ensure the %s app is installed and" \
                  " run the migration again" % (field, app)
        super(Exception, self).__init__(message)


def check_for_dataloss(authservicesinfo):
    """
    Check if any of the authservicesinfo contain a field which contains data
    that would be lost because the target module for migration is not installed.
    If a record is found with no matching target installed an exception is raised.
    :param authservicesinfo: AuthServicesInfo records to check
    """
    installed_apps = settings.INSTALLED_APPS

    for authinfo in authservicesinfo:
        if authinfo.xenforo_username and 'services.modules.xenforo' not in installed_apps:
            raise DatalossException('xenforo_username', 'services.modules.xenforo')
        if authinfo.discord_uid and 'services.modules.discord' not in installed_apps:
            raise DatalossException('discord_uid', 'services.modules.discord')
        if authinfo.discourse_enabled and 'services.modules.discourse' not in installed_apps:
            raise DatalossException('discourse_enabled', 'services.modules.discourse')
        if authinfo.ipboard_username and 'services.modules.ipboard' not in installed_apps:
            raise DatalossException('ipboard_username', 'services.modules.ipboard')
        if authinfo.ips4_id and 'services.modules.ips4' not in installed_apps:
            raise DatalossException('ips4_id', 'services.modules.ips4')
        if authinfo.market_username and 'services.modules.market' not in installed_apps:
            raise DatalossException('market_username', 'services.modules.market')
        if authinfo.jabber_username and 'services.modules.openfire' not in installed_apps:
            raise DatalossException('jabber_username', 'services.modules.openfire')
        if authinfo.smf_username and 'services.modules.smf' not in installed_apps:
            raise DatalossException('smf_username', 'services.modules.smf')
        if authinfo.teamspeak3_uid and 'services.modules.teamspeak3' not in installed_apps:
            raise DatalossException('teamspeak3_uid', 'services.modules.teamspeak3')
        if authinfo.mumble_username and 'services.modules.mumble' not in installed_apps:
            raise DatalossException('mumble_username', 'services.modules.mumble')
        if authinfo.forum_username and 'services.modules.phpbb3' not in installed_apps:
            raise DatalossException('forum_username', 'services.modules.phpbb3')


def forward(apps, schema_editor):
    installed_apps = settings.INSTALLED_APPS
    AuthServicesInfo = apps.get_model("authentication", "AuthServicesInfo")

    # Check if any records would result in dataloss
    check_for_dataloss(AuthServicesInfo.objects.all())

    XenforoUser = apps.get_model('xenforo', 'XenforoUser') if 'services.modules.xenforo' in installed_apps else None
    DiscordUser = apps.get_model('discord', 'DiscordUser') if 'services.modules.discord' in installed_apps else None
    DiscourseUser = apps.get_model('discourse', 'DiscourseUser') if 'services.modules.discourse' in installed_apps else None
    IpboardUser = apps.get_model('ipboard', 'IpboardUser') if 'services.modules.ipboard' in installed_apps else None
    Ips4User = apps.get_model('ips4', 'Ips4User') if 'services.modules.ips4' in installed_apps else None
    MarketUser = apps.get_model('market', 'MarketUser') if 'services.modules.market' in installed_apps else None
    OpenfireUser = apps.get_model('openfire', 'OpenfireUser') if 'services.modules.openfire' in installed_apps else None
    SmfUser = apps.get_model('smf', 'SmfUser') if 'services.modules.smf' in installed_apps else None
    Teamspeak3User = apps.get_model('teamspeak3', 'Teamspeak3User') if 'services.modules.teamspeak3' in installed_apps else None
    MumbleUser = apps.get_model('mumble', 'MumbleUser') if 'services.modules.mumble' in installed_apps else None
    Phpbb3User = apps.get_model('phpbb3', 'Phpbb3User') if 'services.modules.phpbb3' in installed_apps else None

    for authinfo in AuthServicesInfo.objects.all():
        user = authinfo.user

        if XenforoUser is not None and authinfo.xenforo_username:
            logging.debug('Updating Xenforo info for %s' % user.username)
            xfu = XenforoUser()
            xfu.user = user
            xfu.username = authinfo.xenforo_username
            xfu.save()

        if DiscordUser is not None and authinfo.discord_uid:
            logging.debug('Updating Discord info for %s' % user.username)
            du = DiscordUser()
            du.user = user
            du.uid = authinfo.discord_uid
            du.save()

        if DiscourseUser is not None and authinfo.discourse_enabled:
            logging.debug('Updating Discourse info for %s' % user.username)
            du = DiscourseUser()
            du.user = user
            du.enabled = authinfo.discourse_enabled
            du.save()

        if IpboardUser is not None and authinfo.ipboard_username:
            logging.debug('Updating IPBoard info for %s' % user.username)
            ipb = IpboardUser()
            ipb.user = user
            ipb.username = authinfo.ipboard_username
            ipb.save()

        if Ips4User is not None and authinfo.ips4_id:
            logging.debug('Updating Ips4 info for %s' % user.username)
            ips = Ips4User()
            ips.user = user
            ips.id = authinfo.ips4_id
            ips.username = authinfo.ips4_username
            ips.save()

        if MarketUser is not None and authinfo.market_username:
            logging.debug('Updating Market info for %s' % user.username)
            mkt = MarketUser()
            mkt.user = user
            mkt.username = authinfo.market_username
            mkt.save()

        if OpenfireUser is not None and authinfo.jabber_username:
            logging.debug('Updating Openfire (jabber) info for %s' % user.username)
            ofu = OpenfireUser()
            ofu.user = user
            ofu.username = authinfo.jabber_username
            ofu.save()

        if SmfUser is not None and authinfo.smf_username:
            logging.debug('Updating SMF info for %s' % user.username)
            smf = SmfUser()
            smf.user = user
            smf.username = authinfo.smf_username
            smf.save()

        if Teamspeak3User is not None and authinfo.teamspeak3_uid:
            logging.debug('Updating Teamspeak3 info for %s' % user.username)
            ts3 = Teamspeak3User()
            ts3.user = user
            ts3.uid = authinfo.teamspeak3_uid
            ts3.perm_key = authinfo.teamspeak3_perm_key
            ts3.save()

        if MumbleUser is not None and authinfo.mumble_username:
            logging.debug('Updating mumble info for %s' % user.username)
            try:
                mbl = MumbleUser.objects.get(username=authinfo.mumble_username)
                mbl.user = user
                mbl.save()
            except ObjectDoesNotExist:
                logger.warn('AuthServiceInfo mumble_username for {} but no '
                            'corresponding record in MumbleUser, dropping'.format(user.username))

        if Phpbb3User is not None and authinfo.forum_username:
            logging.debug('Updating phpbb3 info for %s' % user.username)
            phb = Phpbb3User()
            phb.user = user
            phb.username = authinfo.forum_username
            phb.save()


def reverse(apps, schema_editor):
    User = apps.get_model('auth', 'User')
    AuthServicesInfo = apps.get_model("authentication", "AuthServicesInfo")

    for user in User.objects.all():
        authinfo, c = AuthServicesInfo.objects.get_or_create(user=user)

        if hasattr(user, 'xenforo'):
            logging.debug('Reversing xenforo for %s' % user.username)
            authinfo.xenforo_username = user.xenforo.username

        if hasattr(user, 'discord'):
            logging.debug('Reversing discord for %s' % user.username)
            authinfo.discord_uid = user.discord.uid

        if hasattr(user, 'discourse'):
            logging.debug('Reversing discourse for %s' % user.username)
            authinfo.discourse_enabled = user.discourse.enabled

        if hasattr(user, 'ipboard'):
            logging.debug('Reversing ipboard for %s' % user.username)
            authinfo.ipboard_username = user.ipboard.username

        if hasattr(user, 'ips4'):
            logging.debug('Reversing ips4 for %s' % user.username)
            authinfo.ips4_id = user.ips4.id
            authinfo.ips4_username = user.ips4.username

        if hasattr(user, 'market'):
            logging.debug('Reversing market for %s' % user.username)
            authinfo.market_username = user.market.username

        if hasattr(user, 'openfire'):
            logging.debug('Reversing openfire (jabber) for %s' % user.username)
            authinfo.jabber_username = user.openfire.username

        if hasattr(user, 'smf'):
            logging.debug('Reversing smf for %s' % user.username)
            authinfo.smf_username = user.smf.username

        if hasattr(user, 'teamspeak3'):
            logging.debug('Reversing teamspeak3 for %s' % user.username)
            authinfo.teamspeak3_uid = user.teamspeak3.uid
            authinfo.teamspeak3_perm_key = user.teamspeak3.perm_key

        if hasattr(user, 'mumble'):
            logging.debug('Reversing mumble for %s' % user.username)
            try:
                authinfo.mumble_username = user.mumble.all()[:1].get().username
            except ObjectDoesNotExist:
                logging.debug('Failed to reverse mumble for %s' % user.username)

        if hasattr(user, 'phpbb3'):
            logging.debug('Reversing phpbb3 for %s' % user.username)
            authinfo.forum_username = user.phpbb3.username

        logging.debug('Saving AuthServicesInfo for %s ' % user.username)
        authinfo.save()


class Migration(migrations.Migration):

    dependencies = optional_dependencies() + [
        ('authentication', '0012_remove_add_delete_authservicesinfo_permissions'),
    ]

    operations = [
        # Migrate data
        migrations.RunPython(forward, reverse),
        # Remove fields
        migrations.RemoveField(
            model_name='authservicesinfo',
            name='discord_uid',
        ),
        migrations.RemoveField(
            model_name='authservicesinfo',
            name='discourse_enabled',
        ),
        migrations.RemoveField(
            model_name='authservicesinfo',
            name='forum_username',
        ),
        migrations.RemoveField(
            model_name='authservicesinfo',
            name='ipboard_username',
        ),
        migrations.RemoveField(
            model_name='authservicesinfo',
            name='ips4_id',
        ),
        migrations.RemoveField(
            model_name='authservicesinfo',
            name='ips4_username',
        ),
        migrations.RemoveField(
            model_name='authservicesinfo',
            name='jabber_username',
        ),
        migrations.RemoveField(
            model_name='authservicesinfo',
            name='market_username',
        ),
        migrations.RemoveField(
            model_name='authservicesinfo',
            name='mumble_username',
        ),
        migrations.RemoveField(
            model_name='authservicesinfo',
            name='smf_username',
        ),
        migrations.RemoveField(
            model_name='authservicesinfo',
            name='teamspeak3_perm_key',
        ),
        migrations.RemoveField(
            model_name='authservicesinfo',
            name='teamspeak3_uid',
        ),
        migrations.RemoveField(
            model_name='authservicesinfo',
            name='xenforo_username',
        ),
    ]