"""Step definitions for cloud_handler_coverage.feature.

These steps target specific uncovered lines in
cleveragents/resource/handlers/cloud.py:

- Lines 249-251: extract_provider hierarchical prefix loop
- Line 253: extract_provider returns None for cloud-* generic types
- Lines 302-303: resolve_credentials explicit property priority
- Lines 331-333: validate_credentials with unknown provider
- Lines 420-423: CloudResourceHandler.resolve generic cloud type
- Lines 428-431: CloudResourceHandler.resolve unknown provider mapping
"""

import os
from unittest.mock import MagicMock, patch

from behave import given, then, when

from cleveragents.domain.models.core.resource import (
    PhysVirt,
    Resource,
    ResourceCapabilities,
    SandboxStrategy,
)
from cleveragents.resource.handlers.cloud import (
    CloudResourceHandler,
    extract_provider,
    resolve_credentials,
    validate_credentials,
)

# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------

_VALID_ULID = "01ARZ3NDEKTSV4RRFFQ69G5FAV"


def _make_resource(type_name: str, properties: dict | None = None) -> Resource:
    """Create a minimal Resource for testing."""
    return Resource(
        resource_id=_VALID_ULID,
        resource_type_name=type_name,
        classification=PhysVirt.VIRTUAL,
        properties=properties or {},
        sandbox_strategy=SandboxStrategy.NONE,
        capabilities=ResourceCapabilities(),
    )


# ---------------------------------------------------------------------------
# Background
# ---------------------------------------------------------------------------


@given("the cloud handler module is imported")
def step_cloud_module_imported(context):
    """Ensure the cloud handler module is importable."""
    assert extract_provider is not None
    assert resolve_credentials is not None
    assert validate_credentials is not None
    assert CloudResourceHandler is not None


# ---------------------------------------------------------------------------
# extract_provider: hierarchical prefix matching (lines 249-251)
# ---------------------------------------------------------------------------


@when('I extract the provider from type name "{type_name}"')
def step_extract_provider(context, type_name):
    """Call extract_provider with the given type name."""
    context.extracted_provider = extract_provider(type_name)


@then('the extracted provider should be "{expected}"')
def step_verify_extracted_provider(context, expected):
    """Verify the extracted provider matches the expected value."""
    assert context.extracted_provider == expected, (
        f"Expected provider '{expected}', got '{context.extracted_provider}'"
    )


@then("the extracted provider should be None")
def step_verify_extracted_provider_none(context):
    """Verify the extracted provider is None."""
    assert context.extracted_provider is None, (
        f"Expected None, got '{context.extracted_provider}'"
    )


# ---------------------------------------------------------------------------
# resolve_credentials: explicit properties take priority (lines 302-303)
# ---------------------------------------------------------------------------


@given('chcov the environment variable "{var_name}" is set to "{var_value}"')
def step_set_env_var(context, var_name, var_value):
    """Set an environment variable, restoring on cleanup."""
    original = os.environ.get(var_name)

    os.environ[var_name] = var_value

    def restore():
        if original is None:
            os.environ.pop(var_name, None)
        else:
            os.environ[var_name] = original

    context.add_cleanup(restore)


@when(
    'I resolve credentials for provider "{provider}" with explicit property "{prop}" set to "{value}"'
)
def step_resolve_with_explicit(context, provider, prop, value):
    """Resolve credentials with explicit properties."""
    context.resolved_creds = resolve_credentials(provider, {prop: value})


@then('the resolved credential "{field}" should be "{expected}"')
def step_verify_resolved_credential(context, field, expected):
    """Verify a specific resolved credential value."""
    actual = context.resolved_creds.get(field)
    assert actual == expected, (
        f"Expected resolved['{field}'] == '{expected}', got '{actual}'"
    )


# ---------------------------------------------------------------------------
# validate_credentials: unknown provider (lines 331-333)
# ---------------------------------------------------------------------------


@when('I validate credentials for unknown provider "{provider}"')
def step_validate_unknown_provider(context, provider):
    """Call validate_credentials with an unknown provider."""
    context.validation_errors = validate_credentials(provider, {})


@then('the validation errors should mention unknown provider "{provider}"')
def step_verify_unknown_provider_errors(context, provider):
    """Verify validation errors mention the unknown provider."""
    assert len(context.validation_errors) > 0, "Expected at least one error"
    joined = " ".join(context.validation_errors)
    assert f"Unknown cloud provider '{provider}'" in joined, (
        f"Expected mention of unknown provider '{provider}' in: {joined}"
    )


# ---------------------------------------------------------------------------
# CloudResourceHandler.resolve: generic cloud-* type (lines 420-423)
# ---------------------------------------------------------------------------


@given('I have a cloud resource with type "{type_name}"')
def step_create_cloud_resource(context, type_name):
    """Create a Resource with the given type name."""
    context.cloud_resource = _make_resource(type_name)


@when("I call resolve on the cloud resource handler")
def step_call_handler_resolve(context):
    """Call CloudResourceHandler.resolve and capture the exception."""
    handler = CloudResourceHandler()
    sandbox_mgr = MagicMock()
    try:
        handler.resolve(
            resource=context.cloud_resource,
            plan_id="plan-001",
            slot_name="test-slot",
            sandbox_manager=sandbox_mgr,
        )
        context.handler_error = None
        context.handler_error_type = None
    except NotImplementedError as exc:
        context.handler_error = exc
        context.handler_error_type = "NotImplementedError"
    except ValueError as exc:
        context.handler_error = exc
        context.handler_error_type = "ValueError"


@then('a NotImplementedError should be raised mentioning generic type "{type_name}"')
def step_verify_not_implemented_generic(context, type_name):
    """Verify a NotImplementedError was raised with the correct message."""
    assert context.handler_error_type == "NotImplementedError", (
        f"Expected NotImplementedError, got {context.handler_error_type}: {context.handler_error}"
    )
    msg = str(context.handler_error)
    assert type_name in msg, f"Expected '{type_name}' in error message: {msg}"
    assert "generic" in msg.lower(), f"Expected 'generic' in error message: {msg}"


# ---------------------------------------------------------------------------
# CloudResourceHandler.resolve: unknown provider mapping (lines 428-431)
# ---------------------------------------------------------------------------


@given('extract_provider is mocked to return "{fake_provider}"')
def step_mock_extract_provider(context, fake_provider):
    """Patch extract_provider to return a fake provider string."""
    context.fake_provider = fake_provider
    context.extract_patcher = patch(
        "cleveragents.resource.handlers.cloud.extract_provider",
        return_value=fake_provider,
    )
    context.extract_patcher.start()
    context.add_cleanup(context.extract_patcher.stop)


@when("I call resolve on the cloud resource handler expecting a ValueError")
def step_call_handler_resolve_value_error(context):
    """Call CloudResourceHandler.resolve and capture the ValueError."""
    handler = CloudResourceHandler()
    sandbox_mgr = MagicMock()
    try:
        handler.resolve(
            resource=context.cloud_resource,
            plan_id="plan-002",
            slot_name="test-slot",
            sandbox_manager=sandbox_mgr,
        )
        context.handler_error = None
        context.handler_error_type = None
    except ValueError as exc:
        context.handler_error = exc
        context.handler_error_type = "ValueError"
    except NotImplementedError as exc:
        context.handler_error = exc
        context.handler_error_type = "NotImplementedError"


@then('a ValueError should be raised mentioning unknown provider "{provider}"')
def step_verify_value_error_unknown_provider(context, provider):
    """Verify a ValueError was raised mentioning the unknown provider."""
    assert context.handler_error_type == "ValueError", (
        f"Expected ValueError, got {context.handler_error_type}: {context.handler_error}"
    )
    msg = str(context.handler_error)
    assert provider in msg, f"Expected '{provider}' in error message: {msg}"
    assert "unknown provider" in msg.lower() or "maps to unknown" in msg.lower(), (
        f"Expected 'unknown provider' in error message: {msg}"
    )
