#
#   Copyright © 2022 MaadiX
#   Copyright © 2022 Evilham <contact@evilham.com>
#
#   This file is part of DD
#
#   DD is free software: you can redistribute it and/or modify
#   it under the terms of the GNU Affero General Public License as published by
#   the Free Software Foundation, either version 3 of the License, or (at your
#   option) any later version.
#
#   DD is distributed in the hope that it will be useful, but WITHOUT ANY
#   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
#   FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
#   details.
#
#   You should have received a copy of the GNU Affero General Public License
#   along with DD. If not, see <https://www.gnu.org/licenses/>.
#
# SPDX-License-Identifier: AGPL-3.0-or-later

import traceback
from functools import wraps
from typing import TYPE_CHECKING, Any, Callable, Dict, Tuple

from flask import request
from jose import jwt
from werkzeug.wrappers import Response

from admin.lib.api_exceptions import Error

if TYPE_CHECKING:
    from admin.flaskapp import AdminFlaskApp


def has_jws_token(
    app: "AdminFlaskApp", *args: Any, **kwargs: Any
) -> Callable[..., Response]:
    @wraps
    def decorated(fn: Callable[..., Response]) -> Response:
        get_jws_payload(app)
        return fn(*args, **kwargs)

    return decorated


def get_jws_payload(app: "AdminFlaskApp") -> Tuple[str, Dict]:
    """
    Try to parse the Authorization header into a JWT.
    By getting the key ID from the unverified headers, try to verify the token
    with the associated key.

    For pragmatism it returns a tuple of the (key_id, jwt_claims).
    """
    kid: str = ""
    try:
        t: str = request.headers.get("Authorization", "")
        # Get which KeyId we have to use.
        # We default to 'empty', so a missing 'kid' is not an issue in an on
        # itself.
        # This enables using an empty key in app.api_3p as the default.
        # That default is better managed in AdminFlaskApp and not here.
        kid = jwt.get_unverified_headers(t).get("kid", "")
    except:
        raise Error(
            "unauthorized", "Token is missing or malformed", traceback.format_stack()
        )
    try:
        # Try to get payload
        return (kid, app.api_3p[kid].verify_incoming_data(t))
    except:
        raise Error("forbidden", "Data verification failed", traceback.format_stack())
