"""Compact ASCII world map with density-colored recording markers."""

from __future__ import annotations

from collections import Counter
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from xc_dl.api.models import Recording

# Land ranges per row: list of (col_start, col_end) inclusive.
# Grid: 76 cols x 18 rows, equirectangular projection.
#   col = (lon + 180) / 360 * 76
#   row = (90 - lat) / 180 * 18
# Each row spans ~10° latitude, each col ~4.7° longitude.
_LAND_RANGES: dict[int, list[tuple[int, int]]] = {
    # Row 1 (75°N): Arctic Canada, Greenland, Svalbard, Novaya Zemlya
    1: [(13, 15), (17, 17), (19, 21), (26, 33), (41, 41), (49, 50)],
    # Row 2 (65°N): Alaska, Canada, Iceland, Scandinavia, Russia
    2: [(3, 6), (11, 27), (36, 36), (38, 42), (44, 74)],
    # Row 3 (55°N): Canada, UK/Ireland, Europe, Russia, Kamchatka
    3: [(14, 25), (36, 37), (39, 71), (73, 74)],
    # Row 4 (45°N): US/Canada, Europe through Central Asia/China, Japan
    4: [(13, 24), (37, 65), (67, 68)],
    # Row 5 (35°N): US, N Africa/Mediterranean/Middle East through China, Japan
    5: [(13, 22), (36, 65), (67, 68)],
    # Row 6 (25°N): Mexico, Sahara/Arabia, India, SE Asia
    6: [(16, 20), (34, 49), (52, 57), (58, 64)],
    # Row 7 (15°N): Central America, W/E Africa, India, SE Asia/Philippines
    7: [(19, 21), (34, 49), (53, 56), (58, 65)],
    # Row 8 (5°N): N South America, W/Central/E Africa, Sri Lanka, SE Asia
    8: [(22, 26), (36, 49), (55, 55), (59, 65)],
    # Row 9 (5°S): Amazon/Brazil, Congo/E Africa, Indonesia
    9: [(22, 30), (40, 46), (59, 68)],
    # Row 10 (15°S): Brazil, E Africa, Madagascar, N Australia
    10: [(24, 30), (40, 46), (48, 49), (62, 68)],
    # Row 11 (25°S): Brazil, Southern Africa, Australia
    11: [(25, 29), (41, 46), (62, 70)],
    # Row 12 (35°S): Chile/Argentina, S Africa, SE Australia, NZ
    12: [(23, 26), (42, 44), (62, 70), (74, 74)],
    # Row 13 (45°S): Patagonia, Tasmania, NZ
    13: [(23, 24), (69, 69), (74, 75)],
    # Row 14 (55°S): Tierra del Fuego
    14: [(23, 23)],
}

_MAP_WIDTH = 76
_MAP_HEIGHT = 18


def _build_base_map() -> list[str]:
    """Build the base map strings from land ranges."""
    rows: list[str] = []
    for r in range(_MAP_HEIGHT):
        chars = [" "] * _MAP_WIDTH
        for start, end in _LAND_RANGES.get(r, []):
            for c in range(start, min(end + 1, _MAP_WIDTH)):
                chars[c] = "#"
        rows.append("".join(chars))
    return rows


_BASE_MAP = _build_base_map()

# Style constants
_STYLE_OCEAN = ""
_STYLE_LAND = "fg:#555555"
_STYLE_LOW = "fg:#999999"
_STYLE_MED = "fg:#cccccc"
_STYLE_HIGH = "fg:#ffffff bold"

_CHAR_LAND = "░"
_CHAR_LOW = "▒"
_CHAR_MED = "▓"
_CHAR_HIGH = "█"


def render_map(
    recordings: list[Recording],
    width: int = _MAP_WIDTH,
    height: int = _MAP_HEIGHT,
) -> list[tuple[str, str]]:
    """Render a styled ASCII world map with recording density markers.

    Returns a list of (style, text) tuples suitable for FormattedTextControl.
    """
    # Build hit-count grid from recordings with valid lat/lon
    hits: Counter[tuple[int, int]] = Counter()
    for rec in recordings:
        if rec.lat is None or rec.lon is None:
            continue
        try:
            lat = float(rec.lat)
            lon = float(rec.lon)
        except (ValueError, TypeError):
            continue
        col = _clamp(int((lon + 180) / 360 * width), 0, width - 1)
        row = _clamp(int((90 - lat) / 180 * height), 0, height - 1)
        hits[(row, col)] += 1

    # Build styled output
    result: list[tuple[str, str]] = []
    result.append(("fg:#888888", " Distribution Map"))
    result.append(("", "\n"))

    for r in range(height):
        # Pad or truncate the base map row
        if r < len(_BASE_MAP):
            base_row = _BASE_MAP[r].ljust(width)[:width]
        else:
            base_row = " " * width

        result.append(("", " "))  # left margin
        for c in range(width):
            count = hits.get((r, c), 0)
            is_land = base_row[c] == "#"

            if count >= 4:
                result.append((_STYLE_HIGH, _CHAR_HIGH))
            elif count >= 2:
                result.append((_STYLE_MED, _CHAR_MED))
            elif count >= 1:
                result.append((_STYLE_LOW, _CHAR_LOW))
            elif is_land:
                result.append((_STYLE_LAND, _CHAR_LAND))
            else:
                result.append((_STYLE_OCEAN, " "))

        result.append(("", "\n"))

    return result


def _clamp(value: int, lo: int, hi: int) -> int:
    if value < lo:
        return lo
    if value > hi:
        return hi
    return value
