# By: Riasat Ullah
# This file contains all slack integration related views.

from dbqueries import db_policies, db_services, db_workflows
from dbqueries.integrations import db_slack
from integrations import slack
from modules.router import Router
from objects.task_payload import TaskPayload
from rest_framework.decorators import api_view
from rest_framework.response import Response
from translators import label_translator as _lt
from utils import constants, errors, helpers, info, key_manager, label_names as lnm, logging, times, var_names
from utils.db_connection import CACHE_CLIENT, CONN_POOL
from validations import request_validator
import configuration
import json


@api_view(['POST'])
def process_slack_commands(request, conn=None, cache=None):
    '''
    Handles a command received from TaskCall's Slack app.
    Calls to this function come from requests made directly from Slack.
    :param request: Http request
    :param conn: db connection
    :param cache: cache client
    :return: Http response
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        valid_instructions = [slack.slack_verify_user_cmd, slack.slack_remove_user_cmd,
                              slack.slack_get_user_cmd, slack.slack_create_incident_cmd, slack.slack_help_cmd]
        command = request.data['command']
        text_list = request.data['text'].split()
        instruction = text_list[0]

        slack_team_id = request.POST['team_id']
        slack_user_id = request.POST['user_id']
        slack_channel_id = request.POST['channel_id']
        slack_response_url = request.POST['response_url']
        try:
            conn = CONN_POOL.get_db_conn() if conn is None else conn
            cache = CACHE_CLIENT if cache is None else cache

            # Error messages are being sent with 200 status because otherwise Slack requires that as a form of
            # acknowledgment that the message was received successfully. The body of the message can contain the error.
            if len(text_list) == 0:
                return Response(_lt.get_label(errors.err_invalid_request, constants.lang_en), status=200)
            else:
                instruction = text_list[0]

                if command != slack.slack_command or instruction not in valid_instructions:
                    return Response(slack.unknown_command_error, status=200)
                else:
                    if instruction == slack.slack_help_cmd:
                        return Response(_lt.get_label(info.msg_slack_help, lang), status=200)
                    else:
                        return Response(status=200)
        except Exception as e:
            logging.exception(str(e))
            return Response(_lt.get_label(errors.err_system_error, lang), status=200)
        finally:
            if instruction == slack.slack_verify_user_cmd:
                if len(text_list) != 2:
                    return Response(_lt.get_label(errors.err_invalid_request, constants.lang_en), status=200)
                taskcall_pref_name = text_list[1]
                slack.SlackUserVerifier(conn, slack_team_id, slack_channel_id, slack_user_id,
                                        taskcall_pref_name, slack_response_url).start()

            elif instruction == slack.slack_remove_user_cmd:
                slack.SlackUserRemover(conn, slack_team_id, slack_channel_id, slack_user_id,
                                       slack_response_url).start()

            elif instruction == slack.slack_get_user_cmd:
                slack.SlackUserInfo(conn, slack_team_id, slack_channel_id, slack_user_id,
                                    slack_response_url).start()

            elif instruction == slack.slack_create_incident_cmd:
                data = {'text': _lt.get_label(errors.err_unknown_resource, lang), 'replace_original': True}
                timestamp = times.get_current_timestamp()
                try:
                    org_id, service_id, integ_id, acc_token, slack_hook, external_info = \
                        db_slack.get_slack_integration_details(conn, timestamp, slack_team_id,
                                                               slack_channel_id)

                    if len(text_list) < 2:
                        data = slack.get_create_incident_button_content(org_id, acc_token, lang)
                        data['replace_original'] = True
                    else:
                        incident_title = ' '.join(text_list[1:])

                        if service_id is not None and external_info is not None \
                                and var_names.users in external_info \
                                and slack_user_id in external_info[var_names.users]:
                            payload = TaskPayload(timestamp, org_id, timestamp.date(), incident_title,
                                                  configuration.standard_timezone, timestamp.time(),
                                                  trigger_method=constants.integrations_api,
                                                  service_id=service_id, integration_id=integ_id)
                            Router(conn, cache, payload).start()

                            data = {'text': _lt.get_label(info.msg_incident_created, lang),
                                    'replace_original': True}
                except Exception as e:
                    logging.exception(str(e))
                    data = {'text': _lt.get_label(errors.err_system_error, lang), 'replace_original': True}
                finally:
                    helpers.post_api_request(slack_response_url, data, get_status_only=True)

            CONN_POOL.put_db_conn(conn)


@api_view(['POST'])
def process_slack_actions(request, conn=None, cache=None):
    '''
    Handles user actions taken on messages from TaskCall's Slack app.
    Calls to this function come from requests made directly from Slack.
    :param request: Http request
    :param conn: db connection
    :param cache: cache client
    :return: Http response
    '''
    if request.method == 'POST':
        lang = request_validator.get_user_language(request)
        accepted_direct_actions = [constants.acknowledge_event, constants.resolve_event, constants.escalate_event]
        accepted_modal_types = [slack.open_modal_create_incident, slack.open_modal_notate, slack.open_modal_reassign,
                                slack.open_modal_add_responders, slack.open_modal_run_workflow,
                                slack.open_modal_status_update]
        payload = json.loads(request.data['payload'])
        action_type = payload['type']
        try:
            conn = CONN_POOL.get_db_conn() if conn is None else conn
            cache = CACHE_CLIENT if cache is None else cache
            return Response(status=200)
        except Exception as e:
            logging.exception(str(e))
            return Response(_lt.get_label(errors.err_system_error, lang), status=200)
        finally:
            if action_type == 'block_actions':
                trigger_id = payload['trigger_id']
                metadata = payload['actions'][0]['block_id']
                action = payload['actions'][0]['selected_option']['value']\
                    if payload['actions'][0]['type'] == 'overflow' else payload['actions'][0]['value']
                slack_team_id = payload['team']['id']
                slack_channel_id = payload['channel']['id']
                slack_user_id = payload['user']['id']
                slack_username = payload['user']['username']
                slack_response_url = payload['response_url']

                meta_list = key_manager.decode_general_identifier(metadata)
                acc_token = meta_list[0]
                org_id = int(meta_list[1])
                if len(meta_list) > 2:
                    inst_id, org_inst_id = int(meta_list[2]), int(meta_list[3])
                    new_metadata = slack_channel_id + ' ' + key_manager.encode_general_identifier(meta_list[2:])
                else:
                    inst_id, org_inst_id = None, None
                    new_metadata = slack_channel_id

                # Handle the modal trigger requests first.
                if action in accepted_modal_types:
                    if action == slack.open_modal_notate:
                        slack.open_modal_view(
                            acc_token, trigger_id,
                            slack.textbox_modal_view(
                                constants.notate_event, new_metadata, _lt.get_label(lnm.ttl_add_note, lang),
                                _lt.get_label(lnm.ttl_add_note, lang), lang=lang
                            )
                        )

                    elif action == slack.open_modal_status_update:
                        slack.open_modal_view(
                            acc_token, trigger_id,
                            slack.textbox_modal_view(
                                constants.status_update_event, new_metadata,
                                _lt.get_label(lnm.ttl_status_updates, lang),
                                _lt.get_label(lnm.ttl_new_update, lang), lang=lang
                            )
                        )

                    elif action == slack.open_modal_reassign:
                        content = slack.select_modal_view(
                            constants.reassign_event, new_metadata, _lt.get_label(lnm.ttl_reassign, lang),
                            _lt.get_label(lnm.ttl_reassign, lang), [], lang=lang
                        )
                        view_id, hash_val = slack.open_modal_view(acc_token, trigger_id, content)
                        if view_id is not None and hash_val is not None:
                            pol_list = db_policies.get_basic_policies_list(
                                conn, times.get_current_timestamp(), org_id, group_only=False)
                            content = slack.select_modal_view(
                                constants.reassign_event, new_metadata, _lt.get_label(lnm.ttl_reassign, lang),
                                _lt.get_label(lnm.ttl_reassign, lang), pol_list, lang=lang
                            )
                            slack.update_modal_view(acc_token, view_id, hash_val, content)

                    elif action == slack.open_modal_add_responders:
                        content = slack.select_modal_view(
                            constants.add_responders_event, new_metadata, _lt.get_label(lnm.ttl_add_responders, lang),
                            _lt.get_label(lnm.ttl_add_responders, lang), [], lang=lang
                        )
                        view_id, hash_val = slack.open_modal_view(acc_token, trigger_id, content)
                        if view_id is not None and hash_val is not None:
                            pol_list = db_policies.get_basic_policies_list(
                                conn, times.get_current_timestamp(), org_id, group_only=False)
                            content = slack.select_modal_view(
                                constants.add_responders_event, new_metadata,
                                _lt.get_label(lnm.ttl_add_responders, lang),
                                _lt.get_label(lnm.ttl_add_responders, lang), pol_list, lang=lang
                            )
                            slack.update_modal_view(acc_token, view_id, hash_val, content)

                    elif action == slack.open_modal_run_workflow:
                        content = slack.select_modal_view(
                            constants.run_workflow_event, new_metadata,
                            _lt.get_label(lnm.ttl_run_workflow, lang),
                            _lt.get_label(lnm.ttl_run_workflow, lang), [], is_multi=False, lang=lang
                        )
                        view_id, hash_val = slack.open_modal_view(acc_token, trigger_id, content)
                        if view_id is not None and hash_val is not None:
                            workflow_list = db_workflows.get_basic_workflows_list(
                                conn, times.get_current_timestamp(), org_id)
                            content = slack.select_modal_view(
                                constants.run_workflow_event, new_metadata,
                                _lt.get_label(lnm.ttl_run_workflow, lang),
                                _lt.get_label(lnm.ttl_run_workflow, lang),
                                workflow_list, is_multi=False, lang=lang
                            )
                            slack.update_modal_view(acc_token, view_id, hash_val, content)

                    elif action == slack.open_modal_create_incident:
                        content = slack.create_incident_modal_content(new_metadata, [], lang=lang)
                        view_id, hash_val = slack.open_modal_view(acc_token, trigger_id, content)
                        if view_id is not None and hash_val is not None:
                            srv_list = db_services.get_basic_services_list(conn, times.get_current_timestamp(), org_id)
                            content = slack.create_incident_modal_content(new_metadata, srv_list, lang=lang)
                            slack.update_modal_view(acc_token, view_id, hash_val, content)

                elif action in accepted_direct_actions:
                    slack.SlackIncidentActionProcessor(
                        conn, inst_id, org_inst_id, action, slack_team_id, slack_channel_id, slack_user_id,
                        slack_username, slack_response_url, None, lang=lang).start()

            elif action_type == 'view_submission':
                action = payload['view']['callback_id']
                meta_list = payload['view']['private_metadata'].split()
                slack_team_id = payload['team']['id']
                slack_channel_id = meta_list[0]
                slack_user_id = payload['user']['id']
                slack_username = payload['user']['username']
                input_values = payload['view']['state']['values']

                if action == constants.trigger_event:
                    timestamp = times.get_current_timestamp()
                    org_id, service_id, integ_id, acc_token, slack_hook, external_info = \
                        db_slack.get_slack_integration_details(conn, timestamp, slack_team_id, slack_channel_id)

                    if service_id is not None and external_info is not None \
                            and var_names.users in external_info \
                            and slack_user_id in external_info[var_names.users]:
                        title = input_values[var_names.title][var_names.title]['value']
                        sel_srv = input_values[var_names.service][var_names.service]['selected_option']['value']
                        sel_urg = int(input_values[var_names.urgency_level][var_names.urgency_level][
                            'selected_option']['value'])
                        desc = input_values[var_names.text_msg][var_names.text_msg]['value']

                        payload = TaskPayload(timestamp, org_id, timestamp.date(), title,
                                              configuration.standard_timezone, timestamp.time(),
                                              text_msg=desc, urgency_level=sel_urg,
                                              trigger_method=constants.integrations_api,
                                              service_ref_id=sel_srv, integration_id=integ_id)
                        Router(conn, cache, payload).start()
                        data = {'text': _lt.get_label(info.msg_incident_created, lang)}
                        helpers.post_api_request(slack_hook, data, get_status_only=True)
                else:

                    inst_id, org_inst_id = [int(x) for x in key_manager.decode_general_identifier(meta_list[1])]
                    slack.SlackIncidentActionProcessor(
                        conn, inst_id, org_inst_id, action, slack_team_id, slack_channel_id, slack_user_id,
                        slack_username, None, input_values, lang=lang).start()

            CONN_POOL.put_db_conn(conn)
