"""
fonts.py

Turns an uploaded font file into a base64 @font-face CSS block, so custom
heading/body fonts get embedded directly in the manual -- no external font
requests needed at PDF-render time. Falls back to a Google-Fonts-hosted
default when the user doesn't upload one, so the pipeline always works
even with zero font uploads.
"""

import base64
from pathlib import Path

FORMAT_MAP = {
    "otf": "opentype",
    "ttf": "truetype",
    "woff": "woff",
    "woff2": "woff2",
}

ALLOWED_FONT_EXTS = set(FORMAT_MAP.keys())

DEFAULT_HEADING_STACK = "'Barlow Condensed', sans-serif"
DEFAULT_BODY_STACK = "'Barlow', sans-serif"
DEFAULT_GOOGLE_FONTS_IMPORT = (
    "@import url('https://fonts.googleapis.com/css2?"
    "family=Barlow+Condensed:wght@500;600;700"
    "&family=Barlow:wght@400;500;600&display=swap');"
)


def font_family_from_filename(path):
    """Derive a readable font-family name from the uploaded filename."""
    stem = Path(path).stem
    cleaned = stem.replace("_", " ").replace("-", " ").strip()
    return " ".join(w.capitalize() for w in cleaned.split()) or "Custom Font"


def build_font_face(path, family_name, weight="400", style="normal"):
    ext = Path(path).suffix.lstrip(".").lower()
    if ext not in FORMAT_MAP:
        raise ValueError(f"Unsupported font format '.{ext}'. Use OTF, TTF, WOFF, or WOFF2.")
    data = Path(path).read_bytes()
    b64 = base64.b64encode(data).decode("ascii")
    mime = "font/" + ext if ext in ("woff", "woff2") else "application/x-font-" + ext
    return f"""@font-face {{
  font-family: '{family_name}';
  src: url(data:{mime};base64,{b64}) format('{FORMAT_MAP[ext]}');
  font-weight: {weight};
  font-style: {style};
  font-display: swap;
}}"""


def build_font_config(heading_font_path=None, body_font_path=None):
    """
    Returns (font_faces_css, heading_stack, body_stack, heading_name, body_name)
    ready to drop into the template's tokens.
    """
    font_faces = []

    if heading_font_path:
        heading_name = font_family_from_filename(heading_font_path)
        font_faces.append(build_font_face(heading_font_path, heading_name))
        heading_stack = f"'{heading_name}', serif"
    else:
        heading_name = "Barlow Condensed"
        heading_stack = DEFAULT_HEADING_STACK

    if body_font_path:
        body_name = font_family_from_filename(body_font_path)
        font_faces.append(build_font_face(body_font_path, body_name))
        body_stack = f"'{body_name}', sans-serif"
    else:
        body_name = "Barlow"
        body_stack = DEFAULT_BODY_STACK

    css_block = "\n".join(font_faces) if font_faces else ""
    if not heading_font_path or not body_font_path:
        # still need Google Fonts for whichever side wasn't uploaded --
        # this is raw CSS (an @import statement), not a <style> tag, since
        # {{FONT_FACES}} is substituted INSIDE the template's own <style>
        # block. Wrapping it in another <style>...</style> here would
        # prematurely close the outer block and silently break every CSS
        # rule that follows it in the template.
        css_block = DEFAULT_GOOGLE_FONTS_IMPORT + "\n" + css_block

    return css_block, heading_stack, body_stack, heading_name, body_name
