# -*- coding: utf-8 -*-

# Copyright 2010 - 2017 RhodeCode GmbH and the AppEnlight project authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from datetime import timedelta

from appenlight.lib.enums import LogLevelPython, ParsedSentryEventType

EXCLUDED_LOG_VARS = [
    "args",
    "asctime",
    "created",
    "exc_info",
    "exc_text",
    "filename",
    "funcName",
    "levelname",
    "levelno",
    "lineno",
    "message",
    "module",
    "msecs",
    "msg",
    "name",
    "pathname",
    "process",
    "processName",
    "relativeCreated",
    "thread",
    "threadName",
]

EXCLUDE_SENTRY_KEYS = [
    "csp",
    "culprit",
    "event_id",
    "exception",
    "extra",
    "level",
    "logentry",
    "logger",
    "message",
    "modules",
    "platform",
    "query",
    "release",
    "request",
    "sentry.interfaces.Csp",
    "sentry.interfaces.Exception",
    "sentry.interfaces.Http",
    "sentry.interfaces.Message",
    "sentry.interfaces.Query",
    "sentry.interfaces.Stacktrace",
    "sentry.interfaces.Template",
    "sentry.interfaces.User",
    "sentry.interfaces.csp.Csp",
    "sentry.interfaces.exception.Exception",
    "sentry.interfaces.http.Http",
    "sentry.interfaces.message.Message",
    "sentry.interfaces.query.Query",
    "sentry.interfaces.stacktrace.Stacktrace",
    "sentry.interfaces.template.Template",
    "sentry.interfaces.user.User",
    "server_name",
    "stacktrace",
    "tags",
    "template",
    "time_spent",
    "timestamp",
    "user",
]


def get_keys(list_of_keys, json_body):
    for k in list_of_keys:
        if k in json_body:
            return json_body[k]


def get_logentry(json_body):
    key_names = [
        "logentry",
        "sentry.interfaces.message.Message",
        "sentry.interfaces.Message",
    ]
    logentry = get_keys(key_names, json_body)
    return logentry


def get_exception(json_body):
    parsed_exception = {}
    key_names = [
        "exception",
        "sentry.interfaces.exception.Exception",
        "sentry.interfaces.Exception",
    ]
    exception = get_keys(key_names, json_body) or {}
    if exception:
        if isinstance(exception, dict):
            exception = exception["values"][0]
        else:
            exception = exception[0]

    parsed_exception["type"] = exception.get("type")
    parsed_exception["value"] = exception.get("value")
    parsed_exception["module"] = exception.get("module")
    parsed_stacktrace = get_stacktrace(exception) or {}
    parsed_exception = exception or {}
    return parsed_exception, parsed_stacktrace


def get_stacktrace(json_body):
    parsed_stacktrace = []
    key_names = [
        "stacktrace",
        "sentry.interfaces.stacktrace.Stacktrace",
        "sentry.interfaces.Stacktrace",
    ]
    stacktrace = get_keys(key_names, json_body)
    if stacktrace:
        for frame in stacktrace["frames"]:
            parsed_stacktrace.append(
                {
                    "cline": frame.get("context_line", ""),
                    "file": frame.get("filename", ""),
                    "module": frame.get("module", ""),
                    "fn": frame.get("function", ""),
                    "line": frame.get("lineno", ""),
                    "vars": list(frame.get("vars", {}).items()),
                }
            )
    return parsed_stacktrace


def get_template(json_body):
    parsed_template = {}
    key_names = [
        "template",
        "sentry.interfaces.template.Template",
        "sentry.interfaces.Template",
    ]
    template = get_keys(key_names, json_body)
    if template:
        for frame in template["frames"]:
            parsed_template.append(
                {
                    "cline": frame.get("context_line", ""),
                    "file": frame.get("filename", ""),
                    "fn": "",
                    "line": frame.get("lineno", ""),
                    "vars": [],
                }
            )

    return parsed_template


def get_request(json_body):
    parsed_http = {}
    key_names = ["request", "sentry.interfaces.http.Http", "sentry.interfaces.Http"]
    http = get_keys(key_names, json_body) or {}
    for k, v in http.items():
        if k == "headers":
            parsed_http["headers"] = {}
            for sk, sv in http["headers"].items():
                parsed_http["headers"][sk.title()] = sv
        else:
            parsed_http[k.lower()] = v
    return parsed_http


def get_user(json_body):
    parsed_user = {}
    key_names = ["user", "sentry.interfaces.user.User", "sentry.interfaces.User"]
    user = get_keys(key_names, json_body)
    if user:
        parsed_user["id"] = user.get("id")
        parsed_user["username"] = user.get("username")
        parsed_user["email"] = user.get("email")
        parsed_user["ip_address"] = user.get("ip_address")

    return parsed_user


def get_query(json_body):
    query = None
    key_name = ["query", "sentry.interfaces.query.Query", "sentry.interfaces.Query"]
    query = get_keys(key_name, json_body)
    return query


def parse_sentry_event(json_body):
    request_id = json_body.get("event_id")

    # required
    message = json_body.get("message")
    log_timestamp = json_body.get("timestamp")
    level = json_body.get("level")
    if isinstance(level, int):
        level = LogLevelPython.key_from_value(level)

    namespace = json_body.get("logger")
    language = json_body.get("platform")

    # optional
    server_name = json_body.get("server_name")
    culprit = json_body.get("culprit")
    release = json_body.get("release")

    tags = json_body.get("tags", {})
    if hasattr(tags, "items"):
        tags = list(tags.items())
    extra = json_body.get("extra", {})
    if hasattr(extra, "items"):
        extra = list(extra.items())

    parsed_req = get_request(json_body)
    user = get_user(json_body)
    template = get_template(json_body)
    query = get_query(json_body)

    # other unidentified keys found
    other_keys = [
        (k, json_body[k]) for k in json_body.keys() if k not in EXCLUDE_SENTRY_KEYS
    ]

    logentry = get_logentry(json_body)
    if logentry:
        message = logentry["message"]

    exception, stacktrace = get_exception(json_body)

    alt_stacktrace = get_stacktrace(json_body)
    event_type = None
    if not exception and not stacktrace and not alt_stacktrace and not template:
        event_type = ParsedSentryEventType.LOG

        event_dict = {
            "log_level": level,
            "message": message,
            "namespace": namespace,
            "request_id": request_id,
            "server": server_name,
            "date": log_timestamp,
            "tags": tags,
        }
        event_dict["tags"].extend(
            [(k, v) for k, v in extra if k not in EXCLUDED_LOG_VARS]
        )

        # other keys can be various object types
        event_dict["tags"].extend([(k, v) for k, v in other_keys if isinstance(v, str)])
        if culprit:
            event_dict["tags"].append(("sentry_culprit", culprit))
        if language:
            event_dict["tags"].append(("sentry_language", language))
        if release:
            event_dict["tags"].append(("sentry_release", release))

    if exception or stacktrace or alt_stacktrace or template:
        event_type = ParsedSentryEventType.ERROR_REPORT
        event_dict = {
            "client": "sentry",
            "error": message,
            "namespace": namespace,
            "request_id": request_id,
            "server": server_name,
            "start_time": log_timestamp,
            "end_time": None,
            "tags": tags,
            "extra": extra,
            "language": language,
            "view_name": json_body.get("culprit"),
            "http_status": None,
            "username": None,
            "url": parsed_req.get("url"),
            "ip": None,
            "user_agent": None,
            "request": None,
            "slow_calls": None,
            "request_stats": None,
            "traceback": None,
        }

        event_dict["extra"].extend(other_keys)
        if release:
            event_dict["tags"].append(("sentry_release", release))
        event_dict["request"] = parsed_req
        if "headers" in parsed_req:
            event_dict["user_agent"] = parsed_req["headers"].get("User-Agent")
        if "env" in parsed_req:
            event_dict["ip"] = parsed_req["env"].get("REMOTE_ADDR")
        ts_ms = int(json_body.get("time_spent") or 0)
        if ts_ms > 0:
            event_dict["end_time"] = event_dict["start_time"] + timedelta(
                milliseconds=ts_ms
            )
        if stacktrace or alt_stacktrace or template:
            event_dict["traceback"] = stacktrace or alt_stacktrace or template
        for k in list(event_dict.keys()):
            if event_dict[k] is None:
                del event_dict[k]
        if user:
            event_dict["username"] = user["username"] or user["id"] or user["email"]
    return event_dict, event_type
