"""Handles color application and terminal compatibility."""

from __future__ import annotations

import sys

from .enums import Style
from .rgb import RGB
from .theme_style import ThemeStyle


class ColorFormatter:
    """Handles color application and terminal compatibility."""

    def __init__(self, enable_colors: bool | None = None):
        """Initialize color formatter with automatic color detection.

        :param enable_colors: Force enable/disable colors, or None for auto-detection
        """
        self.colors_enabled = self._is_color_terminal() if enable_colors is None else enable_colors

        if self.colors_enabled:
            self.enable_windows_ansi_support()

    @staticmethod
    def enable_windows_ansi_support():
        """Enable ANSI escape sequences on Windows terminals."""
        if sys.platform != "win32":
            return

        try:
            import ctypes

            kernel32 = ctypes.windll.kernel32
            kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)
        except Exception:  # noqa: S110 # intentional silent fail for Windows compatibility
            # Fail silently on older Windows versions or permission issues
            pass

    def _is_color_terminal(self) -> bool:
        """Check if the current terminal supports colors."""
        import os

        # Check for explicit disable first
        if os.environ.get("NO_COLOR") or os.environ.get("CLICOLOR") == "0":
            return False
        elif os.environ.get("FORCE_COLOR") or os.environ.get("CLICOLOR"):
            # Check for explicit enable
            return True
        elif not sys.stdout.isatty():
            # Check if stdout is a TTY (not redirected to file/pipe)
            return False
        else:
            # Check environment variables that indicate color support
            term = sys.platform
            if term == "win32":
                # Windows terminal color support
                return True
            else:
                # Unix-like systems
                term_env = os.environ.get("TERM", "").lower()
                if "color" in term_env or term_env in ("xterm", "xterm-256color", "screen"):
                    return True
                elif term_env in ("dumb", ""):
                    # Default for dumb terminals or empty TERM
                    return False
                else:
                    return True

    def apply_style(self, text: str, style: ThemeStyle) -> str:
        """Apply a theme style to text.

        :param text: Text to style
        :param style: ThemeStyle configuration to apply
        :return: Styled text (or original text if colors disabled)
        """
        if not self.colors_enabled or not text:
            return text

        # Build color codes
        codes = []

        # Foreground color - handle RGB instances and ANSI strings
        if style.fg:
            if isinstance(style.fg, RGB):
                fg_code = style.fg.to_ansi(background=False)
                codes.append(fg_code)
            elif isinstance(style.fg, str) and style.fg.startswith("\x1b["):
                # Allow ANSI escape sequences as strings
                codes.append(style.fg)
            else:
                raise ValueError(
                    f"Foreground color must be RGB instance or ANSI string, got {type(style.fg)}"
                )

        # Background color - handle RGB instances and ANSI strings
        if style.bg:
            if isinstance(style.bg, RGB):
                bg_code = style.bg.to_ansi(background=True)
                codes.append(bg_code)
            elif isinstance(style.bg, str) and style.bg.startswith("\x1b["):
                # Allow ANSI escape sequences as strings
                codes.append(style.bg)
            else:
                raise ValueError(
                    f"Background color must be RGB instance or ANSI string, got {type(style.bg)}"
                )

        # Text styling (using defined ANSI constants)
        if style.bold:
            codes.append(Style.ANSI_BOLD.value)  # Use ANSI bold to avoid Style.BRIGHT color shifts
        if style.dim:
            codes.append(Style.DIM.value)  # ANSI DIM style
        if style.italic:
            codes.append(Style.ANSI_ITALIC.value)  # ANSI italic code (support varies by terminal)
        if style.underline:
            codes.append(Style.ANSI_UNDERLINE.value)  # ANSI underline code

        return "".join(codes) + text + Style.RESET_ALL.value if codes else text
