# retoor <retoor@molodetz.nl>

from __future__ import annotations

import json
from typing import Iterator

from .base import (
    MEDIUM,
    LOW,
    INFO,
    PASS,
    WARN,
    FAIL,
    Check,
    PageContext,
    SiteContext,
    ok_check,
    page_check,
)

CATEGORY = "structured_data"

REQUIRED_PROPS = {
    "Article": ("headline",),
    "BlogPosting": ("headline",),
    "NewsArticle": ("headline",),
    "Product": ("name",),
    "Offer": ("price", "priceCurrency"),
    "Organization": ("name", "url"),
    "LocalBusiness": ("name", "address"),
    "BreadcrumbList": ("itemListElement",),
    "FAQPage": ("mainEntity",),
    "WebSite": ("name", "url"),
    "Person": ("name",),
    "Recipe": ("name", "recipeIngredient"),
    "Event": ("name", "startDate"),
    "VideoObject": ("name", "thumbnailUrl", "uploadDate"),
    "SoftwareApplication": ("name",),
}


def _iter_objects(parsed: object) -> Iterator[dict]:
    if isinstance(parsed, list):
        for item in parsed:
            yield from _iter_objects(item)
        return
    if not isinstance(parsed, dict):
        return
    graph = parsed.get("@graph")
    if isinstance(graph, list):
        for item in graph:
            yield from _iter_objects(item)
    if parsed.get("@type"):
        yield parsed


def _types(node: dict) -> list:
    raw = node.get("@type")
    if isinstance(raw, list):
        return [str(item) for item in raw]
    return [str(raw)] if raw else []


@page_check
def jsonld(page: PageContext, site: SiteContext) -> list:
    blocks = page.dom.get("jsonld", []) or []
    present = bool(blocks)
    results = [
        ok_check(
            "structured.jsonld_present",
            CATEGORY,
            "Structured data (JSON-LD)",
            present,
            severity=MEDIUM,
            value=f"{len(blocks)} block(s)" if present else "none",
            recommendation="Add JSON-LD structured data so search engines can render rich results.",
            warn=True,
            url=page.url,
        )
    ]
    if not present:
        return results

    invalid = 0
    nodes = []
    for block in blocks:
        try:
            parsed = json.loads(block)
        except (ValueError, TypeError):
            invalid += 1
            continue
        nodes.extend(_iter_objects(parsed))

    results.append(
        ok_check(
            "structured.jsonld_valid",
            CATEGORY,
            "JSON-LD parses",
            invalid == 0,
            severity=MEDIUM,
            value="valid" if invalid == 0 else f"{invalid} invalid block(s)",
            recommendation="Fix JSON syntax errors in structured-data blocks.",
            url=page.url,
        )
    )

    found_types = sorted({t for node in nodes for t in _types(node)})
    results.append(
        Check(
            "structured.types",
            CATEGORY,
            "Schema types",
            INFO,
            INFO,
            value=", ".join(found_types) or "none",
            details={"types": found_types},
            url=page.url,
        )
    )

    missing = []
    for node in nodes:
        for type_name in _types(node):
            required = REQUIRED_PROPS.get(type_name)
            if not required:
                continue
            absent = [prop for prop in required if not node.get(prop)]
            if absent:
                missing.append(f"{type_name}: {', '.join(absent)}")
    if missing:
        results.append(
            Check(
                "structured.required_props",
                CATEGORY,
                "Required schema properties",
                WARN,
                LOW,
                value=f"{len(missing)} type(s) missing props",
                recommendation="Add the required properties for each declared schema type.",
                details={"missing": missing[:20]},
                url=page.url,
            )
        )
    return results


@page_check
def other_markup(page: PageContext, site: SiteContext) -> Check:
    microdata = bool(page.dom.get("microdata"))
    rdfa = bool(page.dom.get("rdfa"))
    formats = []
    if microdata:
        formats.append("microdata")
    if rdfa:
        formats.append("RDFa")
    return Check(
        "structured.other_markup",
        CATEGORY,
        "Other structured-data formats",
        INFO,
        INFO,
        value=", ".join(formats) or "none",
        details={"microdata": microdata, "rdfa": rdfa},
        url=page.url,
    )
