# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.
from __future__ import annotations

import sys
from pathlib import Path

# Add the SDK src directory to sys.path so that importlib loads our airflow.sdk module
sys.path.insert(0, str(Path(__file__).parents[3] / "src"))
import importlib
import shutil
import subprocess
import zlib

import pytest


def read_inventory(inv_path: Path):
    """
    Read a Sphinx objects.inv inventory file and return a mapping of documented full names to their entries.
    """
    inv: dict[str, tuple[str, str, str]] = {}
    with inv_path.open("rb") as f:
        f.readline()
        f.readline()
        f.readline()
        f.readline()
        data = zlib.decompress(f.read()).decode("utf-8").splitlines()
    for line in data:
        if not line.strip():
            continue
        parts = line.split(None, 4)
        if len(parts) != 5:
            continue
        name, domain_role, prio, location, dispname = parts
        inv[name] = (domain_role, location, dispname)
    return inv


@pytest.mark.skipif(
    shutil.which("sphinx-build") is None, reason="sphinx-build not available, skipping docs inventory test"
)
def test_docs_inventory_matches_public_api(tmp_path):
    """
    Build the HTML docs and compare the generated Sphinx inventory with the public API re-exports.
    """
    docs_dir = Path(__file__).parents[3] / "docs"
    build_dir = tmp_path / "build"
    sphinx = shutil.which("sphinx-build")
    subprocess.run([sphinx, "-b", "html", "-q", str(docs_dir), str(build_dir)], check=True)
    inv_path = build_dir / "objects.inv"
    assert inv_path.exists(), "objects.inv not found after docs build"

    inv = read_inventory(inv_path)
    documented = {
        name.rsplit(".", 1)[-1]
        for name in inv.keys()
        if name.startswith("airflow.sdk.") and name.count(".") == 2
    }
    sdk = importlib.import_module("airflow.sdk")
    public = set(getattr(sdk, "__all__", [])) - {"__version__"}

    extras = {"AirflowParsingContext"}
    # we do not want to document the class for `conf` but a description is present in the docs
    excluded_from_docs = {"conf", "macros"}
    missing = (public - documented) - excluded_from_docs
    assert not missing, f"Public API items missing in docs: {missing}"
    unexpected = (documented - public) - extras
    assert not unexpected, f"Unexpected documented items: {unexpected}"
