"""Step definitions for resource_repository.feature."""

from __future__ import annotations

import contextlib
from datetime import UTC, datetime

from behave import given, then, when
from behave.runner import Context
from sqlalchemy import create_engine, event
from sqlalchemy.orm import Session, sessionmaker

from cleveragents.domain.models.core.resource import (
    PhysVirt,
    Resource,
    ResourceCapabilities,
)
from cleveragents.domain.models.core.resource_type import (
    ResourceKind,
    ResourceTypeArgument,
    ResourceTypeSpec,
    SandboxStrategy,
)
from cleveragents.infrastructure.database.models import (
    Base,
    ResourceEdgeModel,
)
from cleveragents.infrastructure.database.repositories import (
    DuplicateResourceError,
    DuplicateResourceTypeError,
    ResourceHasEdgesError,
    ResourceNotFoundRepoError,
    ResourceRepository,
    ResourceTypeHasResourcesError,
    ResourceTypeNotFoundError,
    ResourceTypeRepository,
)

# Crockford base32 alphabet for generating ULIDs
_CB32 = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"
_ULID_CTR = 0


def _next_ulid() -> str:
    """Return a unique, valid ULID string for each call."""
    global _ULID_CTR
    _ULID_CTR += 1
    n = _ULID_CTR
    suffix = ""
    for _ in range(8):
        suffix = _CB32[n % 32] + suffix
        n //= 32
    return f"01HGZ6FE0AQDYTR4BX{suffix}"


def _make_resource_type_spec(
    name: str = "myorg/test-type",
    user_addable: bool = True,
) -> ResourceTypeSpec:
    """Create a minimal valid ResourceTypeSpec."""
    return ResourceTypeSpec(
        name=name,
        description="Test resource type",
        resource_kind=ResourceKind.PHYSICAL,
        sandbox_strategy=SandboxStrategy.GIT_WORKTREE,
        user_addable=user_addable,
        cli_args=[],
        parent_types=[],
        child_types=[],
        auto_discovery=None,
        equivalence=None,
        handler=None,
        capabilities={
            "read": True,
            "write": True,
            "sandbox": True,
            "checkpoint": False,
        },
        built_in=False,
    )


def _make_resource(
    name: str | None = "myorg/test-resource",
    type_name: str = "myorg/test-type",
    resource_id: str | None = None,
) -> Resource:
    """Create a minimal valid Resource domain object."""
    return Resource(
        resource_id=resource_id or _next_ulid(),
        name=name,
        resource_type_name=type_name,
        classification=PhysVirt.PHYSICAL,
        description="Test resource",
        properties={},
        location="/tmp/test",
        content_hash=None,
        sandbox_strategy=None,
        capabilities=ResourceCapabilities(
            readable=True,
            writable=True,
            sandboxable=True,
            checkpointable=False,
        ),
        created_at=datetime.now(tz=UTC),
        updated_at=datetime.now(tz=UTC),
    )


# ── Background ────────────────────────────────────────────────


@given("a clean resource repository database")
def step_clean_resource_repo_db(context: Context) -> None:
    from sqlalchemy.pool import StaticPool

    engine = create_engine(
        "sqlite:///:memory:",
        poolclass=StaticPool,
        connect_args={"check_same_thread": False},
    )

    @event.listens_for(engine, "connect")
    def _set_fk_pragma(dbapi_conn: object, _rec: object) -> None:
        cursor = dbapi_conn.cursor()  # type: ignore[union-attr]
        cursor.execute("PRAGMA foreign_keys=ON")
        cursor.close()

    Base.metadata.create_all(engine)

    # Use a single shared session so that flush() data is visible across
    # all repository calls within the same scenario (avoids rollback issues
    # when multiple Session objects share the same SQLite :memory: connection).
    _shared_session = sessionmaker(bind=engine)()

    def _session_factory() -> object:
        return _shared_session

    context.repo_session_factory = _session_factory
    context.repo_engine = engine
    context.repo_error = None


@given("a resource type repository backed by the database")
def step_resource_type_repo(context: Context) -> None:
    context.rt_repo = ResourceTypeRepository(context.repo_session_factory)


@given("a resource repository backed by the database")
def step_resource_repo(context: Context) -> None:
    context.res_repo = ResourceRepository(context.repo_session_factory)


# ── ResourceTypeRepository: Create ────────────────────────────


@given('a valid resource type spec named "{name}"')
def step_valid_rt_spec(context: Context, name: str) -> None:
    context.rt_spec = _make_resource_type_spec(name=name)


@given("the resource type has already been saved once")
def step_rt_saved_once(context: Context) -> None:
    context.rt_repo.create(context.rt_spec)


@when("the resource type is saved through the repository")
def step_save_rt(context: Context) -> None:
    try:
        context.rt_repo.create(context.rt_spec)
        context.repo_error = None
    except Exception as exc:
        context.repo_error = exc


@when('a second resource type with the same name "{name}" is saved')
def step_save_dup_rt(context: Context, name: str) -> None:
    try:
        dup = _make_resource_type_spec(name=name)
        context.rt_repo.create(dup)
        context.repo_error = None
    except Exception as exc:
        context.repo_error = exc


@then("the resource type repository should not raise an error")
def step_rt_no_error(context: Context) -> None:
    assert context.repo_error is None, f"Unexpected error: {context.repo_error}"


@then('the persisted resource type should have name "{name}"')
def step_rt_persisted_name(context: Context, name: str) -> None:
    result = context.rt_repo.get(name)
    assert result is not None, f"Resource type '{name}' not found"
    assert result.name == name


@then('a DuplicateResourceTypeError should be raised mentioning "{name}"')
def step_dup_rt_error(context: Context, name: str) -> None:
    assert isinstance(context.repo_error, DuplicateResourceTypeError), (
        f"Expected DuplicateResourceTypeError, got {type(context.repo_error)}"
    )
    assert name in str(context.repo_error)


# ── ResourceTypeRepository: Read ──────────────────────────────


@given("the resource type has been saved through the repository")
def step_rt_saved(context: Context) -> None:
    context.rt_repo.create(context.rt_spec)


@when('the resource type is looked up by name "{name}"')
def step_lookup_rt_by_name(context: Context, name: str) -> None:
    context.rt_result = context.rt_repo.get(name)


@then('the returned resource type should have name "{name}"')
def step_rt_has_name(context: Context, name: str) -> None:
    assert context.rt_result is not None, "Expected a resource type, got None"
    assert context.rt_result.name == name


@then("no resource type should be returned")
def step_no_rt_returned(context: Context) -> None:
    assert context.rt_result is None, f"Expected None, got {context.rt_result}"


# ── ResourceTypeRepository: List ──────────────────────────────


@given('resource types "{name1}" and "{name2}" have been saved')
def step_two_rts_saved(context: Context, name1: str, name2: str) -> None:
    context.rt_repo.create(_make_resource_type_spec(name=name1))
    context.rt_repo.create(_make_resource_type_spec(name=name2))


@when("all resource types are listed")
def step_list_all_rts(context: Context) -> None:
    context.rt_list = context.rt_repo.list_types()


@when('resource types are listed with namespace "{namespace}"')
def step_list_rts_by_namespace(context: Context, namespace: str) -> None:
    context.rt_list = context.rt_repo.list_types(namespace=namespace)


@given('a resource type "{name}" with user_addable {flag} is saved')
def step_rt_with_addable_flag(context: Context, name: str, flag: str) -> None:
    spec = _make_resource_type_spec(name=name, user_addable=flag.lower() == "true")
    context.rt_repo.create(spec)


@when("resource types are listed with user_addable {flag}")
def step_list_rts_by_addable(context: Context, flag: str) -> None:
    context.rt_list = context.rt_repo.list_types(user_addable=flag.lower() == "true")


@then("the resource type list should have {count:d} entries")
def step_rt_list_count(context: Context, count: int) -> None:
    assert len(context.rt_list) == count, (
        f"Expected {count} entries, got {len(context.rt_list)}"
    )


@then('the first resource type in the list should be "{name}"')
def step_rt_list_first(context: Context, name: str) -> None:
    assert context.rt_list[0].name == name, (
        f"Expected first entry '{name}', got '{context.rt_list[0].name}'"
    )


# ── ResourceTypeRepository: Update ────────────────────────────


@when('the resource type description is updated to "{desc}"')
def step_update_rt_desc(context: Context, desc: str) -> None:
    updated = ResourceTypeSpec(
        name=context.rt_spec.name,
        description=desc,
        resource_kind=context.rt_spec.resource_kind,
        sandbox_strategy=context.rt_spec.sandbox_strategy,
        user_addable=context.rt_spec.user_addable,
        cli_args=context.rt_spec.cli_args,
        parent_types=context.rt_spec.parent_types,
        child_types=context.rt_spec.child_types,
        auto_discovery=context.rt_spec.auto_discovery,
        equivalence=context.rt_spec.equivalence,
        handler=context.rt_spec.handler,
        capabilities=context.rt_spec.capabilities,
        built_in=context.rt_spec.built_in,
    )
    context.rt_repo.update(updated)


@then('the resource type should be retrievable with description "{desc}"')
def step_rt_has_desc(context: Context, desc: str) -> None:
    result = context.rt_repo.get(context.rt_spec.name)
    assert result is not None
    assert result.description == desc, (
        f"Expected description '{desc}', got '{result.description}'"
    )


@when('a non-existent resource type "{name}" is updated')
def step_update_nonexistent_rt(context: Context, name: str) -> None:
    try:
        fake = _make_resource_type_spec(name=name)
        context.rt_repo.update(fake)
        context.repo_error = None
    except Exception as exc:
        context.repo_error = exc


@then("a ResourceTypeNotFoundError should be raised")
def step_rt_not_found_error(context: Context) -> None:
    assert isinstance(context.repo_error, ResourceTypeNotFoundError), (
        f"Expected ResourceTypeNotFoundError, got {type(context.repo_error)}"
    )


# ── ResourceTypeRepository: Delete ────────────────────────────


@when('the resource type "{name}" is deleted')
def step_delete_rt(context: Context, name: str) -> None:
    try:
        context.delete_result = context.rt_repo.delete(name)
        context.repo_error = None
    except Exception as exc:
        context.repo_error = exc
        context.delete_result = None


@then("the resource type deletion should return true")
def step_rt_delete_true(context: Context) -> None:
    assert context.delete_result is True, f"Expected True, got {context.delete_result}"


@then("the resource type deletion should return false")
def step_rt_delete_false(context: Context) -> None:
    assert context.delete_result is False, (
        f"Expected False, got {context.delete_result}"
    )


@then('the resource type "{name}" should no longer exist')
def step_rt_no_longer_exists(context: Context, name: str) -> None:
    result = context.rt_repo.get(name)
    assert result is None, f"Resource type '{name}' still exists"


@given('a resource of type "{type_name}" exists in the database')
def step_resource_of_type_exists(context: Context, type_name: str) -> None:
    res = _make_resource(name=None, type_name=type_name)
    context.res_repo.create(res)


@then("a ResourceTypeHasResourcesError should be raised")
def step_rt_has_resources_error(context: Context) -> None:
    assert isinstance(context.repo_error, ResourceTypeHasResourcesError), (
        f"Expected ResourceTypeHasResourcesError, got {type(context.repo_error)}"
    )


# ── ResourceRepository: Create ────────────────────────────────


@given('a resource type "{name}" exists for resource tests')
def step_rt_exists_for_res(context: Context, name: str) -> None:
    with contextlib.suppress(DuplicateResourceTypeError):
        context.rt_repo.create(_make_resource_type_spec(name=name))
    context.res_type_name = name


@given('a valid resource domain object with name "{name}"')
def step_valid_res_obj(context: Context, name: str) -> None:
    type_name = "myorg/res-type"
    if hasattr(context, "res_type_name"):
        type_name = context.res_type_name
    context.res_obj = _make_resource(name=name, type_name=type_name)
    context.res_type_name = type_name


@given('a valid resource domain object with type "{type_name}"')
def step_valid_res_obj_with_type(context: Context, type_name: str) -> None:
    context.res_obj = _make_resource(name="myorg/orphan-res", type_name=type_name)


@when("the resource is saved through the repository")
def step_save_res(context: Context) -> None:
    try:
        context.res_repo.create(context.res_obj)
        context.repo_error = None
    except Exception as exc:
        context.repo_error = exc


@then("the resource repository should not raise an error")
def step_res_no_error(context: Context) -> None:
    assert context.repo_error is None, f"Unexpected error: {context.repo_error}"


@then('the persisted resource should have name "{name}"')
def step_res_persisted_name(context: Context, name: str) -> None:
    result = context.res_repo.get_by_name(name)
    assert result is not None, f"Resource '{name}' not found"
    assert result.name == name


@then('a ResourceTypeNotFoundError should be raised for type "{type_name}"')
def step_rt_not_found_for_type(context: Context, type_name: str) -> None:
    assert isinstance(context.repo_error, ResourceTypeNotFoundError), (
        f"Expected ResourceTypeNotFoundError, got {type(context.repo_error)}"
    )
    assert type_name in str(context.repo_error)


@given('a resource with name "{name}" has been saved')
def step_res_named_saved(context: Context, name: str) -> None:
    # Determine the type name from the most recent resource type created
    type_name = "myorg/res-type"
    if hasattr(context, "res_type_name"):
        type_name = context.res_type_name
    else:
        # Try to guess from existing resource types
        for attr_name in ("rt_spec",):
            if hasattr(context, attr_name):
                type_name = getattr(context, attr_name).name
                break

    # Use type name from context if set up by previous step
    rt_list = context.rt_repo.list_types()
    if rt_list:
        type_name = rt_list[-1].name

    res = _make_resource(name=name, type_name=type_name)
    context.res_repo.create(res)
    context.last_saved_resource = res
    context.res_type_name = type_name


@when('a second resource with name "{name}" is saved')
def step_save_dup_res(context: Context, name: str) -> None:
    try:
        dup = _make_resource(name=name, type_name=context.res_type_name)
        context.res_repo.create(dup)
        context.repo_error = None
    except Exception as exc:
        context.repo_error = exc


@then('a DuplicateResourceError should be raised mentioning "{name}"')
def step_dup_res_error(context: Context, name: str) -> None:
    assert isinstance(context.repo_error, DuplicateResourceError), (
        f"Expected DuplicateResourceError, got {type(context.repo_error)}"
    )
    assert name in str(context.repo_error)


# ── ResourceRepository: Read ──────────────────────────────────


@given("a resource with known ULID has been saved")
def step_res_known_ulid_saved(context: Context) -> None:
    rt_list = context.rt_repo.list_types()
    type_name = rt_list[-1].name if rt_list else "myorg/get-type"
    ulid = _next_ulid()
    res = _make_resource(
        name=f"myorg/ulid-res-{ulid[-6:]}",
        type_name=type_name,
        resource_id=ulid,
    )
    context.res_repo.create(res)
    context.known_ulid = ulid
    context.last_saved_resource = res


@when("the resource is looked up by its ULID")
def step_lookup_res_by_ulid(context: Context) -> None:
    context.res_result = context.res_repo.get(context.known_ulid)


@then("the returned resource should have the correct ULID")
def step_res_has_ulid(context: Context) -> None:
    assert context.res_result is not None, "Expected a resource, got None"
    assert context.res_result.resource_id == context.known_ulid


@when('the resource is looked up by name "{name}"')
def step_lookup_res_by_name(context: Context, name: str) -> None:
    context.res_result = context.res_repo.get_by_name(name)


@then('the returned resource should have name "{name}"')
def step_res_has_name(context: Context, name: str) -> None:
    assert context.res_result is not None, "Expected a resource, got None"
    assert context.res_result.name == name


@when('the resource is looked up by ULID "{ulid}"')
def step_lookup_res_by_specific_ulid(context: Context, ulid: str) -> None:
    context.res_result = context.res_repo.get(ulid)


@then("no resource should be returned")
def step_no_res_returned(context: Context) -> None:
    assert context.res_result is None, f"Expected None, got {context.res_result}"


# ── ResourceRepository: List ──────────────────────────────────


@given('{count:d} resources of type "{type_name}" have been saved')
def step_n_resources_saved(context: Context, count: int, type_name: str) -> None:
    for i in range(count):
        res = _make_resource(
            name=f"myorg/batch-{type_name.split('/')[-1]}-{i}",
            type_name=type_name,
        )
        context.res_repo.create(res)


@when("resources are listed with limit {limit:d} and offset {offset:d}")
def step_list_res_paginated(context: Context, limit: int, offset: int) -> None:
    context.res_list = context.res_repo.list_resources(limit=limit, offset=offset)


@when('resources are listed with type_name "{type_name}"')
def step_list_res_by_type(context: Context, type_name: str) -> None:
    context.res_list = context.res_repo.list_resources(type_name=type_name)


@given('resource types "{name1}" and "{name2}" exist for resource tests')
def step_two_rts_for_res(context: Context, name1: str, name2: str) -> None:
    for n in (name1, name2):
        with contextlib.suppress(DuplicateResourceTypeError):
            context.rt_repo.create(_make_resource_type_spec(name=n))


@then("the resource list should have {count:d} entries")
def step_res_list_count(context: Context, count: int) -> None:
    assert len(context.res_list) == count, (
        f"Expected {count} entries, got {len(context.res_list)}"
    )


# ── ResourceRepository: Update ────────────────────────────────


@when('the resource description is updated to "{desc}"')
def step_update_res_desc(context: Context, desc: str) -> None:
    original = context.last_saved_resource
    updated = Resource(
        resource_id=original.resource_id,
        name=original.name,
        resource_type_name=original.resource_type_name,
        classification=PhysVirt.PHYSICAL,
        description=desc,
        properties=original.properties,
        location=original.location,
        content_hash=original.content_hash,
        sandbox_strategy=original.sandbox_strategy,
        capabilities=original.capabilities,
        created_at=original.created_at,
        updated_at=datetime.now(tz=UTC),
    )
    context.res_repo.update(updated)


@then('the resource should be retrievable with description "{desc}"')
def step_res_has_desc(context: Context, desc: str) -> None:
    result = context.res_repo.get(context.last_saved_resource.resource_id)
    assert result is not None
    assert result.description == desc, (
        f"Expected description '{desc}', got '{result.description}'"
    )


@when('a non-existent resource with ULID "{ulid}" is updated')
def step_update_nonexistent_res(context: Context, ulid: str) -> None:
    try:
        fake = _make_resource(
            name="myorg/phantom-res",
            type_name="myorg/phantom-type",
            resource_id=ulid,
        )
        context.res_repo.update(fake)
        context.repo_error = None
    except Exception as exc:
        context.repo_error = exc


@then("a ResourceNotFoundRepoError should be raised")
def step_res_not_found_error(context: Context) -> None:
    assert isinstance(context.repo_error, ResourceNotFoundRepoError), (
        f"Expected ResourceNotFoundRepoError, got {type(context.repo_error)}"
    )


# ── ResourceRepository: Delete ────────────────────────────────


@when("the resource is deleted by its ULID")
def step_delete_res_by_ulid(context: Context) -> None:
    try:
        ulid = context.last_saved_resource.resource_id
        context.delete_result = context.res_repo.delete(ulid)
        context.repo_error = None
    except Exception as exc:
        context.repo_error = exc
        context.delete_result = None


@then("the resource deletion should return true")
def step_res_delete_true(context: Context) -> None:
    assert context.delete_result is True, f"Expected True, got {context.delete_result}"


@then("the resource deletion should return false")
def step_res_delete_false(context: Context) -> None:
    assert context.delete_result is False, (
        f"Expected False, got {context.delete_result}"
    )


@then("the resource should no longer exist")
def step_res_no_longer_exists(context: Context) -> None:
    result = context.res_repo.get(context.last_saved_resource.resource_id)
    assert result is None, "Resource still exists"


@when('the resource with ULID "{ulid}" is deleted')
def step_delete_res_specific_ulid(context: Context, ulid: str) -> None:
    try:
        context.delete_result = context.res_repo.delete(ulid)
        context.repo_error = None
    except Exception as exc:
        context.repo_error = exc
        context.delete_result = None


@given('a parent and child resource of type "{type_name}" are linked')
def step_parent_child_linked(context: Context, type_name: str) -> None:
    parent = _make_resource(
        name=f"myorg/parent-{_next_ulid()[-6:]}",
        type_name=type_name,
    )
    child = _make_resource(
        name=f"myorg/child-{_next_ulid()[-6:]}",
        type_name=type_name,
    )
    context.res_repo.create(parent)
    context.res_repo.create(child)
    context.parent_resource = parent
    context.child_resource = child

    # Insert edge directly via session
    session: Session = context.repo_session_factory()
    edge = ResourceEdgeModel(
        parent_id=parent.resource_id,
        child_id=child.resource_id,
        link_type="contains",
        auto_discovered=False,
        created_at=datetime.now(tz=UTC).isoformat(),
    )
    session.add(edge)
    session.flush()
    session.commit()


@when("the parent resource is deleted")
def step_delete_parent_res(context: Context) -> None:
    try:
        context.delete_result = context.res_repo.delete(
            context.parent_resource.resource_id
        )
        context.repo_error = None
    except Exception as exc:
        context.repo_error = exc
        context.delete_result = None


@then("a ResourceHasEdgesError should be raised")
def step_res_has_edges_error(context: Context) -> None:
    assert isinstance(context.repo_error, ResourceHasEdgesError), (
        f"Expected ResourceHasEdgesError, got {type(context.repo_error)}"
    )


# ── ResourceRepository: resolve_namespaced_name ───────────────


@when('the resource is resolved by name_or_id "{name_or_id}"')
def step_resolve_res(context: Context, name_or_id: str) -> None:
    context.resolved_result = context.res_repo.resolve_namespaced_name(name_or_id)


@then('the resolved resource should have name "{name}"')
def step_resolved_has_name(context: Context, name: str) -> None:
    assert context.resolved_result is not None, "Expected a resource, got None"
    assert context.resolved_result.name == name


@then("no resolved resource should be returned")
def step_no_resolved_res(context: Context) -> None:
    assert context.resolved_result is None, (
        f"Expected None, got {context.resolved_result}"
    )


@given("a resource with known ULID has been saved for resolve test")
def step_res_ulid_for_resolve(context: Context) -> None:
    type_name = getattr(context, "res_type_name", "myorg/resolve-ulid-type")
    ulid = _next_ulid()
    res = _make_resource(
        name=None,
        type_name=type_name,
        resource_id=ulid,
    )
    context.res_repo.create(res)
    context.known_ulid = ulid
    context.last_saved_resource = res


@when("the resource is resolved by its ULID")
def step_resolve_by_ulid(context: Context) -> None:
    context.resolved_result = context.res_repo.resolve_namespaced_name(
        context.known_ulid
    )


@then("the resolved resource should have the correct ULID")
def step_resolved_has_ulid(context: Context) -> None:
    assert context.resolved_result is not None, "Expected a resource, got None"
    assert context.resolved_result.resource_id == context.known_ulid


# ── ResourceTypeRepository: domain round-trip ─────────────────


@given("a full resource type spec with cli_args and capabilities")
def step_full_rt_spec(context: Context) -> None:
    context.full_rt_spec = ResourceTypeSpec(
        name="myorg/full-type",
        description="Full test type with all fields",
        resource_kind=ResourceKind.PHYSICAL,
        sandbox_strategy=SandboxStrategy.COPY_ON_WRITE,
        user_addable=True,
        cli_args=[
            ResourceTypeArgument(
                name="path",
                type="path",
                required=True,
                description="Path to resource",
            ),
            ResourceTypeArgument(
                name="branch",
                type="string",
                required=False,
                description="Branch name",
                default="main",
            ),
        ],
        parent_types=["myorg/parent-type"],
        child_types=["myorg/child-type"],
        auto_discovery={"strategy": "glob", "pattern": "**/*.py"},
        equivalence=None,
        handler="myorg.handlers:FullHandler",
        capabilities={
            "read": True,
            "write": True,
            "sandbox": True,
            "checkpoint": True,
        },
        built_in=False,
    )


@when("the full resource type is saved and retrieved")
def step_save_and_retrieve_full_rt(context: Context) -> None:
    context.rt_repo.create(context.full_rt_spec)
    context.restored_rt = context.rt_repo.get(context.full_rt_spec.name)


@then("the restored resource type should match the original fields")
def step_rt_fields_match(context: Context) -> None:
    original = context.full_rt_spec
    restored = context.restored_rt
    assert restored is not None, "Restored resource type is None"
    assert restored.name == original.name
    assert restored.description == original.description
    assert restored.resource_kind == original.resource_kind
    assert restored.sandbox_strategy == original.sandbox_strategy
    assert restored.user_addable == original.user_addable
    assert len(restored.cli_args) == len(original.cli_args)
    assert restored.cli_args[0].name == "path"
    assert restored.cli_args[0].type == "path"
    assert restored.cli_args[0].required is True
    assert restored.cli_args[1].name == "branch"
    assert restored.cli_args[1].default == "main"
    assert restored.parent_types == original.parent_types
    assert restored.child_types == original.child_types
    assert restored.auto_discovery == original.auto_discovery
    assert restored.handler == original.handler
    assert restored.capabilities["checkpoint"] is True
