"""ANSI color codes for terminal rendering."""

# ANSI color codes
RESET = "\033[0m"
BOLD = "\033[1m"

# foreground colors
BLACK = "\033[30m"
RED = "\033[31m"
GREEN = "\033[32m"
YELLOW = "\033[33m"
BLUE = "\033[34m"
MAGENTA = "\033[35m"
CYAN = "\033[36m"
WHITE = "\033[37m"

# bright foreground colors
BRIGHT_BLACK = "\033[90m"
BRIGHT_RED = "\033[91m"
BRIGHT_GREEN = "\033[92m"
BRIGHT_YELLOW = "\033[93m"
BRIGHT_BLUE = "\033[94m"
BRIGHT_MAGENTA = "\033[95m"
BRIGHT_CYAN = "\033[96m"
BRIGHT_WHITE = "\033[97m"


def fg_256(n: int) -> str:
    """Generate 256-color foreground escape code.

    Args:
        n: Color index (0-255). Values outside range are clamped.

    Returns:
        ANSI escape sequence for 256-color foreground.
    """
    clamped = max(0, min(255, n))
    return f"\033[38;5;{clamped}m"


def bg_256(n: int) -> str:
    """Generate 256-color background escape code.

    Args:
        n: Color index (0-255). Values outside range are clamped.

    Returns:
        ANSI escape sequence for 256-color background.
    """
    clamped = max(0, min(255, n))
    return f"\033[48;5;{clamped}m"


def fg_rgb(r: int, g: int, b: int) -> str:
    """Generate truecolor foreground escape code.

    Args:
        r: Red component (0-255). Values outside range are clamped.
        g: Green component (0-255). Values outside range are clamped.
        b: Blue component (0-255). Values outside range are clamped.

    Returns:
        ANSI escape sequence for truecolor foreground.
    """
    r_clamped = max(0, min(255, r))
    g_clamped = max(0, min(255, g))
    b_clamped = max(0, min(255, b))
    return f"\033[38;2;{r_clamped};{g_clamped};{b_clamped}m"


def bg_rgb(r: int, g: int, b: int) -> str:
    """Generate truecolor background escape code.

    Args:
        r: Red component (0-255). Values outside range are clamped.
        g: Green component (0-255). Values outside range are clamped.
        b: Blue component (0-255). Values outside range are clamped.

    Returns:
        ANSI escape sequence for truecolor background.
    """
    r_clamped = max(0, min(255, r))
    g_clamped = max(0, min(255, g))
    b_clamped = max(0, min(255, b))
    return f"\033[48;2;{r_clamped};{g_clamped};{b_clamped}m"


# terrain color mapping (16-color baseline)
TERRAIN_COLORS = {
    ".": GREEN,  # grass
    "^": BRIGHT_BLACK,  # mountain
    "~": BLUE,  # water
    "T": GREEN,  # forest (darker would be "\033[32m")
    ":": YELLOW,  # sand
    "@": BOLD + BRIGHT_WHITE,  # player
    "*": BOLD + BRIGHT_RED,  # other entity
}

# 256-color terrain palette
TERRAIN_COLORS_256 = {
    ".": fg_256(34),  # grass - richer green
    "^": fg_256(242),  # mountain - gray
    "~": fg_256(27),  # water - deeper blue
    "T": fg_256(22),  # forest - darker green
    ":": fg_256(221),  # sand - warm yellow
    "@": BOLD + fg_256(231),  # player - bright white
    "*": BOLD + fg_256(196),  # other entity - bright red
}

# truecolor terrain palette
TERRAIN_COLORS_RGB = {
    ".": fg_rgb(76, 175, 80),  # grass - material green
    "^": fg_rgb(96, 125, 139),  # mountain - blue-gray
    "~": fg_rgb(33, 150, 243),  # water - material blue
    "T": fg_rgb(27, 94, 32),  # forest - dark green
    ":": fg_rgb(255, 235, 59),  # sand - bright yellow
    "@": BOLD + fg_rgb(255, 255, 255),  # player - pure white
    "*": BOLD + fg_rgb(244, 67, 54),  # other entity - material red
}


def colorize_terrain(char: str, color_depth: str = "16") -> str:
    """Return ANSI-colored version of terrain character.

    Args:
        char: Terrain character to colorize
        color_depth: Color mode - "16", "256", or "truecolor"

    Returns:
        ANSI-colored character with reset code, or unchanged char if no color mapping.
    """
    if color_depth == "truecolor":
        color = TERRAIN_COLORS_RGB.get(char, "")
    elif color_depth == "256":
        color = TERRAIN_COLORS_256.get(char, "")
    else:
        color = TERRAIN_COLORS.get(char, "")

    if color:
        return f"{color}{char}{RESET}"
    return char


def colorize_map(grid: list[list[str]], color_depth: str = "16") -> str:
    """Colorize a 2D grid of terrain and return as string.

    Args:
        grid: 2D list of terrain characters
        color_depth: Color mode - "16", "256", or "truecolor"

    Returns:
        Newline-separated string of colorized terrain rows.
    """
    lines = []
    for row in grid:
        colored_row = "".join(colorize_terrain(char, color_depth) for char in row)
        lines.append(colored_row)
    return "\n".join(lines)
