import asyncio
import os
import traceback
from datetime import datetime


from api.middleware.request import get_request
from core.environment import Environment


class Logger:
    _level = "info"
    _levels = ["trace", "debug", "info", "warning", "error", "fatal"]

    # ANSI color codes for different log levels
    COLORS = {
        "trace": "\033[37m",  # Light Gray
        "debug": "\033[94m",  # Blue
        "info": "\033[92m",  # Green
        "warning": "\033[93m",  # Yellow
        "error": "\033[91m",  # Red
        "fatal": "\033[95m",  # Magenta
        "reset": "\033[0m",  # Reset color
    }

    def __init__(self, source: str, file_prefix: str = None):
        self.source = source

        if file_prefix is None:
            file_prefix = "app"

        self._file_prefix = file_prefix
        self._create_log_dir()

    @property
    def log_file(self):
        return f"logs/{self._file_prefix}_{datetime.now().strftime('%Y-%m-%d')}.log"

    @staticmethod
    def _create_log_dir():
        if os.path.exists("logs"):
            return

        os.makedirs("logs")

    def _ensure_file_size(self):
        log_file = self.log_file
        # if exists and size is greater than 300MB, create a new file
        if os.path.exists(log_file) and os.path.getsize(log_file) > 300 * 1024 * 1024:
            os.rename(
                log_file,
                f"{log_file.split('.log')[0]}_{datetime.now().strftime('%H-%M-%S')}.log",
            )

    @classmethod
    def set_level(cls, level: str):
        if level.lower() in cls._levels:
            cls._level = level.lower()
        else:
            raise ValueError(f"Invalid log level: {level}")

    def _get_structured_message(self, level: str, timestamp: str, messages: str) -> str:
        structured_message = {
            "timestamp": timestamp,
            "level": level.upper(),
            "source": self.source,
            "messages": messages,
        }

        request = get_request()

        if request is not None:
            structured_message["request"] = {
                "url": str(request.url),
                "method": request.method if request.scope == "http" else "ws",
                "data": (
                    asyncio.create_task(request.body())
                    if request.scope == "http"
                    else None
                ),
            }
        return str(structured_message)

    def _write_log_to_file(self, content: str):
        self._ensure_file_size()
        with open(self.log_file, "a") as log_file:
            log_file.write(content + "\n")
            log_file.close()

    def _log(self, level: str, *messages):
        try:
            if self._levels.index(level) < self._levels.index(self._level):
                return

            messages = [str(message) for message in messages]
            timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")
            formatted_message = f"<{timestamp}> [{level.upper():^7}] [{self._file_prefix:^5}] - [{self.source}]: {' '.join(messages)}"

            if Environment.get("STRUCTURED_LOGGING", bool, False):
                self._write_log_to_file(
                    self._get_structured_message(level, timestamp, " ".join(messages))
                )
            else:
                self._write_log_to_file(formatted_message)

            print(
                f"{self.COLORS.get(level, self.COLORS["reset"])}{formatted_message}{self.COLORS["reset"]}"
            )
        except Exception as e:
            print(f"Error while logging: {e} -> {traceback.format_exc()}")

    def trace(self, *messages):
        self._log("trace", *messages)

    def debug(self, *messages):
        self._log("debug", *messages)

    def info(self, *messages):
        self._log("info", *messages)

    def warning(self, *messages):
        self._log("warning", *messages)

    def error(self, message, e: Exception):
        self._log("error", message, f"{e} -> {traceback.format_exc()}")

    def fatal(self, message, e: Exception, prevent_quit: bool = False):
        self._log("fatal", message, f"{e} -> {traceback.format_exc()}")
        if not prevent_quit:
            exit(-1)


class DBLogger(Logger):

    def __init__(self, source: str):
        Logger.__init__(self, source, "db")


class APILogger(Logger):

    def __init__(self, source: str):
        Logger.__init__(self, source, "api")


class MailLogger(Logger):

    def __init__(self, source: str):
        Logger.__init__(self, source, "mail")
