import logging
from datetime import datetime
from logging import Handler, LogRecord
from typing import Optional

import requests
from loralogger.instances import LoggerInstances
from loralogger.producer import config, worker
from loralogger.utils import RETRY_HTTP_ADAPTER, separate_log_data


class LogToESHandler(Handler):
    def __init__(self, label: str, skip_queue: bool = False) -> None:
        super().__init__()

        # TODO: Can we make LoggerInstance takes str except for those in ENUM?
        #  It seems not the best way if people using this package also need to
        #  update this package.
        if label in LoggerInstances.values():
            label_name = label
        else:
            label_name = "other"

        self.skip_queue = skip_queue
        self.label = label_name

    def send_to_elasticsearch(self, level: str, data: dict, extra: dict) -> None:
        index_name: str = f"logs-loralogger-{self.label}"
        endpoint: str = f"{config.elasticsearch.url}/{index_name}/_doc"

        es_username = config.elasticsearch.username
        es_password = config.elasticsearch.password

        file: Optional[str] = data.get("file")
        if file:
            del data["file"]
            extra["file_content"] = file

        log_data = dict(
            id=data.get("id"),
            level=level,
            message=data.get("msg"),
            event=data.get("event"),
            extra=extra,
            path=data.get("pathname"),
            file_name=data.get("filename"),
            line_no=data.get("lineno"),
            module=data.get("module"),
            function=data.get("funcName"),
        )

        log_data["@timestamp"] = datetime.utcfromtimestamp(
            datetime.timestamp(datetime.now())
        ).isoformat()

        log_data = {k: v for k, v in log_data.items() if v}

        session = requests.Session()
        session.mount("http://", RETRY_HTTP_ADAPTER)
        # session.mount("https://", RETRY_HTTP_ADAPTER)

        if es_username or es_password:
            session.auth = (es_username, es_password)  # type: ignore

        response = session.post(
            endpoint,
            json=log_data,
        )
        response.raise_for_status()

    def send_to_logger_service(self, level: str, data: dict, extra: dict) -> None:
        worker.send_task(
            "logger.tasks.write_log",
            queue="logger",
            args=(self.label, level, data, extra),
        )

    def write_log(self, level: str, log_content: dict, extra_data: dict) -> None:
        try:
            if self.skip_queue:
                self.send_to_elasticsearch(level, log_content, extra_data)
            else:
                self.send_to_logger_service(level, log_content, extra_data)
        except Exception as e:
            logging.critical("Cannot write log")

    def emit(self, record: LogRecord) -> None:
        # get main and extra data from the log record
        main_data, extra_data = separate_log_data(record.__dict__)

        self.write_log(record.levelname, main_data, extra_data)
