import datetime
import logging
import traceback
from dataclasses import dataclass

from elasticsearch import Elasticsearch


@dataclass
class ElasticLoggerConfig:
    hosts: str
    api_key: str
    index_name: str
    application_name: str
    cert_path: str = "./logger/elasticsearch-ca.crt"


def format_time(record: logging.LogRecord, datefmt: str = None) -> str:
    dt = datetime.datetime.fromtimestamp(record.created, datetime.timezone.utc)
    return dt.strftime(datefmt or "%Y-%m-%dT%H:%M:%SZ")


class LogHandler(logging.Handler):
    def __init__(self, config: ElasticLoggerConfig):
        super().__init__()
        self.client = Elasticsearch(
            hosts=[host.strip() for host in config.hosts.split(",")],
            api_key=config.api_key,
            verify_certs=False,
            ca_certs=config.cert_path
        )
        self.index_name = config.index_name
        self.application_name = config.application_name

        if not self.client.indices.exists(index=self.index_name):
            self.client.indices.create(
                index=self.index_name,
                body={
                    "settings": {"number_of_shards": 1, "number_of_replicas": 1},
                    "mappings": {
                        "properties": {
                            "timestamp": {"type": "date"},
                            "level": {"type": "keyword"},
                            "message": {"type": "text"},
                            "logger": {"type": "keyword"},
                            "filename": {"type": "keyword"},
                            "line": {"type": "integer"},
                            "function": {"type": "keyword"},
                            "application_name": {"type": "keyword"},
                            "stack_trace": {"type": "text"},
                        }
                    }
                }
            )

    def emit(self, record: logging.LogRecord):
        stack_trace = None
        if record.exc_info:
            stack_trace = ''.join(traceback.format_exception(*record.exc_info))

        log_entry = {
            "timestamp": format_time(record),
            "level": record.levelname,
            "message": record.getMessage(),
            "logger": record.name,
            "filename": record.pathname,
            "line": record.lineno,
            "function": record.funcName,
            "application_name": self.application_name,
            "stack_trace": stack_trace,
        }

        try:
            self.client.index(index=self.index_name, body=log_entry)
        except Exception as e:
            print(f"Failed to send log to Elasticsearch: {e}")


def create_logger(config: ElasticLoggerConfig) -> logging.Logger:
    logger = logging.getLogger("ElasticLogger")
    logger.setLevel(logging.INFO)

    if not logger.handlers:
        es_handler = LogHandler(config)
        logger.addHandler(es_handler)

        console_handler = logging.StreamHandler()
        console_handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))
        logger.addHandler(console_handler)

    return logger
