import datetime
import os
import re
from colorama import Fore, Style, init
from .enums import LogLevel

init(autoreset=False)


class Logger:
    """
    Base Logger factory class.

    Usage:
        log = Logger()                        # ColorLogger (default)
        log = Logger(style=2)                 # SimpleLogger
        log = Logger(prefix="myapp")          # Custom prefix
        log = Logger(level=LogLevel.WARNING)  # Only WARNING and above
        log = Logger(log_file="logs/app.log") # Save logs to file
    """

    def __new__(cls, style: int = 1, *args, **kwargs):
        if cls is Logger:
            if style == 2:
                return super().__new__(SimpleLogger)
            return super().__new__(ColorLogger)
        return super().__new__(cls)

    def __init__(
        self,
        style: int = 1,
        prefix: str | None = "karenhoyoshi.asia",
        github_repository: str | None = None,
        level: LogLevel = LogLevel.DEBUG,
        log_file: str | None = None,
    ):
        self.level = level
        self.repo_url = github_repository
        self.log_file = log_file
        self.prefix = prefix

        if log_file:
            os.makedirs(os.path.dirname(log_file), exist_ok=True) if os.path.dirname(log_file) else None
            self._write_to_log(f"=== Logging started at {datetime.datetime.now()} ===\n")

    # ── Helpers ──────────────────────────────────────────────────────────────

    def get_time(self) -> str:
        return datetime.datetime.now().strftime("%H:%M:%S")

    def _should_log(self, message_level: LogLevel) -> bool:
        return message_level.value >= self.level.value

    def _strip_ansi(self, text: str) -> str:
        ansi_escape = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])")
        return ansi_escape.sub("", text)

    def _write_to_log(self, message: str) -> None:
        if self.log_file:
            try:
                with open(self.log_file, "a", encoding="utf-8") as f:
                    f.write(self._strip_ansi(message) + "\n")
            except Exception as e:
                print(f"Error writing to log file: {e}")

    def _extract_github_username(self, url: str) -> str | None:
        url = url.replace("https://", "").replace("http://", "").replace("www.", "")
        patterns = [
            r"^github\.com/([^/]+)(?:/.*)?$",
            r"^([^/]+)(?:/.*)?$",
            r"^@(.+)$",
        ]
        for pattern in patterns:
            if match := re.search(pattern, url):
                return match.group(1).rstrip("/ \t\n\r")
        return None

    def display_repo_info(self):
        if self.repo_url:
            username = self._extract_github_username(self.repo_url)
            if username:
                self.info(f"Developed by {username} - {self.repo_url}")
            else:
                self.info(f"GitHub Repository: {self.repo_url}")

    # ── Abstract-like methods (overridden by subclasses) ──────────────────────

    def info(self, message: str, start=None, end=None): ...
    def debug(self, message: str, start=None, end=None): ...
    def warning(self, message: str, start=None, end=None): ...
    def success(self, message: str, start=None, end=None): ...
    def failure(self, message: str, start=None, end=None): ...
    def error(self, message: str, start=None, end=None): ...
    def critical(self, message: str, start=None, end=None, exit_code: int = 1): ...
    def question(self, message: str) -> str: ...


# ── ColorLogger ───────────────────────────────────────────────────────────────

class ColorLogger(Logger):
    """Full-color logger with timestamp and styled prefix."""

    # ANSI color constants
    WHITE          = "\u001b[37m"
    MAGENTA        = "\033[38;5;97m"
    BRIGHT_MAGENTA = "\033[38;2;157;38;255m"
    LIGHT_CORAL    = "\033[38;5;210m"
    RED            = "\033[38;5;196m"
    GREEN          = "\033[38;5;40m"
    YELLOW         = "\033[38;5;220m"
    BLUE           = "\033[38;5;21m"
    PINK           = "\033[38;5;176m"
    CYAN           = "\033[96m"

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        raw_prefix = self.prefix or ""
        self.prefix = f"{self.PINK}[{self.MAGENTA}{raw_prefix}{self.PINK}] " if raw_prefix else f"{self.PINK}"
        self.display_repo_info()

    # ── Internal formatters ───────────────────────────────────────────────────

    def _timer_str(self, start, end) -> str:
        if start is not None and end is not None:
            elapsed = str(float(end) - float(start))[:5]
            return f" {self.BRIGHT_MAGENTA}In{self.WHITE} -> {self.BRIGHT_MAGENTA}{elapsed} Seconds{Fore.RESET}"
        return ""

    def _base(self, level_str: str, message_str: str, start=None, end=None) -> str:
        t = self.get_time()
        timer = self._timer_str(start, end)
        return (
            f"{self.prefix}"
            f"[{self.BRIGHT_MAGENTA}{t}{self.PINK}] "
            f"{self.PINK}[{level_str}{self.PINK}] -> "
            f"{message_str}{Fore.RESET}"
            f"{timer}"
        )

    def _print_and_log(self, msg: str):
        print(msg)
        self._write_to_log(msg)

    # ── Public log methods ────────────────────────────────────────────────────

    def info(self, message: str, start=None, end=None) -> None:
        if self._should_log(LogLevel.INFO):
            msg = self._base(f"{Fore.BLUE}!", f"{self.CYAN}{message}", start, end)
            self._print_and_log(msg)

    def debug(self, message: str, start=None, end=None) -> None:
        if self._should_log(LogLevel.DEBUG):
            msg = self._base(f"{Fore.YELLOW}DEBUG", f"{self.GREEN}{message}", start, end)
            self._print_and_log(msg)

    def success(self, message: str, start=None, end=None, level: str = "Success") -> None:
        if self._should_log(LogLevel.SUCCESS):
            msg = self._base(f"{self.GREEN}{level}", f"{self.GREEN}{message}", start, end)
            self._print_and_log(msg)

    def failure(self, message: str, start=None, end=None, level: str = "Failure") -> None:
        if self._should_log(LogLevel.FAILURE):
            msg = self._base(f"{self.RED}{level}", f"{self.RED}{message}", start, end)
            self._print_and_log(msg)

    def error(self, message: str, start=None, end=None, level: str = "Error") -> None:
        if self._should_log(LogLevel.FAILURE):
            msg = self._base(f"{self.RED}{level}", f"{self.RED}{message}", start, end)
            self._print_and_log(msg)

    def warning(self, message: str, start=None, end=None, level: str = "Warning") -> None:
        if self._should_log(LogLevel.WARNING):
            msg = self._base(f"{self.YELLOW}{level}", f"{self.YELLOW}{message}", start, end)
            self._print_and_log(msg)

    def critical(self, message: str, start=None, end=None, level: str = "CRITICAL", exit_code: int = 1) -> None:
        if self._should_log(LogLevel.CRITICAL):
            t = self.get_time()
            timer = self._timer_str(start, end)
            msg = (
                f"{self.prefix}"
                f"[{self.BRIGHT_MAGENTA}{t}{self.PINK}]{Fore.RESET} "
                f"{self.PINK}[{self.RED}{level}{self.PINK}] -> "
                f"{self.LIGHT_CORAL}{message}{Fore.RESET}{timer}"
            )
            print(msg)
            input()
            self._write_to_log(msg)
            self._write_to_log(f"=== Program terminated with exit code {exit_code} at {datetime.datetime.now()} ===")
            exit(exit_code)

    def question(self, message: str) -> str:
        t = self.get_time()
        prompt = (
            f"{self.prefix}"
            f"[{self.BRIGHT_MAGENTA}{t}{self.PINK}]{Fore.RESET} "
            f"{self.PINK}[{Fore.BLUE}?{self.PINK}] ->  "
            f"{self.CYAN}{message}{Fore.RESET}"
        )
        print(prompt, end="")
        answer = input()
        self._write_to_log(prompt)
        self._write_to_log(f"User Answer: {answer}")
        return answer

    def message(self, level: str, message: str, start=None, end=None) -> None:
        timer = self._timer_str(start, end)
        t = self.get_time()
        msg = (
            f"{self.prefix}"
            f"[{self.BRIGHT_MAGENTA}{t}{self.PINK}] "
            f"[{self.CYAN}{level}{self.PINK}] -> "
            f"[{self.CYAN}{message}{self.PINK}]{timer}"
        )
        print(msg)
        self._write_to_log(msg)


# ── SimpleLogger ──────────────────────────────────────────────────────────────

class SimpleLogger(Logger):
    """Minimal, clean logger without heavy styling."""

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._raw_prefix = self.prefix or ""
        self.prefix = f"{Fore.BLACK}{self.get_time()} » {Fore.RESET}"
        self.display_repo_info()

    def _timer_str(self, start, end) -> str:
        if start is not None and end is not None:
            return f" (In {str(float(end) - float(start))[:5]}s)"
        return ""

    def _print_and_log(self, msg: str):
        print(msg)
        self._write_to_log(msg)

    def info(self, message: str, start=None, end=None, level: str = "INFO") -> None:
        if self._should_log(LogLevel.INFO):
            msg = f"{self.prefix}{Fore.LIGHTBLUE_EX}{level:<8}{Fore.BLACK}➔{Fore.RESET} {message}{self._timer_str(start, end)}"
            self._print_and_log(msg)

    def debug(self, message: str, start=None, end=None) -> None:
        if self._should_log(LogLevel.DEBUG):
            msg = f"{self.prefix}{Fore.GREEN}[{Fore.YELLOW}DEBUG{Fore.GREEN}]{Fore.BLACK} ➔{Fore.RESET} {message}{self._timer_str(start, end)}"
            self._print_and_log(msg)

    def success(self, message: str, start=None, end=None, level: str = "SUCCESS") -> None:
        if self._should_log(LogLevel.SUCCESS):
            msg = f"{self.prefix}{Fore.LIGHTGREEN_EX}{level:<8}{Fore.BLACK}➔{Fore.RESET} {message}{self._timer_str(start, end)}"
            self._print_and_log(msg)

    def failure(self, message: str, start=None, end=None, level: str = "FAILURE") -> None:
        if self._should_log(LogLevel.FAILURE):
            msg = f"{self.prefix}{Fore.LIGHTRED_EX}{level:<8}{Fore.BLACK}➔{Fore.RESET} {message}{self._timer_str(start, end)}"
            self._print_and_log(msg)

    def error(self, message: str, start=None, end=None, level: str = "ERROR") -> None:
        if self._should_log(LogLevel.FAILURE):
            msg = f"{self.prefix}{Fore.LIGHTRED_EX}{level:<8}{Fore.BLACK}➔{Fore.RESET} {message}{self._timer_str(start, end)}"
            self._print_and_log(msg)

    def warning(self, message: str, start=None, end=None, level: str = "WARNING") -> None:
        if self._should_log(LogLevel.WARNING):
            msg = f"{self.prefix}{Fore.LIGHTYELLOW_EX}{level:<8}{Fore.BLACK}➔{Fore.RESET} {message}{self._timer_str(start, end)}"
            self._print_and_log(msg)

    def critical(self, message: str, start=None, end=None, level: str = "CRITICAL", exit_code: int = 1) -> None:
        if self._should_log(LogLevel.CRITICAL):
            msg = f"{self.prefix}{Fore.RED}{level:<8}{Fore.BLACK}➔{Fore.RESET} {message}{self._timer_str(start, end)}"
            print(msg)
            input()
            self._write_to_log(msg)
            self._write_to_log(f"=== Program terminated with exit code {exit_code} at {datetime.datetime.now()} ===")
            exit(exit_code)

    def question(self, message: str, level: str = "QUESTION") -> str:
        prompt = f"{self.prefix}{Fore.LIGHTCYAN_EX}{level:<8}{Fore.BLACK}➔{Fore.RESET} {message}"
        print(prompt, end="")
        answer = input()
        self._write_to_log(prompt)
        self._write_to_log(f"User Answer: {answer}")
        return answer

    def message(self, message: str, start=None, end=None, level: str = "MESSAGE") -> None:
        msg = f"{self.prefix}{Fore.LIGHTMAGENTA_EX}{level:<8}{Fore.BLACK}➔{Fore.RESET} {message}{self._timer_str(start, end)}"
        self._print_and_log(msg)
