"""Step definitions for automation profile precedence chain tests.

Tests the four-level precedence chain: plan > action > project > global.
Covers all 16 combinations of (plan set/unset) x (action set/unset) x
(project set/unset) x (global set/default).
"""

from __future__ import annotations

import logging
import os

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

from cleveragents.application.services.automation_profile_precedence import (
    PrecedenceResolution,
    PrecedenceSource,
    resolve_precedence_chain,
)
from cleveragents.domain.models.core.automation_profile import AutomationProfile

# ---------------------------------------------------------------------------
# Constants
# ---------------------------------------------------------------------------

_ENV_VAR = "CLEVERAGENTS_AUTOMATION_PROFILE"


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


def _cleanup_env_var(context: Context) -> None:
    """Remove the env var if it was set during a scenario."""
    os.environ.pop(_ENV_VAR, None)


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


@given('the global profile is configured as "{global_name}"')
def step_given_global_profile_configured(context: Context, global_name: str) -> None:
    """Set an explicit global profile override."""
    context.global_override = global_name
    # Ensure env var does not interfere
    os.environ.pop(_ENV_VAR, None)
    if not hasattr(context, "env_vars_to_clean"):
        context.env_vars_to_clean = []


@given("the global profile is the default")
def step_given_global_profile_default(context: Context) -> None:
    """Use the default global profile (no explicit override, no env var)."""
    context.global_override = None
    os.environ.pop(_ENV_VAR, None)
    if not hasattr(context, "env_vars_to_clean"):
        context.env_vars_to_clean = []


@given("debug logging is captured for the precedence module")
def step_given_debug_logging_captured(context: Context) -> None:
    """Install a log handler to capture debug messages from the precedence module."""
    log_records: list[logging.LogRecord] = []
    context.log_records = log_records

    class _CapturingHandler(logging.Handler):
        def emit(self, record: logging.LogRecord) -> None:
            log_records.append(record)

    handler = _CapturingHandler()
    handler.setLevel(logging.DEBUG)
    prec_logger = logging.getLogger(
        "cleveragents.application.services.automation_profile_precedence"
    )
    prec_logger.setLevel(logging.DEBUG)
    prec_logger.addHandler(handler)
    context.log_handler = handler
    context.prec_logger = prec_logger

    # Register cleanup to remove the handler after the scenario
    if not hasattr(context, "_cleanup_handlers"):
        context._cleanup_handlers = []
    context._cleanup_handlers.append(lambda: prec_logger.removeHandler(handler))


@given('the env var CLEVERAGENTS_AUTOMATION_PROFILE is "{value}"')
def step_given_env_var_set(context: Context, value: str) -> None:
    """Set the CLEVERAGENTS_AUTOMATION_PROFILE env var."""
    os.environ[_ENV_VAR] = value
    if not hasattr(context, "env_vars_to_clean"):
        context.env_vars_to_clean = []
    context.env_vars_to_clean.append(_ENV_VAR)


@given('a custom profile registry contains "{name}" with select_tool {threshold:g}')
def step_given_custom_registry(context: Context, name: str, threshold: float) -> None:
    """Create a custom profile registry with a named profile."""
    custom_profile = AutomationProfile(name=name, select_tool=threshold)
    context.custom_registry: dict[str, AutomationProfile] = {name: custom_profile}


# ---------------------------------------------------------------------------
# When steps
# ---------------------------------------------------------------------------


@when(
    'I resolve the precedence chain with plan "{plan_name}" action "{action_name}" project "{project_name}"'
)
def step_when_resolve_plan_action_project(
    context: Context, plan_name: str, action_name: str, project_name: str
) -> None:
    """Resolve with plan, action, and project all set."""
    global_override: str | None = getattr(context, "global_override", None)
    registry: dict[str, AutomationProfile] | None = getattr(
        context, "custom_registry", None
    )
    context.resolution: PrecedenceResolution = resolve_precedence_chain(
        plan_profile=plan_name,
        action_profile=action_name,
        project_profile=project_name,
        global_override=global_override,
        profile_registry=registry,
    )


@when(
    'I resolve the precedence chain with plan "{plan_name}" action "{action_name}" and no project'
)
def step_when_resolve_plan_and_action(
    context: Context, plan_name: str, action_name: str
) -> None:
    """Resolve with plan and action set, project absent."""
    global_override: str | None = getattr(context, "global_override", None)
    registry: dict[str, AutomationProfile] | None = getattr(
        context, "custom_registry", None
    )
    context.resolution: PrecedenceResolution = resolve_precedence_chain(
        plan_profile=plan_name,
        action_profile=action_name,
        project_profile=None,
        global_override=global_override,
        profile_registry=registry,
    )


@when(
    'I resolve the precedence chain with plan "{plan_name}" and no action project "{project_name}"'
)
def step_when_resolve_plan_and_project(
    context: Context, plan_name: str, project_name: str
) -> None:
    """Resolve with plan and project set, action absent."""
    global_override: str | None = getattr(context, "global_override", None)
    registry: dict[str, AutomationProfile] | None = getattr(
        context, "custom_registry", None
    )
    context.resolution: PrecedenceResolution = resolve_precedence_chain(
        plan_profile=plan_name,
        action_profile=None,
        project_profile=project_name,
        global_override=global_override,
        profile_registry=registry,
    )


@when(
    'I resolve the precedence chain with plan "{plan_name}" and no action and no project'
)
def step_when_resolve_plan_only(context: Context, plan_name: str) -> None:
    """Resolve with plan set, action and project absent."""
    global_override: str | None = getattr(context, "global_override", None)
    registry: dict[str, AutomationProfile] | None = getattr(
        context, "custom_registry", None
    )
    context.resolution = resolve_precedence_chain(
        plan_profile=plan_name,
        action_profile=None,
        project_profile=None,
        global_override=global_override,
        profile_registry=registry,
    )


@when(
    'I resolve the precedence chain with no plan and action "{action_name}" project "{project_name}"'
)
def step_when_resolve_action_and_project(
    context: Context, action_name: str, project_name: str
) -> None:
    """Resolve with action and project set, plan absent."""
    global_override: str | None = getattr(context, "global_override", None)
    registry: dict[str, AutomationProfile] | None = getattr(
        context, "custom_registry", None
    )
    context.resolution: PrecedenceResolution = resolve_precedence_chain(
        plan_profile=None,
        action_profile=action_name,
        project_profile=project_name,
        global_override=global_override,
        profile_registry=registry,
    )


@when(
    'I resolve the precedence chain with no plan and action "{action_name}" and no project'
)
def step_when_resolve_action_only(context: Context, action_name: str) -> None:
    """Resolve with action set, plan and project absent."""
    global_override: str | None = getattr(context, "global_override", None)
    registry: dict[str, AutomationProfile] | None = getattr(
        context, "custom_registry", None
    )
    context.resolution = resolve_precedence_chain(
        plan_profile=None,
        action_profile=action_name,
        project_profile=None,
        global_override=global_override,
        profile_registry=registry,
    )


@when(
    'I resolve the precedence chain with no plan and no action project "{project_name}"'
)
def step_when_resolve_project_only(context: Context, project_name: str) -> None:
    """Resolve with project set, plan and action absent."""
    global_override: str | None = getattr(context, "global_override", None)
    registry: dict[str, AutomationProfile] | None = getattr(
        context, "custom_registry", None
    )
    context.resolution = resolve_precedence_chain(
        plan_profile=None,
        action_profile=None,
        project_profile=project_name,
        global_override=global_override,
        profile_registry=registry,
    )


@when("I resolve the precedence chain with no plan and no action and no project")
def step_when_resolve_global_only(context: Context) -> None:
    """Resolve with plan, action, and project all absent."""
    global_override: str | None = getattr(context, "global_override", None)
    registry: dict[str, AutomationProfile] | None = getattr(
        context, "custom_registry", None
    )
    context.resolution = resolve_precedence_chain(
        plan_profile=None,
        action_profile=None,
        project_profile=None,
        global_override=global_override,
        profile_registry=registry,
    )


@when(
    'I resolve the precedence chain with empty plan and action "{action_name}" and no project'
)
def step_when_resolve_empty_plan_with_action(
    context: Context, action_name: str
) -> None:
    """Resolve with empty string plan (treated as absent) and action set."""
    global_override: str | None = getattr(context, "global_override", None)
    context.resolution = resolve_precedence_chain(
        plan_profile="",
        action_profile=action_name,
        project_profile=None,
        global_override=global_override,
    )


@when("I resolve the precedence chain with plan absent and empty action and no project")
def step_when_resolve_absent_plan_empty_action(context: Context) -> None:
    """Resolve with plan absent and empty string action (treated as absent)."""
    global_override: str | None = getattr(context, "global_override", None)
    context.resolution = resolve_precedence_chain(
        plan_profile=None,
        action_profile="",
        project_profile=None,
        global_override=global_override,
    )


@when(
    "I resolve the precedence chain with plan absent and action absent and empty project"
)
def step_when_resolve_absent_plan_action_empty_project(context: Context) -> None:
    """Resolve with plan and action absent and empty string project (treated as absent)."""
    global_override: str | None = getattr(context, "global_override", None)
    context.resolution = resolve_precedence_chain(
        plan_profile=None,
        action_profile=None,
        project_profile="",
        global_override=global_override,
    )


# ---------------------------------------------------------------------------
# Then steps: resolution result
# ---------------------------------------------------------------------------


@then('the resolved precedence profile name should be "{expected}"')
def step_then_resolved_name(context: Context, expected: str) -> None:
    """Assert the resolved profile name."""
    actual = context.resolution.profile_name
    assert actual == expected, (
        f"Expected resolved profile name '{expected}', got '{actual}'"
    )


@then('the precedence source should be "{expected_source}"')
def step_then_precedence_source(context: Context, expected_source: str) -> None:
    """Assert the precedence source (plan, action, project, or global)."""
    actual = context.resolution.source.value
    assert actual == expected_source, (
        f"Expected precedence source '{expected_source}', got '{actual}'"
    )


@then('the resolution plan_profile field should be "{expected}"')
def step_then_resolution_plan_field(context: Context, expected: str) -> None:
    """Assert the plan_profile field on the resolution result."""
    actual = context.resolution.plan_profile
    assert actual == expected, (
        f"Expected resolution.plan_profile '{expected}', got '{actual}'"
    )


@then('the resolution action_profile field should be "{expected}"')
def step_then_resolution_action_field(context: Context, expected: str) -> None:
    """Assert the action_profile field on the resolution result."""
    actual = context.resolution.action_profile
    assert actual == expected, (
        f"Expected resolution.action_profile '{expected}', got '{actual}'"
    )


@then('the resolution project_profile field should be "{expected}"')
def step_then_resolution_project_field(context: Context, expected: str) -> None:
    """Assert the project_profile field on the resolution result."""
    actual = context.resolution.project_profile
    assert actual == expected, (
        f"Expected resolution.project_profile '{expected}', got '{actual}'"
    )


@then('the resolution global_profile field should be "{expected}"')
def step_then_resolution_global_field(context: Context, expected: str) -> None:
    """Assert the global_profile field on the resolution result."""
    actual = context.resolution.global_profile
    assert actual == expected, (
        f"Expected resolution.global_profile '{expected}', got '{actual}'"
    )


@then('the resolved precedence profile object should have name "{expected}"')
def step_then_resolved_profile_object_name(context: Context, expected: str) -> None:
    """Assert the resolved AutomationProfile object's name."""
    actual = context.resolution.profile.name
    assert actual == expected, f"Expected profile.name '{expected}', got '{actual}'"


@then("the resolved precedence profile decompose_task should be {expected:g}")
def step_then_resolved_profile_decompose_task(
    context: Context, expected: float
) -> None:
    """Assert the resolved profile's decompose_task threshold."""
    actual = context.resolution.profile.decompose_task
    assert actual == expected, (
        f"Expected profile.decompose_task {expected}, got {actual}"
    )


# ---------------------------------------------------------------------------
# Then steps: debug logging
# ---------------------------------------------------------------------------


@then('a debug log entry should mention the resolved profile name "{name}"')
def step_then_debug_log_mentions_name(context: Context, name: str) -> None:
    """Assert that a debug log entry contains the resolved profile name."""
    records: list[logging.LogRecord] = getattr(context, "log_records", [])
    messages = [r.getMessage() for r in records if r.levelno == logging.DEBUG]
    found = any(name in msg for msg in messages)
    assert found, f"Expected debug log to mention '{name}'. Debug messages: {messages}"


@then('the debug log entry should mention the source "{source}"')
def step_then_debug_log_mentions_source(context: Context, source: str) -> None:
    """Assert that a debug log entry contains the source level."""
    records: list[logging.LogRecord] = getattr(context, "log_records", [])
    messages = [r.getMessage() for r in records if r.levelno == logging.DEBUG]
    found = any(source in msg for msg in messages)
    assert found, (
        f"Expected debug log to mention source '{source}'. Debug messages: {messages}"
    )


# ---------------------------------------------------------------------------
# Then steps: PrecedenceSource enum
# ---------------------------------------------------------------------------


@then('the PrecedenceSource PLAN value should be "{expected}"')
def step_then_precedence_source_plan_value(context: Context, expected: str) -> None:
    """Assert PrecedenceSource.PLAN has the expected string value."""
    assert PrecedenceSource.PLAN.value == expected, (
        f"Expected PrecedenceSource.PLAN.value '{expected}', "
        f"got '{PrecedenceSource.PLAN.value}'"
    )


@then('the PrecedenceSource ACTION value should be "{expected}"')
def step_then_precedence_source_action_value(context: Context, expected: str) -> None:
    """Assert PrecedenceSource.ACTION has the expected string value."""
    assert PrecedenceSource.ACTION.value == expected, (
        f"Expected PrecedenceSource.ACTION.value '{expected}', "
        f"got '{PrecedenceSource.ACTION.value}'"
    )


@then('the PrecedenceSource PROJECT value should be "{expected}"')
def step_then_precedence_source_project_value(context: Context, expected: str) -> None:
    """Assert PrecedenceSource.PROJECT has the expected string value."""
    assert PrecedenceSource.PROJECT.value == expected, (
        f"Expected PrecedenceSource.PROJECT.value '{expected}', "
        f"got '{PrecedenceSource.PROJECT.value}'"
    )


@then('the PrecedenceSource GLOBAL value should be "{expected}"')
def step_then_precedence_source_global_value(context: Context, expected: str) -> None:
    """Assert PrecedenceSource.GLOBAL has the expected string value."""
    assert PrecedenceSource.GLOBAL.value == expected, (
        f"Expected PrecedenceSource.GLOBAL.value '{expected}', "
        f"got '{PrecedenceSource.GLOBAL.value}'"
    )
