import logging
import requests
from celery.exceptions import MaxRetriesExceededError
from pretix.base.models import Order, OrderPosition
from pretix.base.services.tasks import ProfiledEventTask
from pretix.celery_app import app
from urllib.parse import quote as url_quote

from .helpers import matrix_room_id

logger = logging.getLogger(__name__)


@app.task(
    base=ProfiledEventTask,
    bind=True,
    max_retries=10,
    retry_backoff=True,
    retry_backoff_max=3600,
)
def matrix_inviter_invite(
    self,
    event: int,
    order: int,
    order_position: int,
    invitation_reason: str,
    room_id: str,
):
    order_position = OrderPosition.objects.get(pk=order_position)

    user_matrix_id = order_position.meta_info_data.get("question_form_data", {}).get(
        "matrix_inviter_matrix_id"
    )

    if not user_matrix_id:
        return

    if not room_id:
        return

    order = Order.objects.get(pk=order)
    server = event.settings.matrix_inviter_matrix_server
    token = event.settings.matrix_inviter_authorization_token
    room_id = matrix_room_id(server, room_id)
    payload = {"user_id": user_matrix_id}
    if invitation_reason:
        payload["reason"] = invitation_reason

    try:
        r = requests.post(
            "https://{}/_matrix/client/v3/rooms/{}/invite".format(
                url_quote(server),
                url_quote(room_id),
            ),
            headers={
                "Authorization": "Bearer {}".format(token),
            },
            json=payload,
        )
        r.raise_for_status()
    except (requests.ConnectionError, requests.HTTPError) as e:
        if r.status_code in (400, 403):
            order.log_action(
                "pretix_matrix_inviter.error",
                data={
                    "matrix_id": user_matrix_id,
                    "matrix_room": room_id,
                    "error": "HTTP Code {} ({})".format(
                        r.status_code, r.json()["error"]
                    ),
                },
            )
        else:
            try:
                if r.status_code == 429:
                    backoff = r.json()["retry_after_ms"] / 1000
                    self.retry(countdown=backoff)
                else:
                    self.retry()
            except MaxRetriesExceededError:
                order.log_action(
                    "pretix_matrix_inviter.error",
                    data={
                        "matrix_id": user_matrix_id,
                        "matrix_room": room_id,
                        "error": "HTTP Code {}".format(r.status_code),
                    },
                )
                raise e
    else:
        order.log_action(
            "pretix_matrix_inviter.invite_sent",
            data={
                "matrix_id": user_matrix_id,
                "matrix_room": room_id,
            },
        )


@app.task(
    base=ProfiledEventTask,
    bind=True,
    max_retries=10,
    retry_backoff=True,
    retry_backoff_max=3600,
)
def matrix_inviter_kick(
    self, event: int, order: int, order_position: int, room_id: str
):
    order_position = OrderPosition.objects.get(pk=order_position)

    user_matrix_id = order_position.meta_info_data.get("question_form_data", {}).get(
        "matrix_inviter_matrix_id"
    )

    if not user_matrix_id:
        return

    if not room_id:
        return

    order = Order.objects.get(pk=order)
    server = event.settings.matrix_inviter_matrix_server
    token = event.settings.matrix_inviter_authorization_token
    room_id = matrix_room_id(server, room_id)
    payload = {"user_id": user_matrix_id}

    try:
        r = requests.post(
            "https://{}/_matrix/client/v3/rooms/{}/kick".format(
                url_quote(server),
                url_quote(room_id),
            ),
            headers={
                "Authorization": "Bearer {}".format(token),
            },
            json=payload,
        )
        r.raise_for_status()
    except (requests.ConnectionError, requests.HTTPError) as e:
        if r.status_code in (403,):
            order.log_action(
                "pretix_matrix_inviter.remove_error",
                data={
                    "matrix_id": user_matrix_id,
                    "matrix_room": room_id,
                    "error": "HTTP Code {} ({})".format(
                        r.status_code, r.json()["error"]
                    ),
                },
            )
        else:
            try:
                if r.status_code == 429:
                    backoff = r.json()["retry_after_ms"] / 1000
                    self.retry(countdown=backoff)
                else:
                    self.retry()
            except MaxRetriesExceededError:
                order.log_action(
                    "pretix_matrix_inviter.remove_error",
                    data={
                        "matrix_id": user_matrix_id,
                        "matrix_room": room_id,
                        "error": "HTTP Code {}".format(r.status_code),
                    },
                )
                raise e
    else:
        order.log_action(
            "pretix_matrix_inviter.invite_rescinded",
            data={
                "matrix_id": user_matrix_id,
                "matrix_room": room_id,
            },
        )