#!/usr/bin/env python3
"""
L'index de la documentation dans la colonne laterale du theme.

Pourquoi la colonne du theme plutot qu'une colonne dans le contenu : l'index
insere dans le corps de la page chevauchait le texte. La cause tenait a
wpautop, qui enveloppe dans un <p> tout element inline place entre deux
balises de bloc — le dernier de ces <p> n'etait jamais ferme avant </nav>. On
peut s'en premunir, et c'est fait, mais la colonne du theme est de toute facon
le bon endroit : elle est deja prevue pour cela, elle se replie seule sur
mobile, et elle ne se bat pas avec la largeur maximale de `.wd-doc`.

La contrepartie, assumee : un widget est GLOBAL. L'index apparaitra sur toutes
les pages du site, et l'entree courante ne peut pas y etre mise en evidence.

Usage :
    python3 wp_sidebar.py --dry-run
    python3 wp_sidebar.py --apply
    python3 wp_sidebar.py --hide --apply       retire l'index de la colonne
    python3 wp_sidebar.py --restore --apply    remet les widgets d'origine
"""
import json, os, sys, base64, urllib.request

HERE = os.path.dirname(os.path.abspath(__file__))
conf = {}
for line in open(os.path.join(HERE, '.wpcreds'), encoding='utf-8'):
    if '=' in line:
        k, v = line.strip().split('=', 1)
        conf[k] = v.strip().strip("'")
SITE = conf['WP_SITE']
AUTH = base64.b64encode(f"{conf['WP_USER']}:{conf['WP_PASS']}".encode()).decode()

SIDEBAR = 'sidebar-1'
# Reconnaissable d'une execution a l'autre : c'est ce qui rend le script
# rejouable sans empiler les widgets.
SIGNATURE = 'data-wd-docindex="1"'

sys.path.insert(0, HERE)
from wp_publish import ORDER, LABEL_EN, fetch_pages, DOC_PARENT   # noqa: E402


def api(path, method='GET', payload=None):
    url = SITE + '/wp-json/wp/v2/' + path
    data = json.dumps(payload).encode() if payload is not None else None
    req = urllib.request.Request(url, data=data, method=method)
    req.add_header('Authorization', 'Basic ' + AUTH)
    if data:
        req.add_header('Content-Type', 'application/json')
    with urllib.request.urlopen(req, timeout=60) as r:
        return json.loads(r.read().decode())


S_A = 'display:block;padding:.28rem .1rem;text-decoration:none;color:#334155;font-size:.9rem'
S_H = ('margin:.9rem 0 .3rem;font-size:.72rem;letter-spacing:.06em;'
       'text-transform:uppercase;color:#94a3b8;font-weight:700')


def build_widget_html(pages):
    """
    L'index des deux langues, dans un seul widget.

    Un widget etant global, on ne peut pas servir l'index francais aux pages
    francaises et l'anglais aux anglaises. Les deux sont donc presentes, l'un
    sous l'autre, separes par leur intitule de langue.
    """
    out = ['<div ' + SIGNATURE + ' style="font-size:.9rem;line-height:1.5">']
    for lang, parent, lab in (('fr', DOC_PARENT['fr'], 'Documentation'),
                              ('en', DOC_PARENT['en'], 'Documentation (EN)')):
        kids = {p['slug']: p for p in pages if p.get('parent') == parent}
        if not kids:
            continue
        out.append('<p style="margin:.2rem 0 .1rem;font-size:.8rem;letter-spacing:.05em;'
                   'text-transform:uppercase;color:#2563eb;font-weight:700">%s</p>' % lab)
        first = True
        for group, slugs in ORDER:
            entries = [kids[s] for s in slugs if s in kids]
            if not entries:
                continue
            out.append('<p style="%s%s">%s</p>'
                       % (S_H, ';margin-top:.4rem' if first else '',
                          group if lang == 'fr' else LABEL_EN[group]))
            first = False
            for p in entries:
                out.append('<p style="margin:0"><a href="/?page_id=%d" style="%s">%s</a></p>'
                           % (p['id'], S_A, p['title']['raw']))
        out.append('<p style="%s">%s</p>' % (S_H, 'TELECHARGER' if lang == 'fr' else 'DOWNLOAD'))
        out.append('<p style="margin:0 0 .6rem"><a href="/?page_id=%d" style="%s">%s</a></p>'
                   % (1111 if lang == 'fr' else 1339, S_A,
                      'Telechargement' if lang == 'fr' else 'Download'))
    out.append('</div>')
    return ''.join(out)


def main():
    apply = '--apply' in sys.argv
    restore = '--restore' in sys.argv
    hide = '--hide' in sys.argv

    current = api('sidebars/' + SIDEBAR)['widgets']
    widgets = api('widgets?context=edit&sidebar=' + SIDEBAR)
    by_id = {w['id']: w for w in widgets}

    if hide:
        # Retire l'index de la colonne sans le detruire : il repart en widgets
        # desactives, pret a etre remis d'un `--apply` le jour de la
        # publication. Tant que les pages de documentation sont en brouillon,
        # ses liens ne menent nulle part pour un visiteur.
        idx = next((w for w in widgets
                    if w['id_base'] == 'block'
                    and SIGNATURE in ((w.get('instance') or {}).get('raw') or {}).get('content', '')),
                   None)
        if not idx:
            print('aucun index dans %s — rien a faire.' % SIDEBAR)
            return
        reste = [w for w in current if w != idx['id']]
        print('retrait de l\'index de la colonne laterale')
        print('  widget retire  :', idx['id'])
        print('  colonne apres  :', ', '.join(reste) or '(vide)')
        if not apply:
            print('\n(simulation — relancer avec --hide --apply)')
            return
        apres = api('sidebars/' + SIDEBAR, 'POST', {'widgets': reste})['widgets']
        print('\n  ordre final :', ', '.join(apres) or '(vide)')
        print('  l\'index reste en widgets desactives : un --apply le remettra.')
        return

    if restore:
        avant = json.load(open(os.path.join(HERE, 'wp-backup/sidebar1-avant.json'), encoding='utf-8'))
        print('remise en place des widgets d\'origine :')
        for w in avant['widgets']:
            print('  ', w)
        if not apply:
            print('\n(simulation — relancer avec --restore --apply)')
            return
        api('sidebars/' + SIDEBAR, 'POST', {'widgets': avant['widgets']})
        print('\ncolonne laterale restauree.')
        return

    pages = fetch_pages()
    html = build_widget_html(pages)

    # Le widget d'index deja pose, s'il existe : on le met a jour au lieu
    # d'en ajouter un second.
    existing = next((w for w in widgets
                     if w['id_base'] == 'block'
                     and SIGNATURE in ((w.get('instance') or {}).get('raw') or {}).get('content', '')),
                    None)

    garder = [w for w in current if w.startswith('search-')]
    if not garder:
        print('  !! aucun widget de recherche dans %s — on continue sans' % SIDEBAR)
    retires = [w for w in current if w not in garder and (not existing or w != existing['id'])]

    print('colonne laterale « %s »\n' % SIDEBAR)
    print('  conserves :', ', '.join(garder) or '(aucun)')
    print('  index     :', (existing['id'] + ' (mis a jour)') if existing else '(nouveau widget)')
    print('  retires   :', ', '.join(retires) or '(aucun)')
    for w in retires:
        base = by_id.get(w, {}).get('id_base', '?')
        print('      %-20s %s' % (w, base))
    print('\n  taille de l\'index : %d octets' % len(html))

    if not apply:
        print('\n(simulation — rien n\'a ete ecrit ; relancer avec --apply)')
        return

    if existing:
        api('widgets/' + existing['id'], 'POST',
            {'instance': {'raw': {'content': html}}})
        wid = existing['id']
        print('\n  index mis a jour :', wid)
    else:
        created = api('widgets', 'POST',
                      {'sidebar': SIDEBAR, 'id_base': 'block',
                       'instance': {'raw': {'content': html}}})
        wid = created['id']
        print('\n  index cree :', wid)

    # L'ordre final : la recherche, puis l'index. Les autres quittent la
    # colonne — WordPress les range dans les widgets desactives, ils restent
    # donc recuperables depuis l'administration.
    # La reponse du POST fait foi : une relecture immediate peut encore
    # renvoyer l'ancienne liste, le cache d'objets de WordPress n'ayant pas
    # toujours expire. C'est ce qui a fait croire, une fois, que la mise a
    # jour n'avait pas pris alors qu'elle etait faite.
    apres = api('sidebars/' + SIDEBAR, 'POST', {'widgets': garder + [wid]})['widgets']
    print('  ordre final :', ', '.join(apres))


if __name__ == '__main__':
    main()
