"""Step definitions for AutomationProfileService CRUD coverage tests."""

from __future__ import annotations

from behave import given, then, when
from behave.runner import Context

from cleveragents.application.services.automation_profile_service import (
    AutomationProfileService,
)
from cleveragents.core.exceptions import (
    NotFoundError,
    ValidationError,
)
from cleveragents.domain.models.core.automation_profile import AutomationProfile

# -------------------------------------------------------------------
# Mock repository
# -------------------------------------------------------------------


class MockAutomationProfileRepository:
    """In-memory mock that implements the repository interface."""

    def __init__(self) -> None:
        self._store: dict[str, AutomationProfile] = {}

    def get_by_name(self, name: str) -> AutomationProfile | None:
        return self._store.get(name)

    def list_all(self) -> list[AutomationProfile]:
        return list(self._store.values())

    def upsert(self, profile: AutomationProfile) -> None:
        self._store[profile.name] = profile

    def delete(self, name: str) -> None:
        self._store.pop(name, None)


# -------------------------------------------------------------------
# Given steps
# -------------------------------------------------------------------


@given("a profile service with a mock repository")
def step_given_service_with_mock_repo(context: Context) -> None:
    """Create a service backed by an in-memory mock repository."""
    context.mock_repo = MockAutomationProfileRepository()
    context.profile_service = AutomationProfileService(
        repo=context.mock_repo,
        global_default="manual",
    )


@given('the mock repository contains a profile named "{name}"')
def step_given_repo_contains_profile(context: Context, name: str) -> None:
    """Seed the mock repository with a custom profile."""
    profile = AutomationProfile(
        name=name,
        description=f"Custom profile {name}",
    )
    context.mock_repo.upsert(profile)


@given("a profile service without a repository")
def step_given_service_without_repo(context: Context) -> None:
    """Create a service with no repository (repo=None)."""
    context.profile_service = AutomationProfileService(
        repo=None,
        global_default="manual",
    )


# -------------------------------------------------------------------
# When steps: get_profile from repo
# -------------------------------------------------------------------


@when('I fetch profile "{name}"')
def step_when_fetch_profile(context: Context, name: str) -> None:
    """Fetch a profile by name (may come from repo)."""
    context.fetched_profile = context.profile_service.get_profile(name)


# -------------------------------------------------------------------
# When steps: list_profiles with repo
# -------------------------------------------------------------------


@when("I list all available profiles")
def step_when_list_available_profiles(context: Context) -> None:
    """List all profiles including custom ones from the repo."""
    context.available_profiles = context.profile_service.list_profiles()


# -------------------------------------------------------------------
# When steps: create_profile
# -------------------------------------------------------------------


@when('I create a profile with name "{name}" and description "{description}"')
def step_when_create_profile(context: Context, name: str, description: str) -> None:
    """Create a custom profile with the given config."""
    config = {"name": name, "description": description}
    context.created_profile = context.profile_service.create_profile(config)


@when('I try to create a profile with builtin name "{name}"')
def step_when_try_create_builtin(context: Context, name: str) -> None:
    """Attempt to create a profile with a built-in name."""
    context.crud_validation_error = None
    try:
        context.profile_service.create_profile({"name": name})
    except ValidationError as exc:
        context.crud_validation_error = exc


# -------------------------------------------------------------------
# When steps: update_profile
# -------------------------------------------------------------------


@when('I update profile "{name}" with description "{description}"')
def step_when_update_profile(context: Context, name: str, description: str) -> None:
    """Update an existing custom profile."""
    config = {"description": description}
    context.updated_profile = context.profile_service.update_profile(name, config)


@when('I try to update builtin profile "{name}"')
def step_when_try_update_builtin(context: Context, name: str) -> None:
    """Attempt to update a built-in profile."""
    context.crud_validation_error = None
    try:
        context.profile_service.update_profile(name, {"description": "changed"})
    except ValidationError as exc:
        context.crud_validation_error = exc


@when('I try to update profile "{name}" without repo')
def step_when_try_update_no_repo(context: Context, name: str) -> None:
    """Attempt to update a profile when no repository is configured."""
    context.crud_not_found_error = None
    try:
        context.profile_service.update_profile(name, {"description": "changed"})
    except NotFoundError as exc:
        context.crud_not_found_error = exc


# -------------------------------------------------------------------
# When steps: delete_profile
# -------------------------------------------------------------------


@when('I delete profile "{name}"')
def step_when_delete_profile(context: Context, name: str) -> None:
    """Delete a custom profile."""
    context.profile_service.delete_profile(name)


@when('I try to delete builtin profile "{name}"')
def step_when_try_delete_builtin(context: Context, name: str) -> None:
    """Attempt to delete a built-in profile."""
    context.crud_validation_error = None
    try:
        context.profile_service.delete_profile(name)
    except ValidationError as exc:
        context.crud_validation_error = exc


@when('I try to delete profile "{name}" without repo')
def step_when_try_delete_no_repo(context: Context, name: str) -> None:
    """Attempt to delete a profile when no repository is configured."""
    context.crud_not_found_error = None
    try:
        context.profile_service.delete_profile(name)
    except NotFoundError as exc:
        context.crud_not_found_error = exc


# -------------------------------------------------------------------
# Then steps
# -------------------------------------------------------------------


@then('the fetched profile name should be "{expected}"')
def step_then_fetched_name(context: Context, expected: str) -> None:
    """Verify the fetched profile has the expected name."""
    actual = context.fetched_profile.name
    assert actual == expected, f"Expected '{expected}', got '{actual}'"


@then('the available profiles should include "{name}"')
def step_then_available_includes(context: Context, name: str) -> None:
    """Verify the available profiles list contains a given name."""
    names = [p.name for p in context.available_profiles]
    assert name in names, f"Expected '{name}' in list, got: {names}"


@then('the created profile name should be "{expected}"')
def step_then_created_name(context: Context, expected: str) -> None:
    """Verify the created profile has the expected name."""
    actual = context.created_profile.name
    assert actual == expected, f"Expected '{expected}', got '{actual}'"


@then('the created profile description should be "{expected}"')
def step_then_created_description(context: Context, expected: str) -> None:
    """Verify the created profile has the expected description."""
    actual = context.created_profile.description
    assert actual == expected, f"Expected '{expected}', got '{actual}'"


@then('the mock repository should contain "{name}"')
def step_then_repo_contains(context: Context, name: str) -> None:
    """Verify the mock repository has the profile stored."""
    profile = context.mock_repo.get_by_name(name)
    assert profile is not None, f"Expected '{name}' in repo, but not found"


@then('the mock repository should not contain "{name}"')
def step_then_repo_not_contains(context: Context, name: str) -> None:
    """Verify the mock repository does not have the profile."""
    profile = context.mock_repo.get_by_name(name)
    assert profile is None, f"Expected '{name}' not in repo, but found it"


@then('the updated profile name should be "{expected}"')
def step_then_updated_name(context: Context, expected: str) -> None:
    """Verify the updated profile has the expected name."""
    actual = context.updated_profile.name
    assert actual == expected, f"Expected '{expected}', got '{actual}'"


@then('the updated profile description should be "{expected}"')
def step_then_updated_description(context: Context, expected: str) -> None:
    """Verify the updated profile has the expected description."""
    actual = context.updated_profile.description
    assert actual == expected, f"Expected '{expected}', got '{actual}'"


@then("a crud validation error should be raised")
def step_then_crud_validation_error(context: Context) -> None:
    """Verify a ValidationError was raised."""
    assert context.crud_validation_error is not None, (
        "Expected ValidationError but none was raised"
    )


@then('the crud validation error message should mention "{text}"')
def step_then_crud_validation_error_mentions(context: Context, text: str) -> None:
    """Check the ValidationError message contains the expected text."""
    err = str(context.crud_validation_error)
    assert text in err, f"Expected '{text}' in error, got: {err}"


@then("a crud not found error should be raised")
def step_then_crud_not_found_error(context: Context) -> None:
    """Verify a NotFoundError was raised."""
    assert context.crud_not_found_error is not None, (
        "Expected NotFoundError but none was raised"
    )


@then('the crud not found error message should mention "{text}"')
def step_then_crud_not_found_error_mentions(context: Context, text: str) -> None:
    """Check the NotFoundError message contains the expected text."""
    err = str(context.crud_not_found_error)
    assert text in err, f"Expected '{text}' in error, got: {err}"
