"""Helper script for plan_use_action_args_integrity.robot integration test.

Validates that ``plan use`` does not crash with an IntegrityError when the
action has arguments that were already inserted by ``action create``.

Covers issue #4174.
"""

from __future__ import annotations

import sys
from pathlib import Path

# Ensure local source tree is importable
_SRC = str(Path(__file__).resolve().parents[1] / "src")
if _SRC not in sys.path:
    sys.path.insert(0, _SRC)

# Ensure the features directory is importable for shared mocks
_FEATURES = str(Path(__file__).resolve().parents[1] / "features")
if _FEATURES not in sys.path:
    sys.path.insert(0, _FEATURES)

from mocks.test_uow_factory import build_test_uow  # noqa: E402
from sqlalchemy import func, select  # noqa: E402

from cleveragents.application.services.plan_lifecycle_service import (  # noqa: E402
    PlanLifecycleService,
)
from cleveragents.config.settings import Settings  # noqa: E402
from cleveragents.domain.models.core.action import (  # noqa: E402
    ActionArgument,
    ArgumentRequirement,
    ArgumentType,
)
from cleveragents.domain.models.core.plan import NamespacedName  # noqa: E402
from cleveragents.infrastructure.database.models import (  # noqa: E402
    ActionArgumentModel,
    ActionInvariantModel,
)


def main() -> None:
    """Run all plan use action arguments integrity tests."""
    uow, sf, engine = build_test_uow()
    settings = Settings()
    svc = PlanLifecycleService(settings=settings, unit_of_work=uow)

    try:
        # --- Test 1: plan use with arguments succeeds ---
        test_args = [
            ActionArgument(
                name="my_arg",
                arg_type=ArgumentType.STRING,
                requirement=ArgumentRequirement.OPTIONAL,
                description="A test argument",
                default_value="hello",
            ),
            ActionArgument(
                name="count",
                arg_type=ArgumentType.INTEGER,
                requirement=ArgumentRequirement.REQUIRED,
                description="A required integer argument",
            ),
        ]
        svc.create_action(
            name="local/int-args-test",
            description="Action with arguments",
            definition_of_done="Tests pass",
            strategy_actor="openai/gpt-4",
            execution_actor="openai/gpt-4",
            arguments=test_args,
        )
        plan1 = svc.use_action(
            action_name="local/int-args-test",
            arguments={"count": 42},
        )
        assert plan1.phase.value == "strategize", (
            f"FAIL: expected strategize, got {plan1.phase.value}"
        )
        assert len(plan1.identity.plan_id) == 26, "FAIL: plan_id not a ULID"
        print("PASS: plan use with arguments succeeds")

        # --- Test 2: plan use with zero arguments succeeds ---
        svc.create_action(
            name="local/int-no-args",
            description="Action without arguments",
            definition_of_done="Tests pass",
            strategy_actor="openai/gpt-4",
            execution_actor="openai/gpt-4",
            arguments=[],
        )
        plan2 = svc.use_action(action_name="local/int-no-args")
        assert plan2.phase.value == "strategize", (
            f"FAIL: expected strategize, got {plan2.phase.value}"
        )
        print("PASS: plan use with zero arguments succeeds")

        # --- Test 3: plan use with multiple arguments succeeds ---
        multi_args = [
            ActionArgument(
                name="target",
                arg_type=ArgumentType.STRING,
                requirement=ArgumentRequirement.REQUIRED,
                description="Target path",
            ),
            ActionArgument(
                name="verbose",
                arg_type=ArgumentType.BOOLEAN,
                requirement=ArgumentRequirement.OPTIONAL,
                description="Verbose output",
                default_value=True,
            ),
            ActionArgument(
                name="threshold",
                arg_type=ArgumentType.FLOAT,
                requirement=ArgumentRequirement.OPTIONAL,
                description="Quality threshold",
                default_value=0.8,
            ),
        ]
        svc.create_action(
            name="local/int-multi-args",
            description="Action with multiple arguments",
            definition_of_done="Tests pass",
            strategy_actor="openai/gpt-4",
            execution_actor="openai/gpt-4",
            arguments=multi_args,
        )
        plan3 = svc.use_action(
            action_name="local/int-multi-args",
            arguments={"target": "src/"},
        )
        assert plan3.phase.value == "strategize", (
            f"FAIL: expected strategize, got {plan3.phase.value}"
        )
        print("PASS: plan use with multiple arguments succeeds")

        # --- Test 4: plan use twice on same reusable action succeeds ---
        single_arg = [
            ActionArgument(
                name="my_arg",
                arg_type=ArgumentType.STRING,
                requirement=ArgumentRequirement.OPTIONAL,
                description="A test argument",
                default_value="world",
            ),
        ]
        svc.create_action(
            name="local/int-reuse-test",
            description="Reusable action",
            definition_of_done="Tests pass",
            strategy_actor="openai/gpt-4",
            execution_actor="openai/gpt-4",
            arguments=single_arg,
            reusable=True,
        )
        plan4a = svc.use_action(action_name="local/int-reuse-test")
        plan4b = svc.use_action(action_name="local/int-reuse-test")
        assert plan4a.phase.value == "strategize", "FAIL: first plan wrong phase"
        assert plan4b.phase.value == "strategize", "FAIL: second plan wrong phase"
        assert plan4a.identity.plan_id != plan4b.identity.plan_id, (
            "FAIL: two plans have the same ID"
        )
        print("PASS: plan use twice on same action succeeds")

        # --- Test 5: plan use archives non-reusable action with invariants ---
        non_reusable_args = [
            ActionArgument(
                name="payload",
                arg_type=ArgumentType.STRING,
                requirement=ArgumentRequirement.REQUIRED,
                description="Payload to process",
            ),
            ActionArgument(
                name="retries",
                arg_type=ArgumentType.INTEGER,
                requirement=ArgumentRequirement.OPTIONAL,
                description="Retry attempts",
                default_value=3,
            ),
        ]
        invariants = ["Plans must log archival outcomes."]
        svc.create_action(
            name="local/int-non-reuse",
            description="Non-reusable action",
            definition_of_done="Tests pass",
            strategy_actor="openai/gpt-4",
            execution_actor="openai/gpt-4",
            arguments=non_reusable_args,
            invariants=invariants,
            reusable=False,
        )
        plan5 = svc.use_action(
            action_name="local/int-non-reuse",
            arguments={"payload": "data"},
        )
        assert plan5.phase.value == "strategize", (
            f"FAIL: expected strategize, got {plan5.phase.value}"
        )

        normalised_non_reuse = str(NamespacedName.parse("local/int-non-reuse"))
        # Clear service cache to force a fresh read of persisted rows.
        svc._actions.pop(normalised_non_reuse, None)

        with sf() as session:
            arg_count = int(
                session.scalar(
                    select(func.count())
                    .select_from(ActionArgumentModel)
                    .where(ActionArgumentModel.action_name == normalised_non_reuse)
                )
                or 0
            )
            inv_count = int(
                session.scalar(
                    select(func.count())
                    .select_from(ActionInvariantModel)
                    .where(ActionInvariantModel.action_name == normalised_non_reuse)
                )
                or 0
            )

        assert arg_count == 2, f"FAIL: expected 2 persisted arguments, got {arg_count}"
        assert inv_count == 1, f"FAIL: expected 1 persisted invariant, got {inv_count}"
        action_non_reuse = svc.get_action("local/int-non-reuse")
        assert len(action_non_reuse.arguments) == 2, (
            f"FAIL: expected 2 arguments, got {len(action_non_reuse.arguments)}"
        )
        assert len(action_non_reuse.invariants) == 1, (
            f"FAIL: expected 1 invariant, got {len(action_non_reuse.invariants)}"
        )
        print("PASS: plan use archives non-reusable action with invariants")

        # --- Test 6: action update with existing arguments succeeds ---
        svc.create_action(
            name="local/int-update-test",
            description="Action to update",
            definition_of_done="Tests pass",
            strategy_actor="openai/gpt-4",
            execution_actor="openai/gpt-4",
            arguments=single_arg,
            invariants=["Persisted actions must remain valid."],
        )

        action = svc.get_action("local/int-update-test")
        action.description = "Updated description"
        with uow.transaction() as ctx:
            ctx.actions.update(action)

        normalised = str(NamespacedName.parse("local/int-update-test"))
        svc._actions.pop(normalised, None)

        with sf() as session:
            db_count = int(
                session.scalar(
                    select(func.count())
                    .select_from(ActionArgumentModel)
                    .where(
                        ActionArgumentModel.action_name == normalised,
                    )
                )
                or 0
            )
            inv_count = int(
                session.scalar(
                    select(func.count())
                    .select_from(ActionInvariantModel)
                    .where(
                        ActionInvariantModel.action_name == normalised,
                    )
                )
                or 0
            )

        updated = svc.get_action("local/int-update-test")
        assert updated.description == "Updated description", (
            "FAIL: description not updated"
        )
        assert db_count == 1, f"FAIL: expected 1 persisted argument, got {db_count}"
        assert len(updated.arguments) == 1, (
            f"FAIL: expected 1 argument, got {len(updated.arguments)}"
        )
        assert inv_count == 1, f"FAIL: expected 1 persisted invariant, got {inv_count}"
        assert len(updated.invariants) == 1, (
            f"FAIL: expected 1 invariant, got {len(updated.invariants)}"
        )
        print("PASS: action update with existing arguments and invariants succeeds")

        print("PASS: plan_use_action_args_integrity smoke test")
    finally:
        engine.dispose()


if __name__ == "__main__":
    main()
