"""Typography + color theme injected into every Streamlit page.

Implements ``geist_spec.md`` §3 verbatim. Single source of truth for
the type scale and the base color tokens. Component-specific styling
(buttons, sidebar, file uploader, expanders, alerts, …) lives in
``components/_legacy.py:_DESIGN_TOKENS_CSS`` and reads the
``--font-sans`` / ``--font-mono`` / ``--ink`` / ``--bg`` / ``--surface``
/ ``--border`` / ``--accent`` / ``--accent-fill`` variables this
module declares.

The spec wants this called at the top of every page right after
``st.set_page_config()``. In this codebase every page already calls
``hide_streamlit_chrome()`` for that purpose, so :func:`apply_theme`
is invoked from there — one place, every page.
"""

from __future__ import annotations

import streamlit as st


_GOOGLE_FONTS_URL = (
    "https://fonts.googleapis.com/css2"
    "?family=Geist:wght@400;500;600;700"
    "&family=Geist+Mono:wght@400;500"
    "&display=swap"
)

# Streamlit loads Material Symbols Outlined automatically wherever it
# uses ``:material/<name>:`` syntax (sidebar icons). Our custom HTML
# severity-chip icons reach for the same family, so request it
# explicitly here — guarantees the font is present before any inline
# ligature renders, regardless of whether Streamlit has emitted a
# ``:material/`` token yet.
_MATERIAL_SYMBOLS_URL = (
    "https://fonts.googleapis.com/css2"
    "?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,400,0,0"
    "&display=block"
)


# Spec §3 + §7. Heading rules track the canonical type scale in spec §4:
# h1 32/600/-0.035em, h2 22/600/-0.025em, h3 18/500/-0.018em, h4 15/500/
# -0.012em, body 14/400, caption 12.5/400, mono inherits × 0.92.
# The severity extension tokens (--warn / --info / --success / --danger
# + their fills) are NOT in spec §7 but live here so the component CSS
# in _legacy.py has a single :root table to read from.
_CSS = f"""
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="{_GOOGLE_FONTS_URL}" rel="stylesheet">
<link href="{_MATERIAL_SYMBOLS_URL}" rel="stylesheet">

<style>
  :root {{
    --font-sans: "Geist", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
    --font-mono: "Geist Mono", ui-monospace, "SF Mono", Menlo, monospace;

    --ink: #1c1917;
    --ink-secondary: #57534e;
    --ink-tertiary: #a8a29e;
    --bg: #fafaf7;
    --surface: #ffffff;
    --surface-hover: #f8f7f3;
    --border: #e7e5dc;
    --border-strong: #d6d3c7;
    --accent: #c2410c;
    --accent-hover: #9a3412;
    --accent-fill: #fef4ed;
    --accent-fill-strong: #fde4d3;

    --warn: #b45309;
    --warn-fill: #fef3c7;
    --info: #0369a1;
    --info-fill: #e0f2fe;
    --success: #15803d;
    --success-fill: #dcfce7;
    --danger: #b91c1c;
    --danger-fill: #fee2e2;

    --r-sm: 6px;
    --r-md: 10px;
    --r-lg: 14px;
  }}

  /* Base — Geist everywhere by default. Spec §3 + §6 item 6.
     ``!important`` on the family is a deviation from the spec but
     necessary: Streamlit sets ``font-family: "Source Sans"`` on
     ``[data-testid="stMarkdownContainer"]`` (and a few other widget
     wrappers) at equal-or-higher specificity than the spec's selector
     list, so plain inheritance from html/body loses. */
  html, body, [class*="css"], .stApp, .stMarkdown, .stText,
  .stButton button, .stTextInput input, .stSelectbox, .stDataFrame,
  [data-testid="stSidebar"],
  [data-testid="stSidebarNav"],
  [data-testid="stSidebarNav"] a,
  [data-testid="stSidebarNav"] span,
  [data-testid="stMarkdownContainer"],
  [data-testid="stVerticalBlock"],
  [data-testid="stHorizontalBlock"] {{
    font-family: var(--font-sans) !important;
    color: var(--ink);
    font-feature-settings: "ss01", "cv01", "cv11";
  }}

  /* Headings — same family, hierarchy via weight + size + tracking.
     Spec §4 rule 1: negative tracking is non-optional at display sizes. */
  .stApp h1, .stApp h2, .stApp h3, .stApp h4,
  [data-testid="stMarkdownContainer"] h1,
  [data-testid="stMarkdownContainer"] h2,
  [data-testid="stMarkdownContainer"] h3,
  [data-testid="stMarkdownContainer"] h4 {{
    font-family: var(--font-sans);
    color: var(--ink);
  }}

  /* h1 — 32px / 600 / -0.035em  (spec §4) */
  .stApp h1, [data-testid="stMarkdownContainer"] h1 {{
    font-size: 32px;
    font-weight: 600;
    letter-spacing: -0.035em;
    line-height: 1.1;
    margin: 0 0 4px;
  }}

  /* h2 — 22px / 600 / -0.025em  (spec §4) */
  .stApp h2, [data-testid="stMarkdownContainer"] h2 {{
    font-size: 22px;
    font-weight: 600;
    letter-spacing: -0.025em;
    line-height: 1.2;
    margin: 1.5rem 0 0.75rem;
  }}

  /* h3 — 18px / 500 / -0.018em  (spec §4) */
  .stApp h3, [data-testid="stMarkdownContainer"] h3 {{
    font-size: 18px;
    font-weight: 500;
    letter-spacing: -0.018em;
    line-height: 1.25;
    margin: 1.25rem 0 0.5rem;
  }}

  /* h4 — 15px / 500 / -0.012em  (spec §4) */
  .stApp h4, [data-testid="stMarkdownContainer"] h4 {{
    font-size: 15px;
    font-weight: 500;
    letter-spacing: -0.012em;
    line-height: 1.35;
    margin: 1rem 0 0.5rem;
  }}

  /* Body — 14px / 400 / 1.55  (spec §4). Family + !important added on
     top of the spec because Streamlit's CSS sets ``font-family`` on
     ``[data-testid="stMarkdownContainer"]`` itself (parent rule with
     equal specificity), so plain inheritance from body loses the
     cascade tiebreaker. Spec §6.1 explicitly anticipates this. */
  .stApp p, [data-testid="stMarkdownContainer"] p {{
    font-family: var(--font-sans) !important;
    font-size: 14px;
    font-weight: 400;
    line-height: 1.55;
    color: var(--ink);
  }}

  /* Body emphasis — 14px / 500  (spec §4) */
  [data-testid="stMarkdownContainer"] strong,
  .stApp strong {{
    font-weight: 500;
    color: var(--ink);
  }}

  /* Caption / muted — 12.5px / 400  (spec §4) */
  .stCaption, [data-testid="stCaptionContainer"] {{
    font-family: var(--font-sans) !important;
    font-size: 12.5px;
    font-weight: 400;
    line-height: 1.5;
    color: var(--ink-tertiary);
  }}

  /* Monospace — inherits × 0.92, ss02 slashed zero (spec §4, §6) */
  code, .stCode, pre,
  [data-testid="stMarkdownContainer"] code {{
    font-family: var(--font-mono);
    font-size: 0.92em;
    font-feature-settings: "ss02";
  }}

  /* Material icons in the sidebar nav. Streamlit's ``:material/<name>:``
     syntax injects a first-child ``<span>`` whose text is the icon's
     Material Symbols ligature (e.g. ``home``), expected to be rendered
     by the Material Symbols font. Our base rule above forces
     ``var(--font-sans)`` on every span inside ``stSidebarNav``, which
     turns those spans into literal text labels. Override the icon
     span back to the Material font here. Targeting by ``:first-child``
     of the nav-link anchor is stable across Streamlit versions —
     emotion-class hashes are not. */
  [data-testid="stSidebarNavLink"] > span:first-child,
  [data-testid="stSidebarNavLink"] > span:first-child * {{
    font-family: "Material Symbols Outlined", "Material Symbols Rounded",
                 "Material Icons" !important;
    font-feature-settings: normal !important;
    font-weight: 400 !important;
    font-size: 18px !important;
    line-height: 1 !important;
    color: var(--ink-secondary);
  }}
  [data-testid="stSidebarNavLink"][aria-current="page"] > span:first-child,
  [data-testid="stSidebarNavLink"][aria-current="page"] > span:first-child * {{
    color: var(--ink) !important;
  }}
</style>
"""


def apply_theme() -> None:
    """Inject typography + color CSS. Call once at the top of every page,
    immediately after :func:`streamlit.set_page_config`.
    """
    st.markdown(_CSS, unsafe_allow_html=True)
