from flask import current_app, g, jsonify, request
from flask_cors import cross_origin

from alerta.app import custom_webhooks
from alerta.auth.decorators import permission
from alerta.exceptions import (AlertaException, ApiError, BlackoutPeriod,
                               ForwardingLoop, HeartbeatReceived, RateLimit,
                               RejectException)
from alerta.models.alert import Alert
from alerta.models.enums import Scope
from alerta.utils.api import assign_customer, process_alert
from alerta.utils.audit import write_audit_trail

from . import webhooks


@webhooks.route('/webhooks/<webhook>', defaults={'path': ''}, methods=['OPTIONS', 'GET', 'POST'])
@webhooks.route('/webhooks/<webhook>/<path:path>', methods=['OPTIONS', 'GET', 'POST'])
@cross_origin()
@permission(Scope.write_webhooks)
def custom(webhook, path):
    if webhook not in custom_webhooks.webhooks:
        raise ApiError(f"Custom webhook '{webhook}' not found.", 404)

    try:
        rv = custom_webhooks.webhooks[webhook].incoming(
            path=path or request.path,
            query_string=request.args,
            payload=request.get_json(silent=True) or request.form or request.get_data(as_text=True)
        )
    except TypeError:
        rv = custom_webhooks.webhooks[webhook].incoming(
            query_string=request.args,
            payload=request.get_json(silent=True) or request.form or request.get_data(as_text=True)
        )
    except AlertaException as e:
        raise ApiError(e.message, code=e.code, errors=e.errors)
    except Exception as e:
        raise ApiError(str(e), 400)

    if isinstance(rv, Alert):
        rv = [rv]

    if isinstance(rv, list):
        alerts = []
        for alert in rv:
            alert.customer = assign_customer(wanted=alert.customer)

            def audit_trail_alert(event: str):
                write_audit_trail.send(current_app._get_current_object(), event=event, message=alert.text, user=g.login,  # type: ignore
                                       customers=g.customers, scopes=g.scopes, resource_id=alert.id, type='alert',
                                       request=request)

            try:
                alert = process_alert(alert)
            except RejectException as e:
                audit_trail_alert(event='alert-rejected')
                raise ApiError(str(e), 403)
            except RateLimit as e:
                audit_trail_alert(event='alert-rate-limited')
                return jsonify(status='error', message=str(e), id=alert.id), 429
            except HeartbeatReceived as heartbeat:
                audit_trail_alert(event='alert-heartbeat')
                return jsonify(status='ok', message=str(heartbeat), id=heartbeat.id), 202
            except BlackoutPeriod as e:
                audit_trail_alert(event='alert-blackout')
                return jsonify(status='ok', message=str(e), id=alert.id), 202
            except ForwardingLoop as e:
                return jsonify(status='ok', message=str(e)), 202
            except AlertaException as e:
                raise ApiError(e.message, code=e.code, errors=e.errors)
            except Exception as e:
                raise ApiError(str(e), 500)

            text = f'alert received via {webhook} webhook'
            write_audit_trail.send(current_app._get_current_object(), event='webhook-received', message=text,
                                   user=g.login, customers=g.customers, scopes=g.scopes, resource_id=alert.id,
                                   type='alert', request=request)
            alerts.append(alert)

        if len(alerts) == 1:
            return jsonify(status='ok', id=alerts[0].id, alert=alerts[0].serialize), 201
        else:
            return jsonify(status='ok', ids=[alert.id for alert in alerts]), 201

    else:
        text = f'request received via {webhook} webhook'
        write_audit_trail.send(current_app._get_current_object(), event='webhook-received', message=text,
                               user=g.login, customers=g.customers, scopes=g.scopes, resource_id=None,
                               type='user-defined', request=request)
        return rv
