"""Tests for the shared top navigation:  python3 -m unittest scripts/test_gen_nav.py"""
import html.parser
import pathlib
import sys
import unittest

sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent))
import gen_nav  # noqa: E402


class Links(html.parser.HTMLParser):
    """Collects (href, text, current, where) for every link, where = 'bar' or 'panel'."""

    def __init__(self):
        super().__init__()
        self.links, self.stack, self.open = [], [], None

    def handle_starttag(self, tag, attrs):
        a = dict(attrs)
        self.stack.append(a.get('class', ''))
        if tag == 'a':
            where = 'panel' if any('nav-panel' in c for c in self.stack) else 'bar'
            self.open = [a['href'], '', a.get('aria-current') == 'page', where]

    def handle_endtag(self, tag):
        self.stack.pop()
        if tag == 'a' and self.open:
            self.links.append(tuple(self.open))
            self.open = None

    def handle_data(self, data):
        if self.open:
            self.open[1] += data.strip()


def links(slug):
    p = Links()
    p.feed(gen_nav.render(slug))
    return p.links


HOME = gen_nav.ROOT / 'www' / 'index.html'


class Render(unittest.TestCase):
    def test_panel_lists_every_demo_once(self):
        panel = [l for l in links('mandelbrot') if l[3] == 'panel']
        self.assertEqual([h for h, *_ in panel], [f'../{slug}/' for slug, _ in gen_nav.DEMOS])

    def test_bar_shows_home_and_the_current_categorys_demos(self):
        bar = [l for l in links('fluid') if l[3] == 'bar']
        simulation = next(demos for key, _, _, demos in gen_nav.CATEGORIES if key == 'simulation')
        self.assertEqual(bar[0][:2], ('../', '← 首页'))
        self.assertEqual([h for h, *_ in bar[1:]], [f'../{slug}/' for slug, _ in simulation])

    def test_only_the_current_page_is_marked(self):
        current = [l for l in links('chip8') if l[2]]
        self.assertEqual({h for h, *_ in current}, {'../chip8/'})
        self.assertEqual({w for *_, w in current}, {'bar', 'panel'})

    def test_bar_names_the_current_category_and_page(self):
        nav = gen_nav.render('jpeg')
        self.assertIn('class="nav-cat">多媒体<', nav)
        self.assertIn('class="nav-here">JPEG<', nav)

    def test_panel_is_a_details_element_that_works_without_script(self):
        nav = gen_nav.render('sha256')
        self.assertIn('<details class="nav-all">', nav)
        self.assertIn('<summary>全部示例</summary>', nav)

    def test_every_page_gets_a_nav_that_the_generator_can_find_again(self):
        # gen_nav replaces the nav by regex; the new markup must round-trip
        page = f'<body class="has-nav">\n{gen_nav.render("wfc")}</body>'
        self.assertEqual(len(gen_nav.NAV_RE.findall(page)), 1)


class HomeNav(unittest.TestCase):
    def test_panel_links_are_relative_to_the_home_page(self):
        panel = [l for l in links(None) if l[3] == 'panel']
        self.assertEqual([h for h, *_ in panel], [f'{slug}/' for slug, _ in gen_nav.DEMOS])

    def test_bar_jumps_to_each_category_section(self):
        bar = [l for l in links(None) if l[3] == 'bar']
        self.assertEqual(bar[0][0], './')
        self.assertEqual([h for h, *_ in bar[1:]], [f'#cat-{key}' for key, *_ in gen_nav.CATEGORIES])

    def test_no_demo_is_marked_current(self):
        self.assertEqual([h for h, _, current, _ in links(None) if current], ['./'])

    def test_every_anchor_has_a_section_on_the_home_page(self):
        home = HOME.read_text(encoding='utf-8')
        for key, *_ in gen_nav.CATEGORIES:
            self.assertIn(f'<section class="cat" id="cat-{key}"', home, key)

    def test_home_page_has_the_generated_nav(self):
        home = HOME.read_text(encoding='utf-8')
        self.assertEqual(gen_nav.NAV_RE.findall(home), [gen_nav.render(None)])
        self.assertRegex(home, r'<body class="[^"]*\bhas-nav\b')
        self.assertIn('<script src="nav.js" defer></script>', home)

    def test_home_stats_count_the_demos_and_categories(self):
        stats = gen_nav.with_home_stats('<b id="stat-demos">1</b> <b id="stat-cats">2</b>')
        self.assertEqual(stats, f'<b id="stat-demos">{len(gen_nav.DEMOS)}</b> '
                                f'<b id="stat-cats">{len(gen_nav.CATEGORIES)}</b>')


class BodyClass(unittest.TestCase):
    def test_adds_has_nav_to_a_bare_body(self):
        self.assertEqual(gen_nav.with_nav_class('<body>\n'), '<body class="has-nav">\n')

    def test_keeps_other_classes(self):
        self.assertEqual(gen_nav.with_nav_class('<body class="dark">'), '<body class="dark has-nav">')

    def test_leaves_pages_that_have_it(self):
        self.assertEqual(gen_nav.with_nav_class('<body class="has-nav">'), '<body class="has-nav">')

    def test_every_demo_page_has_it(self):
        for slug, _ in gen_nav.DEMOS:
            page = (gen_nav.ROOT / 'www' / slug / 'index.html').read_text(encoding='utf-8')
            self.assertRegex(page, r'<body class="[^"]*\bhas-nav\b', slug)


if __name__ == '__main__':
    unittest.main()
