# coding: utf-8


from django.conf import settings
from django.db import connection

from idm.core.constants.groupmembership_system_relation import MEMBERSHIP_SYSTEM_RELATION_STATE
from idm.core.constants.groupmembership_system_relation import (
    GROUPMEMBERSHIP_INCONSISTENCY, GROUPMEMBERSHIP_INCONSISTENCY_TYPE, GROUPMEMBERSHIP_INCONSISTENCY_STATE
)
from idm.core.constants.system import SYSTEM_GROUP_POLICY


SQL_UPDATE_MATCHING_BASE = '''
    UPDATE groupmembership_inconsistency gmi
       SET state = %(gmi_state_matching)s
     WHERE id IN (
           SELECT gmi.id
             FROM groupmembership_inconsistency gmi
                  JOIN users_group g
                  ON gmi.group_id = g.external_id

                  JOIN upravlyator_user u
                  ON gmi.username = u.username

                  JOIN users_groupmembership gm
                  ON gm.user_id = u.id
                     AND gm.group_id = g.id

                  {passport_login_join}

                  JOIN core_groupmembershipsystemrelation gmsr
                  ON gmsr.membership_id = gm.id
            WHERE gmi.sync_key_id = %(action_id)s
              AND gmi.system_id = %(system_id)s

                  {passport_login_equal_condition}

              AND gmsr.state IN %(pushable_states)s
                );
    '''

WITHOUT_PASSPORT_LOGIN_PARAMS = {
    'passport_login_field': '',
    'passport_login_value': '',
    'passport_login_join': '',
    'passport_login_equal_condition': '',
}

WITH_PASSPORT_LOGIN_PARAMS = {
    'passport_login_field': ', passport_login',
    'passport_login_value': ', pl.login',
    'passport_login_join': '''
        LEFT OUTER JOIN upravlyator_userpassportlogin pl
        ON gm.passport_login_id = pl.id
    ''',
    'passport_login_equal_condition': '''
            AND (
                  (
                      pl IS NULL
                  AND gmi.passport_login IS NULL
                  )
             OR pl.login = gmi.passport_login
                )
    ''',
}

SQL_UPDATE_MATCHING_WITH_LOGINS = SQL_UPDATE_MATCHING_BASE.format(**WITH_PASSPORT_LOGIN_PARAMS)

SQL_UPDATE_MATCHING_WITHOUT_LOGINS = SQL_UPDATE_MATCHING_BASE.format(**WITHOUT_PASSPORT_LOGIN_PARAMS)

SQL_UPDATE_MISMATCHING = '''
    UPDATE groupmembership_inconsistency
       SET type = %(system_has_we_dont)s, state = %(gmi_state_active)s
     WHERE sync_key_id = %(action_id)s
       AND system_id = %(system_id)s
       AND state = %(gmi_state_created)s;
    '''


SQL_CREATE_OUR_SIDE_BASE = '''
    INSERT INTO groupmembership_inconsistency (system_id, sync_key_id, type, state, added,
                updated, membership_id, username, group_id

                {passport_login_field}

                )
         SELECT %(system_id)s, %(action_id)s, %(we_have_system_dont)s, %(gmi_state_active)s, CURRENT_TIMESTAMP,
                CURRENT_TIMESTAMP, gmsr.id, u.username, g.external_id

                {passport_login_value}

           FROM core_groupmembershipsystemrelation gmsr
                INNER JOIN users_groupmembership gm
                ON gmsr.membership_id = gm.id

                INNER JOIN upravlyator_user u
                ON gm.user_id = u.id

                INNER JOIN users_group g
                ON gm.group_id = g.id

                {passport_login_join}

                LEFT OUTER JOIN groupmembership_inconsistency gmi
                ON (
                       gmi.username = u.username
                   AND gmi.group_id = g.external_id

                       {passport_login_equal_condition}

                   AND gmi.sync_key_id = %(action_id)s
                   )
          WHERE gmsr.state IN (%(gsmr_state_activated)s, %(gsmr_state_hold)s)
            AND gmsr.system_id = %(system_id)s
            AND gmi.id IS NULL;
    '''

SQL_CREATE_OUR_SIDE_WITH_LOGINS = SQL_CREATE_OUR_SIDE_BASE.format(**WITH_PASSPORT_LOGIN_PARAMS)

SQL_CREATE_OUR_SIDE_WITHOUT_LOGINS = SQL_CREATE_OUR_SIDE_BASE.format(**WITHOUT_PASSPORT_LOGIN_PARAMS)

SQL_DELETE_MATCHING = '''
DELETE FROM groupmembership_inconsistency
      WHERE sync_key_id = %(action_id)s
        AND system_id = %(system_id)s
        AND state = %(gmi_state_matching)s;
'''

SQL_UPDATE_PAIRED = '''
UPDATE groupmembership_inconsistency gmi
   SET state = %(gmi_state_paired)s
 WHERE id IN (
       SELECT gmi_system.id
         FROM groupmembership_inconsistency gmi_system
              JOIN groupmembership_inconsistency gmi_our
              ON (
                     gmi_system.username = gmi_our.username
                 AND gmi_system.group_id = gmi_our.group_id
                 AND gmi_system.sync_key_id = gmi_our.sync_key_id
                 AND gmi_system.state = gmi_our.state
                 )
        WHERE gmi_system.sync_key_id = %(action_id)s
          AND gmi_system.system_id = %(system_id)s
          AND gmi_system.state = %(gmi_state_active)s
          AND gmi_system.type = %(system_has_we_dont)s
          AND gmi_our.type = %(we_have_system_dont)s
             );
'''


def check_group_memberships(system, action):
    from idm.inconsistencies.models import GroupMembershipInconsistency

    SQL_UPDATE_MATCHING = {
        SYSTEM_GROUP_POLICY.AWARE_OF_MEMBERSHIPS_WITHOUT_LOGINS: SQL_UPDATE_MATCHING_WITHOUT_LOGINS,
        SYSTEM_GROUP_POLICY.AWARE_OF_MEMBERSHIPS_WITH_LOGINS: SQL_UPDATE_MATCHING_WITH_LOGINS,
    }
    SQL_CREATE_OUR_SIDE = {
        SYSTEM_GROUP_POLICY.AWARE_OF_MEMBERSHIPS_WITHOUT_LOGINS: SQL_CREATE_OUR_SIDE_WITHOUT_LOGINS,
        SYSTEM_GROUP_POLICY.AWARE_OF_MEMBERSHIPS_WITH_LOGINS: SQL_CREATE_OUR_SIDE_WITH_LOGINS,
    }

    # Все старые неконсистентности в obsolete
    system.groupmembership_inconsistencies.active().update(state=GROUPMEMBERSHIP_INCONSISTENCY.STATES.OBSOLETE)

    # Создаем неконсистентности согласно выдаче системы в статусе created
    system_memberships = system.plugin.get_memberships()
    batch_size = settings.IDM_INCONSISTENCY_BATCH_SIZE
    batch = []
    for membership in system_memberships:
        type_ = GROUPMEMBERSHIP_INCONSISTENCY.TYPES.UNDECIDED
        state = GROUPMEMBERSHIP_INCONSISTENCY.STATES.CREATED
        inconsistency = GroupMembershipInconsistency(
            system=system,
            sync_key=action,
            state=state,
            type=type_,
            username=membership.get('login'),
            group_id=membership.get('group'),
            passport_login=membership.get('passport_login') if membership.get('passport_login') else None,
            membership=None,
        )
        batch.append(inconsistency)
        if len(batch) == batch_size:
            GroupMembershipInconsistency.objects.bulk_create(batch)
            batch = []
    else:
        if batch:
            GroupMembershipInconsistency.objects.bulk_create(batch)

    sql_params = {
        'action_id': action.pk,
        'system_id': system.pk,

        'pushable_states': tuple(map(str, MEMBERSHIP_SYSTEM_RELATION_STATE.PUSHABLE_STATES)),

        'gmi_state_active': GROUPMEMBERSHIP_INCONSISTENCY_STATE.ACTIVE,
        'gmi_state_created': GROUPMEMBERSHIP_INCONSISTENCY_STATE.CREATED,
        'gmi_state_paired': GROUPMEMBERSHIP_INCONSISTENCY_STATE.PAIRED,
        'gmi_state_matching': GROUPMEMBERSHIP_INCONSISTENCY_STATE.MATCHING,

        'gsmr_state_activated': MEMBERSHIP_SYSTEM_RELATION_STATE.ACTIVATED,
        'gsmr_state_hold': MEMBERSHIP_SYSTEM_RELATION_STATE.HOLD,

        'system_has_we_dont': GROUPMEMBERSHIP_INCONSISTENCY_TYPE.SYSTEM_HAS_WE_DONT,
        'we_have_system_dont': GROUPMEMBERSHIP_INCONSISTENCY_TYPE.WE_HAVE_SYSTEM_DONT,
    }

    # Неконсистентностям, для которых обнаружились совпадения, в базе проставим статус matching
    with connection.cursor() as cursor:
        cursor.execute(SQL_UPDATE_MATCHING[system.group_policy], sql_params)

    # Неконсистентностям не в matching проставим статус active и тип system_has_we_dont
    with connection.cursor() as cursor:
        cursor.execute(SQL_UPDATE_MISMATCHING, sql_params)

    # Добавим в неконсистентности типа we_have_system_dont
    with connection.cursor() as cursor:
        cursor.execute(SQL_CREATE_OUR_SIDE[system.group_policy], sql_params)

    # Удалим неконсистентности в статусе matching
    with connection.cursor() as cursor:
        cursor.execute(SQL_DELETE_MATCHING, sql_params)

    if system.group_policy == SYSTEM_GROUP_POLICY.AWARE_OF_MEMBERSHIPS_WITH_LOGINS:
        # Неконсистентности на стороне системы, которые имеют пару, отличающуюся паспортным логином,
        # переводим в статус paired. Считаем, что они разрешатся при запушивании членства с новым логином
        with connection.cursor() as cursor:
            cursor.execute(SQL_UPDATE_PAIRED, sql_params)
