# By: Riasat Ullah
# Contains communication vendor related functions to avoid repetition and circular dependency.

from data_syncers import syncer_task_instances
from dbqueries import db_policies, db_task_instances
from modules.alert_logger import AlertLogger
from modules.event_allocator import EventAllocator
from modules.workflow_manager import WorkflowManager
from objects.alert_log import AlertLog
from objects.events import AcknowledgeEvent, ResolveEvent
from utils import constants, errors, info, logging, permissions, times, var_names


def process_telephone_response(conn, cache, response_method, body, from_number, vendor):
    '''
    Process a response received from a phone through sms or voice call.
    :param conn: db connection
    :param cache: cache client
    :param response_method: the response method - TEXT, CALL
    :param body: the body of the response
    :param from_number: the number the response was received from
    :param vendor: the vendor who processed the incoming SMS/call
    :return: (tuple) -> boolean, str -> (match, reply)
    '''
    # trim the '+' sign from the phone number
    if from_number[0] == '+':
        from_number = from_number[1:]
    body = int(body)

    active_codes = db_task_instances.get_user_active_action_codes(conn, times.get_current_timestamp(), from_number)
    current_time = times.get_current_timestamp()
    matched = False
    item_no = 0
    while not matched and item_no < len(active_codes):
        instance_id = active_codes[item_no][var_names.instance_id]
        org_id = active_codes[item_no][var_names.organization_id]
        org_perm = active_codes[item_no][var_names.organization_permissions]
        user_id = active_codes[item_no][var_names.user_id]
        user_perm = active_codes[item_no][var_names.user_permissions]
        user_iso = active_codes[item_no][var_names.iso_country_code]
        codes = active_codes[item_no][var_names.action_codes]

        if codes[0] == body:
            event = AcknowledgeEvent(instance_id, current_time, response_method, user_id)

            try:
                syncer_task_instances.acknowledge(
                    conn, cache, event, org_id,
                    has_comp_perm=permissions.can_user_have_component_role(user_perm, org_perm),
                    has_team_perm=permissions.can_user_have_team_role(user_perm, org_perm),
                    org_perm=org_perm
                )

                resp = info.dsm_acknowledged
                matched = True
            except LookupError as e:
                logging.exception(str(e))
                resp = str(e)

            log = AlertLog(current_time, side=constants.incoming_side, event_method=response_method,
                           event_type=constants.acknowledge_event, organization_id=org_id, user_id=user_id,
                           sender_iso_code=user_iso, sender_phone_number=from_number, instance_id=instance_id,
                           message=body, processing_log=resp, vendor=vendor)
            AlertLogger(conn, [log]).start()

            if matched:
                return True, resp

        elif codes[1] == body:
            event = ResolveEvent(instance_id, current_time, response_method, user_id)
            try:
                syncer_task_instances.resolve(
                    conn, cache, event, org_id,
                    has_comp_perm=permissions.can_user_have_component_role(user_perm, org_perm),
                    has_team_perm=permissions.can_user_have_team_role(user_perm, org_perm),
                    org_perm=org_perm
                )
                resp = info.dsm_resolved
                matched = True
            except LookupError as e:
                logging.exception(str(e))
                resp = str(e)

            log = AlertLog(current_time, side=constants.incoming_side, event_method=response_method,
                           event_type=constants.resolve_event, organization_id=org_id, user_id=user_id,
                           sender_iso_code=user_iso, sender_phone_number=from_number, instance_id=instance_id,
                           message=body, processing_log=resp, vendor=vendor)
            AlertLogger(conn, [log]).start()

            if matched:
                return True, resp

        elif codes[2] == body:
            instance = db_task_instances.get_instances(conn, current_time, instance_id=instance_id)[instance_id]
            all_policies = []
            policy_ids_from_instances = [assignee.for_policy_id for assignee in instance.assignees]
            if len(policy_ids_from_instances) > 0:
                all_policies = db_policies.get_policies(conn, current_time, with_policyid=policy_ids_from_instances)

            allocator = EventAllocator(all_policies, None, None, None, None)
            allocator.handle_instance_escalation(current_time, instance, escalated_by=user_id,
                                                 access_method=response_method)
            if len(allocator.escalations) == 0:
                resp = errors.err_escalation_unavailable
                return False, resp
            else:
                try:
                    syncer_task_instances.escalate(
                        conn, cache, allocator.escalations, org_id=org_id,
                        has_comp_perm=permissions.can_user_have_component_role(user_perm, org_perm),
                        has_team_perm=permissions.can_user_have_team_role(user_perm, org_perm)
                    )
                    resp = info.dsm_escalated
                    matched = True
                except LookupError as e:
                    logging.exception(str(e))
                    resp = str(e)

            log = AlertLog(current_time, side=constants.incoming_side, event_method=response_method,
                           event_type=constants.escalate_event, organization_id=org_id, user_id=user_id,
                           sender_iso_code=user_iso, sender_phone_number=from_number, instance_id=instance_id,
                           message=body, processing_log=resp, vendor=vendor)
            AlertLogger(conn, [log]).start()

            if matched:
                return True, resp

        item_no += 1

    if not matched:
        log = AlertLog(current_time, side=constants.incoming_side, event_method=response_method,
                       event_type=constants.escalate_event, sender_phone_number=from_number, message=body,
                       processing_succeeded=False, processing_log=errors.err_invalid_response, vendor=vendor)
        AlertLogger(conn, [log]).start()
        return False, errors.err_invalid_response
