# -*- coding: utf-8 -*-
"""
Fabrique de markup de blocs, pour écrire les pages du site.

POURQUOI UN GÉNÉRATEUR ET NON DU HTML ÉCRIT À LA MAIN

Les attributs d'un bloc sont du JSON dans un commentaire HTML. Écrits à la
main, un guillemet dans un texte — « <mark class="has-inline-color"> » — casse
le JSON, et WordPress ne dit RIEN : il retombe silencieusement sur les valeurs
par défaut. Les titres de héros s'affichaient en texte courant vide.

json.dumps règle le problème une fois pour toutes.

Usage :

    from _blocs import *
    page = section({'cols': 1}, [ texte('hero', 'Bonjour', level=1) ])
"""

import json


def _attrs(d):
    """Attributs d'un bloc, en JSON compact — vide si rien à déclarer."""
    d = {k: v for k, v in d.items() if v is not None}
    if not d:
        return ''
    return ' ' + json.dumps(d, ensure_ascii=False, separators=(',', ':'))


def blk(name, attrs=None, inner=None):
    """Un bloc. `inner` absent produit un bloc auto-fermant."""
    a = _attrs(attrs or {})
    if inner is None:
        return '<!-- wp:%s%s /-->' % (name, a)
    body = inner if isinstance(inner, str) else '\n'.join(x for x in inner if x)
    return '<!-- wp:%s%s -->\n%s\n<!-- /wp:%s -->' % (name, a, body, name)


# --------------------------------------------------------------------------
# Raccourcis de style
# --------------------------------------------------------------------------

def pad(top=None, bottom=None, left=None, right=None):
    d = {'top': top, 'bottom': bottom, 'left': left, 'right': right}
    return {k: v for k, v in d.items() if v}


def style(padding=None, margin=None, color=None, min_height=None):
    """Bloc « style » natif de WordPress."""
    s = {}
    if padding:
        s.setdefault('spacing', {})['padding'] = padding
    if margin:
        s.setdefault('spacing', {})['margin'] = margin
    if color:
        s['color'] = color
    if min_height:
        s['dimensions'] = {'minHeight': min_height}
    return s or None


def sp(n):
    """Une taille de l'échelle d'espacement de theme.json."""
    return 'var:preset|spacing|%d' % n


def col(slug):
    """Une couleur de la palette, au format attendu dans un attribut style."""
    return 'var:preset|color|%s' % slug


def mark(txt, slug):
    """Mot coloré dans un texte riche, au format que produit l'éditeur."""
    return ('<mark style="background-color:rgba(0,0,0,0)" '
            'class="has-inline-color has-%s-color">%s</mark>' % (slug, txt))


# --------------------------------------------------------------------------
# Blocs du thème
# --------------------------------------------------------------------------

def section(attrs, inner):
    return blk('cimo/section', attrs, inner)


def grille(attrs, inner):
    return blk('cimo/grid', attrs, inner)


def pile(attrs, inner):
    return blk('cimo/stack', attrs, inner)


def heros_fendu(attrs, inner):
    return blk('cimo/hero-split', attrs, inner)


def croppe(attrs, inner):
    return blk('cimo/box-cut', attrs, inner)


def arrondi(attrs, inner):
    return blk('cimo/box-round', attrs, inner)


def texte(role, content, level=None, align=None, **extra):
    """Un bloc Texte.

    L'alignement part dans style.typography.textAlign, et NULLE PART AILLEURS.
    Il a d'abord été écrit en attribut « textAlign » de premier niveau, ce qui
    paraissait naturel — c'est le nom du support. Mais le cœur va le chercher
    dans style.typography.textAlign pour en tirer la classe has-text-align-* :
    écrit ailleurs, il était enregistré dans la page, relu sans erreur, et
    ignoré au rendu. Les titres du héros d'accueil et ceux des trois cartes
    s'affichaient à gauche, sans que rien ne le signale.
    """
    a = {'role': role, 'content': content}
    if level is not None:
        a['level'] = level
    a.update(extra)
    if align:
        s = dict(a.get('style') or {})
        s['typography'] = dict(s.get('typography') or {}, textAlign=align)
        a['style'] = s
    return blk('cimo/text', a)


def bouton(content, variant='cimo-btn--teal', url='#', **extra):
    a = {'content': content, 'variant': variant, 'url': url}
    a.update(extra)
    return blk('cimo/button', a)


def etiquette(content, **extra):
    a = {'content': content}
    a.update(extra)
    return blk('cimo/tagline', a)


def icone(name, **extra):
    a = {'name': name}
    a.update(extra)
    return blk('cimo/icon', a)


def decor(**attrs):
    return blk('cimo/decor', attrs)


def figure(**attrs):
    return blk('cimo/figure', attrs)


def composition(slug):
    return blk('pattern', {'slug': 'citizenmotion2/' + slug})


def separateur(classe='cimo-rule', couleur=None):
    """Le filet court sous une icône.

    core/separator est un bloc STATIQUE : son markup doit figurer dans la page,
    et l'éditeur le compare à ce que son save() regénère. D'où deux contraintes
    qui ont coûté un « ce bloc contient du contenu invalide » :

    1. La couleur passe par `backgroundColor`, un SLUG de la palette, jamais par
       style.color.text : core/separator déclare supports.color.text = false,
       la couleur du texte n'y existe pas. Le bloc la sérialise ensuite à la
       fois en color et en background-color, d'où les cinq classes ci-dessous —
       c'est exactement ce que produisent les motifs du cœur.
    2. `has-alpha-channel-opacity` est TOUJOURS là : l'attribut opacity a pour
       valeur par défaut « alpha-channel », et save() en tire cette classe même
       quand le markup ne déclare rien.

    Aucun style en ligne : save() n'écrit la couleur que dans les classes.
    """
    a = {'className': classe}
    classes = ['wp-block-separator']

    if couleur:
        a['backgroundColor'] = couleur
        classes += ['has-text-color', 'has-%s-color' % couleur]

    classes.append('has-alpha-channel-opacity')

    if couleur:
        classes += ['has-%s-background-color' % couleur, 'has-background']

    classes.append(classe)

    return blk('separator', a, '<hr class="%s"/>' % ' '.join(classes))


def surtitre(txt, slug='brand-teal'):
    """Le « // SUR-TITRE » des maquettes."""
    return texte('eyebrow', txt, slashes=True, style=style(color={'text': col(slug)}))


def entete_section(sur, titre, slug='brand-teal', rule=False):
    """Sur-titre + titre, avec le filet horizontal des pages de thématique."""
    return pile(
        {'gap': '0.25rem',
         'className': 'cimo-section-head' + (' has-rule' if rule else '')},
        [surtitre(sur, slug), texte('section', titre, level=2)]
    )
