"""Run beside check-static-site.py: python test-static-site.py

Creates and removes its own temporary fixtures; does not change the site.
"""
import importlib.util
from pathlib import Path
import subprocess
import sys
from tempfile import TemporaryDirectory
import unittest

sys.dont_write_bytecode = True

SCRIPT = Path(__file__).with_name('check-static-site.py')
SPEC = importlib.util.spec_from_file_location('check_static_site', SCRIPT)
CHECKER = importlib.util.module_from_spec(SPEC)
SPEC.loader.exec_module(CHECKER)
ORIGIN = 'https://example.com'
GOOD = '''<!doctype html><html lang="ko"><head>
<meta charset="UTF-8"><title>작은 사이트</title>
<meta name="description" content="설명">
<link rel="canonical" href="https://example.com/">
</head><body><h1>작은 <span>사이트</span></h1>
<a href="#example">예제</a><section id="example">본문</section></body></html>'''
SITEMAP = '''<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url><loc>https://example.com/</loc></url></urlset>'''


class CheckStaticSite(unittest.TestCase):
    def setUp(self):
        self.temp = TemporaryDirectory(prefix='static-site-test-')
        self.root = Path(self.temp.name).resolve()
        self.addCleanup(self.temp.cleanup)
        self.write('index.html', GOOD)
        self.write('sitemap.xml', SITEMAP)

    def write(self, name, content):
        (self.root / name).write_text(content, encoding='utf-8')

    def issues(self):
        return CHECKER.audit(self.root, ORIGIN)[1]

    def test_valid(self):
        self.assertEqual(self.issues(), [])

    def test_missing_and_empty_metadata(self):
        for original, changed in [('<title>작은 사이트</title>', '<title> </title>'),
                                  ('<h1>작은 <span>사이트</span></h1>', ''),
                                  ('<meta name="description" content="설명">', '')]:
            with self.subTest(original=original):
                self.write('index.html', GOOD.replace(original, changed))
                self.assertEqual(len(self.issues()), 1)
                self.assertIn('META', self.issues()[0])

    def test_duplicate_metadata_and_id(self):
        for insertion, code in [('<title>두 번째</title>', 'META'),
                                ('<h1>두 번째</h1>', 'META'),
                                ('<meta name="description" content="둘">', 'META'),
                                ('<p id="example">중복</p>', 'ID')]:
            with self.subTest(insertion=insertion):
                self.write('index.html', GOOD.replace('</body>', insertion + '</body>'))
                self.assertTrue(any(code in item for item in self.issues()))

    def test_wrong_canonical(self):
        self.write('index.html', GOOD.replace('href="https://example.com/"', 'href="https://example.com/old"'))
        self.assertEqual(len(self.issues()), 1)
        self.assertIn('CANONICAL', self.issues()[0])

    def test_broken_local_link_and_anchor(self):
        for link, code in [('/missing', 'LINK'), ('#missing', 'ANCHOR'),
                           ('https://example.com/missing?x=1', 'LINK'),
                           ('/missing.svg', 'LINK')]:
            with self.subTest(link=link):
                self.write('index.html', GOOD.replace('href="#example"', f'href="{link}"'))
                self.assertTrue(any(code in item for item in self.issues()))

    def test_external_and_non_http_links_are_not_fetched(self):
        for link in ['https://external.example/missing', 'mailto:reader@example.com',
                     'data:text/plain,example', '//external.example/missing']:
            with self.subTest(link=link):
                self.write('index.html', GOOD.replace('href="#example"', f'href="{link}"'))
                self.assertEqual(self.issues(), [])

    def test_encoded_anchor_and_query(self):
        self.write('index.html', GOOD.replace('href="#example"', 'href="/?from=test#%ED%95%9C%EA%B8%80"')
                   .replace('id="example"', 'id="한글"'))
        self.assertEqual(self.issues(), [])

    def test_noindex_and_404_not_in_sitemap(self):
        other = GOOD.replace('</head>', '<meta name="robots" content="noindex, follow"></head>')
        self.write('private.html', other.replace('작은 사이트', '제외 페이지'))
        self.write('404.html', GOOD.replace('작은 사이트', '없는 페이지'))
        self.assertEqual(self.issues(), [])

    def test_sitemap_mismatch_duplicate_invalid(self):
        for sitemap in [SITEMAP.replace(ORIGIN + '/', ORIGIN + '/missing'),
                        SITEMAP.replace('</urlset>', '<url><loc>https://example.com/</loc></url></urlset>'),
                        '<urlset>', '<sitemapindex/>']:
            with self.subTest(sitemap=sitemap):
                self.write('sitemap.xml', sitemap)
                self.assertTrue(any('SITEMAP' in item for item in self.issues()))

    def test_duplicate_titles(self):
        self.write('guide.html', GOOD.replace('href="https://example.com/"', 'href="https://example.com/guide"'))
        self.write('sitemap.xml', SITEMAP.replace('</urlset>', '<url><loc>https://example.com/guide</loc></url></urlset>'))
        self.assertEqual(sum('TITLE' in item for item in self.issues()), 2)

    def test_invalid_utf8(self):
        (self.root / 'index.html').write_bytes(b'\xff\xfe')
        self.assertTrue(any('INPUT' in item for item in self.issues()))

    def test_base_is_explicitly_unsupported(self):
        self.write('index.html', GOOD.replace('</head>', '<base href="/elsewhere/"></head>'))
        self.assertTrue(any('UNSUPPORTED' in item for item in self.issues()))

    def test_cli_exit_codes_and_no_file_mutation(self):
        before = {p.name: p.read_bytes() for p in self.root.iterdir()}
        result = subprocess.run([sys.executable, str(SCRIPT), str(self.root), '--origin', ORIGIN],
                                capture_output=True, text=True)
        self.assertEqual(result.returncode, 0)
        self.assertIn('0 issue(s)', result.stdout)
        self.assertEqual(before, {p.name: p.read_bytes() for p in self.root.iterdir()})
        self.write('index.html', GOOD.replace('href="#example"', 'href="#missing"'))
        result = subprocess.run([sys.executable, str(SCRIPT), str(self.root), '--origin', ORIGIN], capture_output=True)
        self.assertEqual(result.returncode, 1)
        result = subprocess.run([sys.executable, str(SCRIPT), str(self.root), '--origin', ORIGIN + '/path'], capture_output=True)
        self.assertEqual(result.returncode, 2)


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