#!/usr/bin/env python3
"""
build_listings.py  --  interconnect Blog + Events/Projects + News + homepage.

Single source of truth = the actual detail pages under <lang>/blog/, <lang>/projects/
and <lang>/news/. This script reads them and regenerates, between AUTO markers:

  <lang>/news/index.html     the News hub grid  (all items, newest first)
  <lang>/index.html          the homepage "Latest news" block (top 3)

So adding a blog post or an event/project and running this script makes it appear
in News AND on the homepage automatically, with the correct date from the central
ARTICLE_DATES registry (tools/seo_inject.py). Run after adding/removing content:

    python tools/build_listings.py

Idempotent. Blocks are wrapped in <!-- AUTO-NEWS:START/END --> and
<!-- AUTO-LATEST-NEWS:START/END -->.
"""
import io, os, re, sys
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import seo_inject

ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
LANGS = ("en", "mk", "si")
DATES = seo_inject.ARTICLE_DATES
N_LATEST = 3

# folder -> (data-type, badge modifier, icon, {lang: label})
TYPES = {
    "news":     ("news",    "news",    "fa-bullhorn",     {"en": "News",  "mk": "Вест",   "si": "Novica"}),
    "blog":     ("blog",    "blog",    "fa-pen-nib",      {"en": "Blog",  "mk": "Блог",   "si": "Blog"}),
    "projects": ("project", "project", "fa-calendar-day", {"en": "Event", "mk": "Настан", "si": "Dogodek"}),
}
MONTHS = {
 "en": ["January","February","March","April","May","June","July","August","September","October","November","December"],
 "mk": ["јануари","февруари","март","април","мај","јуни","јули","август","септември","октомври","ноември","декември"],
 "si": ["januar","februar","marec","april","maj","junij","julij","avgust","september","oktober","november","december"],
}

H1_RE   = re.compile(r"<h1>(.*?)</h1>", re.DOTALL)
DESC_RE = re.compile(r'<meta name="description" content="(.*?)"')
FIG_RE  = re.compile(r'<figure class="(?:article-main-image|news-article-hero)">(.*?)</figure>', re.DOTALL)
IMG_RE  = re.compile(r'<img\b([^>]*)>')
ATTR_RE = lambda a, s: (re.search(a + r'="([^"]*)"', s) or [None, None])[1]


def strip_tags(s):
    return re.sub(r"<[^>]+>", "", s).strip()


def fmt_date(lang, iso):
    y, m, d = iso.split("-")
    mon = MONTHS[lang][int(m) - 1]
    return f"{int(d)}. {mon} {y}" if lang == "si" else f"{int(d)} {mon} {y}"


def rootrel_img(src):
    m = re.search(r'(images/.*)$', src)
    return "/" + m.group(1) if m else src


def collect_items(lang):
    items = []
    for folder in ("news", "blog", "projects"):
        base = os.path.join(ROOT, lang, folder)
        if not os.path.isdir(base):
            continue
        for slug in sorted(os.listdir(base)):
            page = os.path.join(base, slug, "index.html")
            if not os.path.exists(page):
                continue
            key = f"{folder}/{slug}"
            date = DATES.get(key, {})
            iso = date.get("published") or date.get("modified")
            if not iso:
                # Autonomous fallback: read the date straight off the English
                # page, so new content lists without an ARTICLE_DATES entry.
                iso = seo_inject.extract_publish_date(seo_inject.en_content_for(key))
            if not iso:
                continue  # only list genuinely dated content
            html = io.open(page, encoding="utf-8").read()
            h1 = H1_RE.search(html)
            title = strip_tags(h1.group(1)) if h1 else slug
            desc = (DESC_RE.search(html) or [None, ""])[1]
            img = None
            fig = FIG_RE.search(html)
            if fig:
                im = IMG_RE.search(fig.group(1))
                if im:
                    a = im.group(1)
                    src = ATTR_RE("src", a)
                    if src:
                        img = {"src": rootrel_img(src), "w": ATTR_RE("width", a),
                               "h": ATTR_RE("height", a), "alt": ATTR_RE("alt", a) or title}
            items.append({"folder": folder, "slug": slug, "iso": iso, "title": title,
                          "excerpt": desc, "img": img, "url": f"/{lang}/{folder}/{slug}/"})
    items.sort(key=lambda x: x["iso"], reverse=True)
    return items


def badge(folder, lang):
    dtype, mod, icon, labels = TYPES[folder]
    return dtype, f'<span class="news-badge news-badge--{mod}"><i class="fas {icon}" aria-hidden="true"></i> {labels[lang]}</span>'


def esc_attr(s):
    return s.replace('"', "&quot;")


def media(it, folder, lang):
    _, _, icon, _ = TYPES[folder]
    if it["img"]:
        i = it["img"]
        wh = (f' width="{i["w"]}"' if i["w"] else "") + (f' height="{i["h"]}"' if i["h"] else "")
        return f'<img{wh} src="{i["src"]}" alt="{esc_attr(i["alt"])}" loading="lazy">'
    return f'<div class="news-ph"><i class="fas {icon}" aria-hidden="true"></i></div>'


def news_hub_card(it, lang):
    dtype, badge_html = badge(it["folder"], lang)
    return (
      f'                <article class="newshub-card" data-type="{dtype}">\n'
      f'                    <a class="newshub-media" href="{it["url"]}">\n'
      f'                        {media(it, it["folder"], lang)}\n'
      f'                        {badge_html}\n'
      f'                    </a>\n'
      f'                    <div class="newshub-body">\n'
      f'                        <p class="newshub-date">{fmt_date(lang, it["iso"])}</p>\n'
      f'                        <h3><a href="{it["url"]}">{it["title"]}</a></h3>\n'
      f'                        <p class="newshub-excerpt">{it["excerpt"]}</p>\n'
      f'                    </div>\n'
      f'                </article>'
    )


def landing_card(it, lang):
    dtype, badge_html = badge(it["folder"], lang)
    return (
      f'                <article class="news-card" data-type="{dtype}">\n'
      f'                    <a class="news-card-media" href="{it["url"]}">\n'
      f'                        {media(it, it["folder"], lang)}\n'
      f'                        {badge_html}\n'
      f'                    </a>\n'
      f'                    <div class="card-content">\n'
      f'                        <p class="author">{fmt_date(lang, it["iso"])}</p>\n'
      f'                        <h3><a href="{it["url"]}">{it["title"]}</a></h3>\n'
      f'                    </div>\n'
      f'                </article>'
    )


def replace_block(html, start, end, inner, insert_pat=None):
    """Replace content between markers; if markers absent, use insert_pat regex
    (group1)(...content...)(group3) to seed them."""
    block = f'{start}\n{inner}\n            {end}'
    if start in html and end in html:
        return re.sub(re.escape(start) + r".*?" + re.escape(end), block, html, count=1, flags=re.DOTALL)
    if insert_pat:
        m = insert_pat.search(html)
        if m:
            return html[:m.start()] + m.group(1) + "\n" + block + "\n            " + m.group(3) + html[m.end():]
    return html


NEWS_GRID_PAT = re.compile(r'(<div class="newshub-grid">)(.*)(</div>\s*<nav class="pagination newshub-pagination")', re.DOTALL)
LANDING_PAT   = re.compile(r'(<div class="news-articles">)(.*?)(</div>\s*<a href="[^"]*" class="btn btn-secondary">)', re.DOTALL)


def run():
    for lang in LANGS:
        items = collect_items(lang)
        if not items:
            print(f"  [{lang}] no dated content found"); continue

        hub = os.path.join(ROOT, lang, "news", "index.html")
        if os.path.exists(hub):
            html = io.open(hub, encoding="utf-8").read()
            inner = "\n".join(news_hub_card(it, lang) for it in items)
            html = replace_block(html, "<!-- AUTO-NEWS:START -->", "<!-- AUTO-NEWS:END -->",
                                  inner, NEWS_GRID_PAT)
            io.open(hub, "w", encoding="utf-8").write(html)

        home = os.path.join(ROOT, lang, "index.html")
        if os.path.exists(home):
            html = io.open(home, encoding="utf-8").read()
            inner = "\n".join(landing_card(it, lang) for it in items[:N_LATEST])
            html = replace_block(html, "<!-- AUTO-LATEST-NEWS:START -->", "<!-- AUTO-LATEST-NEWS:END -->",
                                  inner, LANDING_PAT)
            io.open(home, "w", encoding="utf-8").write(html)

        print(f"  [{lang}] News hub: {len(items)} items | homepage: top {min(N_LATEST, len(items))}")


if __name__ == "__main__":
    print("Rebuilding News hub + homepage 'Latest news' from Blog/Projects/News pages:")
    run()
    print("Done.")
