#!/usr/bin/env python
from __future__ import annotations

import sys
from html.parser import HTMLParser
from pathlib import Path
from urllib.parse import unquote, urlsplit


class Document(HTMLParser):
    def __init__(self) -> None:
        super().__init__()
        self.references: list[str] = []
        self.ids: set[str] = set()
        self.title = ""
        self._in_title = False
        self.description = ""

    def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]]) -> None:
        values = dict(attrs)
        if value := values.get("id"):
            self.ids.add(value)
        for key in ("href", "src"):
            if value := values.get(key):
                self.references.append(value)
        if tag == "title":
            self._in_title = True
        if tag == "meta" and values.get("name") == "description":
            self.description = values.get("content", "")

    def handle_endtag(self, tag: str) -> None:
        if tag == "title":
            self._in_title = False

    def handle_data(self, data: str) -> None:
        if self._in_title:
            self.title += data


def parse(path: Path) -> Document:
    document = Document()
    document.feed(path.read_text(encoding="utf-8"))
    return document


def main() -> int:
    site = Path(__file__).resolve().parents[1] / "site"
    pages = sorted(site.rglob("*.html"))
    documents = {path.resolve(): parse(path) for path in pages}
    failures: list[str] = []

    if not (site / "assets/og.png").is_file():
        failures.append("site/assets/og.png is missing")

    for path, document in documents.items():
        relative = path.relative_to(site)
        if not document.title.strip():
            failures.append(f"{relative}: missing title")
        if not document.description.strip():
            failures.append(f"{relative}: missing description")
        for reference in document.references:
            parsed = urlsplit(reference)
            if parsed.scheme or parsed.netloc or reference.startswith(("mailto:", "tel:")):
                continue
            target = (path.parent / unquote(parsed.path)).resolve() if parsed.path else path
            if target.is_dir():
                target /= "index.html"
            if not target.exists():
                failures.append(f"{relative}: broken reference {reference!r}")
                continue
            if parsed.fragment and target.suffix == ".html":
                target_document = documents.get(target) or parse(target)
                if parsed.fragment not in target_document.ids:
                    failures.append(f"{relative}: missing fragment {reference!r}")

    if failures:
        print("\n".join(failures), file=sys.stderr)
        return 1
    print(f"Validated {len(pages)} pages and all local references")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
