# By: Riasat Ullah
# This file contains functions that will help to update the subscription of an organization.

from cache_queries import cache_organizations, cache_routing
from dbqueries import db_accounts, db_members
from taskcallrest import settings
from utils import constants, helpers, permissions, roles, var_names
import configuration as configs
import datetime
import json


def update_subscription(conn, client, timestamp, subscription_id, org_perm_map):
    '''
    Updates subscriptions of a list of organization IDs. Also updates the cache for
    organization permissions if the cache is on.
    :param conn: db connection
    :param client: cache client
    :param timestamp: timestamp the request is being made on
    :param subscription_id: the new subscription ID to change to
    :param org_perm_map: (dict) -> { org ID: perm, ...}
    :errors: AssertionError, DatabaseError, Redis errors
    '''
    assert isinstance(timestamp, datetime.datetime)
    assert isinstance(subscription_id, int)
    assert subscription_id in configs.allowed_subscription_ids
    assert isinstance(org_perm_map, dict)

    org_ids = list(org_perm_map.keys())
    all_add_ons = dict() if subscription_id == configs.free_subscription_id\
        else db_accounts.get_add_on_subscriptions(conn, timestamp, org_ids)
    all_org_members = db_members.get_member_roles_and_titles(conn, timestamp, org_ids)
    if len(org_ids) != len(all_org_members):
        internal_err_msg = 'The following organization IDs could not be found - ' +\
                           str(set(org_ids).difference(set(all_org_members.keys())))
        raise SystemError(internal_err_msg)

    # mapping of permissions to add-on IDs
    add_on_id_perm_map = {
        configs.stakeholder_add_on_id: [permissions.ORG_STAKEHOLDER_PERMISSION],
        configs.live_call_routing_add_on_id: [permissions.ORG_LIVE_CALL_ROUTING_SECONDARY_PERMISSION],
        configs.sso_add_on_id: [permissions.ORG_SSO_PERMISSION]
    }

    # IDs of organizations whose conditional routing(s) should be deleted due to loss of permissions
    deleting_cond_rout_org_ids = []

    query_params_list = []
    org_data = dict()
    for id_ in all_org_members:
        new_org_perm = permissions.get_subscription_permission(subscription_id)
        old_org_perm = org_perm_map[id_]
        members = all_org_members[id_]

        org_add_ons = None
        if id_ in all_add_ons:
            org_add_ons = set()
            for ao_id in all_add_ons[id_]:
                if ao_id in add_on_id_perm_map:
                    for add_on_perm in add_on_id_perm_map[ao_id]:
                        if not permissions.has_org_permission(new_org_perm, add_on_perm):
                            new_org_perm = permissions.add_permission(new_org_perm, add_on_perm)
                            org_add_ons.add(ao_id)

            org_add_ons = list(org_add_ons) if len(org_add_ons) > 0 else None

        # handle updates to member permissions and roles
        for item in members:
            # SystemError will be thrown when users with stakeholder roles are attempted to be upgraded
            # to a different role when their new subscription does not allow stakeholders at all.
            try:
                new_role_id, new_user_perm = permissions.convert_user_role_and_permission(
                    new_org_perm, item[var_names.role_id]
                )
            except SystemError:
                new_role_id, new_user_perm = None, None

            item[var_names.role_id] = new_role_id
            item[var_names.user_permissions] = new_user_perm

        # If updating to free subscription, then limit the number of users randomly to the max allowed limit,
        # but make sure to not remove the "owner". Every organization must have an owner. The users are removed
        # by nullifying their roles and user permissions. Then in the "update_subscription" function, users with
        # "null" roles and permission will be automatically deleted.
        num_mem_with_role = sum(1 for item in members if item[var_names.role_id] is not None)
        if subscription_id == configs.free_subscription_id and num_mem_with_role > configs.free_subscription_max_users:
            reduce_by = num_mem_with_role - configs.free_subscription_max_users
            members = helpers.sorted_list_of_dict(members, var_names.user_id, descending=True)

            cutting_count = 0
            for item in members:
                if item[var_names.role_id] != roles.owner_role_id and\
                    item[var_names.role_id] is not None and\
                        cutting_count < reduce_by:

                    item[var_names.role_id] = None
                    item[var_names.user_permissions] = None
                    cutting_count += 1

        to_del_conf_bridges = False
        to_del_cond_routing = False
        to_del_teams = False
        to_del_status_dashboards = False
        to_del_workflows = False
        to_del_bus_subscriptions = False
        to_del_postmortem_reports = False
        to_del_itsm_people = False
        to_del_itsm_groups = False
        to_del_automation_checks = False
        to_del_status_pages = False
        to_del_private_status_pages = False
        to_del_external_sso = False

        # handle changes in permissions
        if permissions.has_org_permission(old_org_perm, permissions.ORG_CONFERENCE_BRIDGES_PERMISSION) and\
                not permissions.has_org_permission(new_org_perm, permissions.ORG_CONFERENCE_BRIDGES_PERMISSION):
            to_del_conf_bridges = True

        if permissions.has_org_permission(old_org_perm, permissions.ORG_ROUTING_PERMISSION) and\
                not permissions.has_org_permission(new_org_perm, permissions.ORG_ROUTING_PERMISSION):
            to_del_cond_routing = True
            deleting_cond_rout_org_ids.append(id_)

        if permissions.has_org_permission(old_org_perm, permissions.ORG_TEAMS_PERMISSION) and\
                not permissions.has_org_permission(new_org_perm, permissions.ORG_TEAMS_PERMISSION):
            to_del_teams = True

        if permissions.has_org_permission(old_org_perm, permissions.ORG_STATUS_DASHBOARDS_CREATE_PERMISSION) and\
                not permissions.has_org_permission(new_org_perm, permissions.ORG_STATUS_DASHBOARDS_CREATE_PERMISSION):
            to_del_status_dashboards = True

        if permissions.has_org_permission(old_org_perm, permissions.ORG_WORKFLOWS_PERMISSION) and\
                not permissions.has_org_permission(new_org_perm, permissions.ORG_WORKFLOWS_PERMISSION):
            to_del_workflows = True

        if permissions.has_org_permission(old_org_perm, permissions.ORG_BUSINESS_SERVICES_SUBSCRIPTION_PERMISSION) and\
                not permissions.has_org_permission(
                    new_org_perm, permissions.ORG_BUSINESS_SERVICES_SUBSCRIPTION_PERMISSION):
            to_del_bus_subscriptions = True

        if permissions.has_org_permission(old_org_perm, permissions.ORG_POSTMORTEM_PERMISSION) and\
                not permissions.has_org_permission(new_org_perm, permissions.ORG_POSTMORTEM_PERMISSION):
            to_del_postmortem_reports = True

        if permissions.has_org_permission(old_org_perm, permissions.ORG_PEOPLE_PERMISSION) and\
                not permissions.has_org_permission(new_org_perm, permissions.ORG_PEOPLE_PERMISSION):
            to_del_itsm_people = True

        if permissions.has_org_permission(old_org_perm, permissions.ORG_GROUPS_PERMISSION) and\
                not permissions.has_org_permission(new_org_perm, permissions.ORG_GROUPS_PERMISSION):
            to_del_itsm_groups = True

        if permissions.has_org_permission(old_org_perm, permissions.ORG_CHECKS_BASIC_PERMISSION) and\
                not permissions.has_org_permission(new_org_perm, permissions.ORG_CHECKS_BASIC_PERMISSION):
            to_del_automation_checks = True

        if permissions.has_org_permission(old_org_perm, permissions.ORG_STATUS_PAGES_PERMISSION) and\
                not permissions.has_org_permission(new_org_perm, permissions.ORG_STATUS_PAGES_PERMISSION):
            to_del_status_pages = True

        if permissions.has_org_permission(old_org_perm, permissions.ORG_STATUS_PAGES_PRIVATE_PERMISSION) and\
                not permissions.has_org_permission(new_org_perm, permissions.ORG_STATUS_PAGES_PRIVATE_PERMISSION):
            to_del_private_status_pages = True

        if permissions.has_org_permission(old_org_perm, permissions.ORG_EXTERNAL_SSO_PERMISSION) and\
                not permissions.has_org_permission(new_org_perm, permissions.ORG_EXTERNAL_SSO_PERMISSION):
            to_del_external_sso = True

        query_params_list.append((
            id_, timestamp, constants.end_timestamp, subscription_id, org_add_ons,
            new_org_perm, json.dumps(members), to_del_conf_bridges, to_del_cond_routing,
            to_del_teams, to_del_status_dashboards, to_del_workflows, to_del_bus_subscriptions,
            to_del_postmortem_reports, to_del_itsm_people, to_del_itsm_groups, to_del_automation_checks,
            to_del_status_pages, to_del_private_status_pages, to_del_external_sso,
        ))
        org_data[id_] = new_org_perm

    # Update subscriptions in the db
    db_accounts.update_alerting_subscription(conn, query_params_list)

    # Update the cache
    if settings.CACHE_ON:

        # update organization permissions in the cache
        if len(org_data) > 0:
            cache_organizations.store_organization_permissions(client, org_data)

        # remove conditional routing of organizations who have lost the permission from the cache
        for id_ in deleting_cond_rout_org_ids:
            cache_routing.remove_org_conditional_routes(client, id_)
