import glob
import os
import re

from app.lib.handwriting.config import (
    ASCENT,
    DEFAULT_WIDTH,
    DESCENT,
    FONT_FAMILY,
    FONT_NAME,
    FONT_STYLE,
    OUT_FONT_DIR,
    UPM,
    VECT_GLYPH_DIR,
)
from app.lib.handwriting.io_utils import ensure_dir


def _char_to_codepoint(ch):
    if isinstance(ch, str) and ch.upper().startswith("U+"):
        try:
            return int(ch[2:], 16)
        except Exception:
            return None
    if isinstance(ch, str) and len(ch) == 1:
        return ord(ch)
    return None


def build_ttf_with_fontforge(out_dir):
    try:
        import fontforge
        import psMat
    except Exception as e:
        raise RuntimeError(
            "FontForge python bindings not available.\n"
            "On Ubuntu: sudo apt-get install fontforge python3-fontforge\n"
            f"Original error: {e}"
        )

    svg_dir = os.path.join(out_dir, VECT_GLYPH_DIR)
    svg_files = sorted(glob.glob(os.path.join(svg_dir, "*.svg")))
    if not svg_files:
        raise RuntimeError("No SVG glyphs found to build TTF.")

    font_out_dir = os.path.join(out_dir, OUT_FONT_DIR)
    ensure_dir(font_out_dir)

    font = fontforge.font()
    font.fontname = FONT_NAME
    font.familyname = FONT_FAMILY
    font.fullname = f"{FONT_FAMILY} {FONT_STYLE}"
    font.weight = FONT_STYLE
    font.em = UPM
    font.ascent = ASCENT
    font.descent = DESCENT

    space = font.createChar(0x20, "space")
    space.width = int(DEFAULT_WIDTH * 0.35)

    for svg_path in svg_files:
        stem = os.path.splitext(os.path.basename(svg_path))[0]
        m = re.match(r"^glyph_(.+?)_binary$", stem)
        if not m:
            continue

        ch = m.group(1)
        codepoint = _char_to_codepoint(ch)
        if codepoint is None:
            continue

        # print(f"Processing glyph '{ch}' ({os.path.basename(svg_path)})")
        try:
            glyph = font.createChar(codepoint)
        except Exception as e:
            raise RuntimeError(
                f"FontForge createChar failed for '{ch}' (U+{codepoint:04X}): {e}"
            ) from e
        glyph.clear()

        try:
            glyph.importOutlines(svg_path)
        except Exception as e:
            raise RuntimeError(
                f"FontForge importOutlines failed for {os.path.basename(svg_path)}: {e}"
            ) from e
        try:
            if not glyph.isWorthOutputting():
                raise RuntimeError(
                    f"FontForge imported empty outline for {os.path.basename(svg_path)}"
                )
        except Exception:
            pass

        xmin, ymin, xmax, ymax = glyph.boundingBox()
        if xmin == xmax or ymin == ymax:
            raise RuntimeError(
                f"FontForge produced zero-area outline for {os.path.basename(svg_path)}"
            )
        # As-written mode: keep the original vertical placement and scale.
        side = 20
        glyph.transform(psMat.translate(-xmin + side, 0))
        glyph.width = int(DEFAULT_WIDTH)

        glyph.removeOverlap()
        glyph.correctDirection()
        glyph.round()

    ttf_path = os.path.join(font_out_dir, f"{FONT_NAME}.ttf")
    font.generate(ttf_path)
    font.close()
