#!/usr/bin/env python3
"""
Characterization tests for tools/build_content.py (U2).

The core guarantee (plan R5): rendering every content/ entry reproduces the
CURRENT live detail page, compared semantically (whitespace- and
attribute-order-insensitive). This is what protects against SEO/layout
regressions when the build takes over page generation.

Run directly:   python tools/tests/test_build_content.py
Or with pytest: pytest tools/tests/test_build_content.py
"""
import os
import sys
import unittest

TOOLS = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
ROOT = os.path.dirname(TOOLS)
sys.path.insert(0, TOOLS)

import build_content as bc  # noqa: E402


def _render(out_rel):
    """Render the content entry whose output path ends with out_rel."""
    for meta, body, _path in bc.iter_entries():
        if bc.output_path(meta).replace(os.sep, "/").endswith(out_rel):
            base = bc._template_html(meta)
            return meta, bc.build_entry(meta, body, base)
    raise AssertionError(f"no content entry maps to {out_rel}")


def _current(out_rel):
    with open(os.path.join(ROOT, out_rel), encoding="utf-8") as f:
        return f.read()


class ReproducesEverySite(unittest.TestCase):
    def test_all_pages_semantically_equivalent(self):
        """Every rendered page must equal the current page (semantic compare)."""
        _built, mismatches = bc.run(check=True, write=False)
        self.assertEqual(mismatches, [], f"pages diverged from the live site: {mismatches}")


class RenderIsIdempotent(unittest.TestCase):
    def test_rendering_output_again_is_a_fixpoint(self):
        """Re-rendering a page using its own rendered output as the template
        must be byte-identical — otherwise the full build churns every run
        (e.g. compounding indentation)."""
        drift = []
        for meta, body, _path in bc.iter_entries():
            r1 = bc.build_entry(meta, body, bc._template_html(meta))
            r2 = bc.build_entry(meta, body, r1)
            if r1 != r2:
                drift.append(bc.output_path(meta).replace(os.sep, "/"))
        self.assertEqual(drift, [], f"non-idempotent renders: {drift}")


class TitleSuffixFaithful(unittest.TestCase):
    def test_cyrillic_suffix_preserved(self):
        # MK pages use ' - МСОС' (Cyrillic); it must survive, not be dropped.
        meta, rendered = _render("mk/projects/macedonian-student-night-in-ljubljana/index.html")
        self.assertIn("- МСОС</title>", rendered)  # - МСОС

    def test_no_suffix_stays_absent(self):
        # Some event pages have no ' - MSOS' suffix; we must not invent one.
        _meta, rendered = _render("en/projects/morning-coffee-in-front-of-ctk/index.html")
        self.assertNotIn("- MSOS</title>", rendered)


class RichContentSurvives(unittest.TestCase):
    def test_event_gallery_and_lightbox_render(self):
        _meta, rendered = _render("en/projects/paint-and-wine-at-sunset/index.html")
        self.assertIn("event-gallery", rendered)
        self.assertIn("eg-thumb", rendered)

    def test_blog_guidebox_and_scrollspy_render(self):
        _meta, rendered = _render("en/blog/how-to-open-slovenian-bank-account/index.html")
        self.assertIn("guide-box", rendered)
        self.assertIn('class="scrollspy-nav"', rendered)


class ScrollspyPreservesCuratedLabels(unittest.TestCase):
    def test_hand_shortened_labels_not_overwritten(self):
        # The body H2 is long; the sidebar label was shortened by hand. The
        # rendered page must keep the curated short label, not the H2 text.
        _meta, rendered = _render("en/projects/macedonian-student-night-in-ljubljana/index.html")
        self.assertIn(">Idea to Movement</a>", rendered)


class HeroPathDepth(unittest.TestCase):
    def test_hero_src_uses_relative_depth(self):
        _meta, rendered = _render("en/blog/how-to-open-slovenian-bank-account/index.html")
        # Hero must resolve from the page's depth, not a root-absolute path.
        self.assertIn('src="../../../images/', rendered)
        self.assertNotIn('src="/images/', rendered)


class NewsStructure(unittest.TestCase):
    def test_news_uses_news_article_body(self):
        _meta, rendered = _render("en/news/proof-of-means-updated-2026/index.html")
        self.assertIn('class="news-article-body"', rendered)
        self.assertIn('class="news-article-date"', rendered)


if __name__ == "__main__":
    unittest.main(verbosity=2)
