"""Unit tests for SOAP PropertyCollector helpers."""

from __future__ import annotations

import os

import pytest

from app.vsphere.inventory import ManagedObject
from app.vsphere.soap.property_collector import (
    _descendants,
    _one_level_traverse,
    _type_matches,
    _wants_parent_traversal,
    build_prop_map,
    clear_pc_state,
    resolve_inventory_path,
    store_page_token,
    take_page_token,
    take_page_token_full,
    view_moids_from_object,
    wait_updates_xml,
)


def _obj(
    moid: str,
    type_name: str,
    name: str,
    parent: str | None,
    props: dict | None = None,
) -> ManagedObject:
    return ManagedObject(
        moid=moid,
        type=type_name,
        name=name,
        parent_moid=parent,
        props=props or {},
    )


def test_type_matches_and_parent_traversal() -> None:
    assert _type_matches("VirtualMachine", {"ManagedEntity"})
    assert _wants_parent_traversal("<path>parent</path>")


def test_descendants() -> None:
    objects = [
        _obj("group-d1", "Folder", "Datacenters", None),
        _obj("datacenter-21", "Datacenter", "DC", "group-d1"),
        _obj("group-v23", "Folder", "vm", "datacenter-21"),
        _obj("vm-101", "VirtualMachine", "web-01", "group-v23"),
    ]
    expanded = _descendants([objects[0]], objects)
    assert {o.moid for o in expanded} >= {"group-d1", "datacenter-21", "group-v23", "vm-101"}


def test_view_moids_from_object_props() -> None:
    view = _obj("view-1", "ContainerView", "view-1", None, {"view_moids": ["vm-101", "vm-102"]})
    assert view_moids_from_object(view) == ["vm-101", "vm-102"]
    props = build_prop_map(
        view,
        children=[],
        all_by_moid={
            "vm-101": _obj("vm-101", "VirtualMachine", "a", "group-v23"),
            "vm-102": _obj("vm-102", "VirtualMachine", "b", "group-v23"),
        },
    )
    assert "view" in props


@pytest.mark.integration
@pytest.mark.asyncio
async def test_page_token_roundtrip_db() -> None:
    database_url = os.getenv("TEST_DATABASE_URL") or os.getenv("DATABASE_URL")
    if not database_url:
        pytest.skip("TEST_DATABASE_URL / DATABASE_URL required")
    from app.config import Settings
    from app.db.pool import AsyncpgDatabase

    database = AsyncpgDatabase(Settings(database_url=database_url))  # type: ignore[arg-type]
    await database.connect()
    try:
        await clear_pc_state(database)
        token = await store_page_token(database, ["vm-1", "vm-2"], path_sets=["name"])
        assert await take_page_token_full(database, token) == (["vm-1", "vm-2"], ["name"])
        token2 = await store_page_token(database, ["vm-3"])
        assert await take_page_token(database, token2) == ["vm-3"]
        assert await take_page_token(database, token2) is None
    finally:
        await database.close()


@pytest.mark.integration
@pytest.mark.asyncio
async def test_wait_updates_versions_db() -> None:
    database_url = os.getenv("TEST_DATABASE_URL") or os.getenv("DATABASE_URL")
    if not database_url:
        pytest.skip("TEST_DATABASE_URL / DATABASE_URL required")
    from app.config import Settings
    from app.db.pool import AsyncpgDatabase

    database = AsyncpgDatabase(Settings(database_url=database_url))  # type: ignore[arg-type]
    await database.connect()
    try:
        await clear_pc_state(database)
        objects = [_obj("vm-101", "VirtualMachine", "web-01", "group-v23")]
        first = await wait_updates_xml(
            database, session_key="sess-1", body="<version></version>", objects=objects
        )
        assert "<version>1</version>" in first
        idle = await wait_updates_xml(
            database, session_key="sess-1", body="<version>1</version>", objects=objects
        )
        assert "<version>1</version>" in idle
        assert "objectSet" not in idle
    finally:
        await database.close()


def test_resolve_inventory_path_and_one_level() -> None:
    objects = [
        _obj("group-d1", "Folder", "Datacenters", None),
        _obj("datacenter-21", "Datacenter", "DC1", "group-d1", {"vm_folder": "group-v23"}),
        _obj("group-v23", "Folder", "vm", "datacenter-21"),
        _obj("vm-101", "VirtualMachine", "web-01", "group-v23"),
    ]
    hit = resolve_inventory_path("/DC1/vm/web-01", objects)
    assert hit is not None and hit.moid == "vm-101"
    kids = _one_level_traverse([objects[2]], objects, "<path>childEntity</path>")
    assert any(o.moid == "vm-101" for o in kids)
