"""Block rendering core - Font loading, text rasterization, word-wrap, and headline assembly.

Provides PIL font-based rendering to terminal half-block characters.
"""

import random
import re
from pathlib import Path
from typing import Optional, Tuple

from PIL import Image, ImageDraw, ImageFont

from sideline.fonts import get_default_font_path, get_default_font_size


# ─── FONT LOADING ─────────────────────────────────────────
_FONT_OBJ = None
_FONT_OBJ_KEY = None
_FONT_CACHE = {}


def font():
    """Lazy-load the default Sideline font."""
    global _FONT_OBJ, _FONT_OBJ_KEY

    try:
        font_path = get_default_font_path()
        font_size = get_default_font_size()
    except FileNotFoundError:
        # Fallback to system default if Sideline font not found
        return ImageFont.load_default()

    key = (font_path, font_size)
    if _FONT_OBJ is None or key != _FONT_OBJ_KEY:
        try:
            _FONT_OBJ = ImageFont.truetype(font_path, font_size)
            _FONT_OBJ_KEY = key
        except Exception:
            # If loading fails, fall back to system default
            _FONT_OBJ = ImageFont.load_default()
            _FONT_OBJ_KEY = key
    return _FONT_OBJ


def clear_font_cache():
    """Reset cached font objects."""
    global _FONT_OBJ, _FONT_OBJ_KEY
    _FONT_OBJ = None
    _FONT_OBJ_KEY = None


def load_font_face(font_path, font_index=0, size=None):
    """Load a specific face from a font file or collection."""
    if size is None:
        size = get_default_font_size()
    return ImageFont.truetype(font_path, size, index=font_index)


def list_font_faces(font_path, max_faces=64):
    """Return discoverable face indexes + display names from a font file."""
    faces = []
    for idx in range(max_faces):
        try:
            fnt = load_font_face(font_path, idx)
        except Exception:
            if idx == 0:
                raise
            break
        family, style = fnt.getname()
        display = f"{family} {style}".strip()
        if not display:
            display = f"{Path(font_path).stem} [{idx}]"
        faces.append({"index": idx, "name": display})
    return faces


def font_for_lang(lang: Optional[str] = None):
    """Get appropriate font for a language.

    Currently uses the default Sideline font for all languages.
    Language-specific fonts can be added via the font cache system.
    """
    if lang is None:
        return font()
    if lang not in _FONT_CACHE:
        # Try to load language-specific font, fall back to default
        try:
            # Could add language-specific font logic here
            _FONT_CACHE[lang] = font()
        except Exception:
            _FONT_CACHE[lang] = font()
    return _FONT_CACHE[lang]


# ─── RASTERIZATION ────────────────────────────────────────
def render_line(text, fnt=None):
    """Render a line of text as terminal rows using OTF font + half-blocks."""
    if fnt is None:
        fnt = font()
    bbox = fnt.getbbox(text)
    if not bbox or bbox[2] <= bbox[0]:
        return [""]
    pad = 4
    img_w = bbox[2] - bbox[0] + pad * 2
    img_h = bbox[3] - bbox[1] + pad * 2
    img = Image.new("L", (img_w, img_h), 0)
    draw = ImageDraw.Draw(img)
    draw.text((-bbox[0] + pad, -bbox[1] + pad), text, fill=255, font=fnt)

    # Rendering parameters (can be made configurable)
    render_h = 6  # Terminal rows per rendered line
    ssaa = 2  # Supersampling anti-aliasing factor

    pix_h = render_h * 2
    hi_h = pix_h * ssaa
    scale = hi_h / max(img_h, 1)
    new_w_hi = max(1, int(img_w * scale))
    img = img.resize((new_w_hi, hi_h), Image.Resampling.LANCZOS)
    new_w = max(1, int(new_w_hi / ssaa))
    img = img.resize((new_w, pix_h), Image.Resampling.LANCZOS)
    data = img.tobytes()
    thr = 80
    rows = []
    for y in range(0, pix_h, 2):
        row = []
        for x in range(new_w):
            top = data[y * new_w + x] > thr
            bot = data[(y + 1) * new_w + x] > thr if y + 1 < pix_h else False
            if top and bot:
                row.append("█")
            elif top:
                row.append("▀")
            elif bot:
                row.append("▄")
            else:
                row.append(" ")
        rows.append("".join(row))
    return rows


def big_wrap(text: str, width: int, fnt=None) -> list[str]:
    """Wrap text and render to big block characters."""
    if fnt is None:
        fnt = font()
    text = re.sub(r"\s+", " ", text.upper())
    words = text.split()
    lines = []
    cur = ""

    # Get font size for height calculation
    try:
        font_size = fnt.size if hasattr(fnt, "size") else get_default_font_size()
    except Exception:
        font_size = get_default_font_size()

    render_h = 6  # Terminal rows per rendered line

    for word in words:
        test = f"{cur} {word}".strip() if cur else word
        bbox = fnt.getbbox(test)
        if bbox:
            img_h = bbox[3] - bbox[1] + 8
            pix_h = render_h * 2
            scale = pix_h / max(img_h, 1)
            term_w = int((bbox[2] - bbox[0] + 8) * scale)
        else:
            term_w = 0
        max_term_w = width - 4 - 4
        if term_w > max_term_w and cur:
            lines.append(cur)
            cur = word
        else:
            cur = test
    if cur:
        lines.append(cur)
    out = []
    for i, ln in enumerate(lines):
        out.extend(render_line(ln, fnt))
        if i < len(lines) - 1:
            out.append("")
    return out


# ─── HEADLINE BLOCK ASSEMBLY ─────────────────────────────
def make_block(title: str, src: str, ts: str, w: int) -> Tuple[list[str], str, int]:
    """Render a headline into a content block with color.

    Args:
        title: Headline text to render
        src: Source identifier (for metadata)
        ts: Timestamp string (for metadata)
        w: Width constraint in terminal characters

    Returns:
        tuple: (content_lines, color_code, meta_row_index)
            - content_lines: List of rendered text lines
            - color_code: ANSI color code for display
            - meta_row_index: Row index of metadata line
    """
    # Use default font for all languages (simplified from original)
    lang_font = font()

    # Simple uppercase conversion (can be made language-aware later)
    title_up = re.sub(r"\s+", " ", title.upper())

    # Standardize quotes and dashes
    for old, new in [
        ("\u2019", "'"),
        ("\u2018", "'"),
        ("\u201c", '"'),
        ("\u201d", '"'),
        ("\u2013", "-"),
        ("\u2014", "-"),
    ]:
        title_up = title_up.replace(old, new)

    big_rows = big_wrap(title_up, w - 4, lang_font)

    # Matrix-style color selection
    hc = random.choice(
        [
            "\033[38;5;46m",  # matrix green
            "\033[38;5;34m",  # dark green
            "\033[38;5;82m",  # lime
            "\033[38;5;48m",  # sea green
            "\033[38;5;37m",  # teal
            "\033[38;5;44m",  # cyan
            "\033[38;5;87m",  # sky
            "\033[38;5;117m",  # ice blue
            "\033[38;5;250m",  # cool white
            "\033[38;5;156m",  # pale green
            "\033[38;5;120m",  # mint
            "\033[38;5;80m",  # dark cyan
            "\033[38;5;108m",  # grey-green
            "\033[38;5;115m",  # sage
            "\033[1;38;5;46m",  # bold green
            "\033[1;38;5;250m",  # bold white
        ]
    )

    content = ["  " + r for r in big_rows]
    content.append("")
    meta = f"\u2591 {src} \u00b7 {ts}"
    content.append(" " * max(2, w - len(meta) - 2) + meta)
    return content, hc, len(content) - 1  # (rows, color, meta_row_index)
