"""Unit tests for forze.application.execution.registry."""

import pytest

from forze.application.execution import ExecutionContext, Deps, Usecase, UsecaseRegistry

# ----------------------- #


def _stub_factory(ctx: ExecutionContext) -> Usecase[str, str]:
    return StubUsecase(ctx=ctx)


class StubUsecase(Usecase[str, str]):
    """Minimal usecase for registry tests."""

    async def main(self, args: str) -> str:
        return f"ok:{args}"


class TestUsecaseRegistry:
    """Tests for UsecaseRegistry."""

    def test_register_returns_new_instance(self) -> None:
        reg = UsecaseRegistry()
        new = reg.register("get", lambda ctx: StubUsecase(ctx=ctx))
        assert new is not reg
        assert new.exists("get")
        assert not reg.exists("get")

    def test_register_inplace_mutates(self) -> None:
        reg = UsecaseRegistry()
        reg.register("get", lambda ctx: StubUsecase(ctx=ctx), inplace=True)
        assert reg.exists("get")

    def test_register_duplicate_raises(self) -> None:
        from forze.base.errors import CoreError

        reg = UsecaseRegistry().register("get", _stub_factory)
        with pytest.raises(CoreError, match="already registered"):
            reg.register("get", _stub_factory)

    def test_register_inplace_returns_none(self) -> None:
        reg = UsecaseRegistry()
        result = reg.register("get", _stub_factory, inplace=True)
        assert result is None
        assert reg.exists("get")

    def test_override_replaces_factory(self) -> None:
        reg = UsecaseRegistry().register("get", lambda ctx: StubUsecase(ctx=ctx))
        new = reg.override("get", lambda ctx: StubUsecase(ctx=ctx))
        assert new is not reg
        assert new.exists("get")

    def test_override_unregistered_raises(self) -> None:
        from forze.base.errors import CoreError

        reg = UsecaseRegistry()
        with pytest.raises(CoreError, match="not registered"):
            reg.override("get", _stub_factory)

    def test_override_inplace_mutates(self) -> None:
        reg = UsecaseRegistry().register("get", _stub_factory)
        result = reg.override("get", _stub_factory, inplace=True)
        assert result is None
        assert reg.exists("get")

    def test_register_many_adds_multiple(self) -> None:
        reg = UsecaseRegistry()
        new = reg.register_many({"get": _stub_factory, "create": _stub_factory})
        assert new.exists("get")
        assert new.exists("create")

    def test_register_many_duplicate_raises(self) -> None:
        from forze.base.errors import CoreError

        reg = UsecaseRegistry().register("get", _stub_factory)
        with pytest.raises(CoreError, match="already registered"):
            reg.register_many({"get": _stub_factory, "create": _stub_factory})

    def test_register_many_inplace_mutates(self) -> None:
        reg = UsecaseRegistry()
        result = reg.register_many(
            {"get": _stub_factory, "create": _stub_factory},
            inplace=True,
        )
        assert result is None
        assert reg.exists("get")
        assert reg.exists("create")

    def test_override_many_replaces_multiple(self) -> None:
        reg = (
            UsecaseRegistry()
            .register("get", _stub_factory)
            .register("create", _stub_factory)
        )
        new = reg.override_many({"get": _stub_factory, "create": _stub_factory})
        assert new.exists("get")
        assert new.exists("create")

    def test_override_many_unregistered_raises(self) -> None:
        from forze.base.errors import CoreError

        reg = UsecaseRegistry().register("get", _stub_factory)
        with pytest.raises(CoreError, match="not registered"):
            reg.override_many({"get": _stub_factory, "create": _stub_factory})

    def test_override_many_inplace_mutates(self) -> None:
        reg = UsecaseRegistry().register("get", _stub_factory)
        result = reg.override_many({"get": _stub_factory}, inplace=True)
        assert result is None
        assert reg.exists("get")

    def test_exists_returns_true_for_registered(self) -> None:
        reg = UsecaseRegistry().register("get", _stub_factory)
        assert reg.exists("get")

    def test_exists_returns_false_for_unregistered(self) -> None:
        reg = UsecaseRegistry()
        assert not reg.exists("get")

    def test_resolve_returns_usecase(self) -> None:
        reg = UsecaseRegistry().register("get", _stub_factory)
        ctx = ExecutionContext(deps=Deps())
        uc = reg.resolve("get", ctx)
        assert isinstance(uc, StubUsecase)

    def test_resolve_with_debug_plan_prints(
        self, capsys: pytest.CaptureFixture
    ) -> None:
        reg = UsecaseRegistry().register("get", _stub_factory)
        ctx = ExecutionContext(deps=Deps())
        reg.resolve("get", ctx, debug_plan=True)
        out = capsys.readouterr().out
        assert "get" in out or "plan" in out.lower() or len(out) > 0

    def test_resolve_unregistered_raises(self) -> None:
        from forze.base.errors import CoreError

        reg = UsecaseRegistry()
        ctx = ExecutionContext(deps=Deps())
        with pytest.raises(CoreError, match="not registered for operation"):
            reg.resolve("get", ctx)

    def test_extend_plan_returns_new_instance(self) -> None:
        from forze.application.execution.plan import UsecasePlan

        reg = UsecaseRegistry().register("get", _stub_factory)

        async def noop(args):
            pass

        def guard_factory(ctx):
            return noop

        plan = UsecasePlan().before("get", guard_factory, priority=1)
        new = reg.extend_plan(plan)
        assert new is not reg
        assert new.exists("get")

    def test_extend_plan_inplace_mutates(self) -> None:
        from forze.application.execution.plan import UsecasePlan

        reg = UsecaseRegistry().register("get", _stub_factory)

        async def noop(args):
            pass

        def guard_factory(ctx):
            return noop

        plan = UsecasePlan().before("get", guard_factory, priority=1)
        result = reg.extend_plan(plan, inplace=True)
        assert result is None
        assert reg.exists("get")
