from __future__ import annotations

import json
import os
import tempfile
from typing import Any

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

from cleveragents.domain.models.core.plan import ExecutionEnvPriority
from cleveragents.domain.models.core.project import ContextConfig

# ---------------------------------------------------------------------------
# Domain model steps
# ---------------------------------------------------------------------------


@given('I create a ContextConfig with execution_env_priority "{priority}"')
def step_create_cc_with_priority(context: Context, priority: str) -> None:
    context.cc = ContextConfig(execution_env_priority=priority)


@given("I create a default ContextConfig")
def step_create_default_cc(context: Context) -> None:
    context.cc = ContextConfig()


@then('the context config execution_env_priority should be "{expected}"')
def step_check_cc_priority(context: Context, expected: str) -> None:
    assert context.cc.execution_env_priority == expected


@then("the context config execution_env_priority should be None")
def step_check_cc_priority_none(context: Context) -> None:
    assert context.cc.execution_env_priority is None


# ---------------------------------------------------------------------------
# Enum steps
# ---------------------------------------------------------------------------


@then('the ExecutionEnvPriority enum should have "{value}"')
def step_check_eep_enum(context: Context, value: str) -> None:
    assert value in [e.value for e in ExecutionEnvPriority]


# ---------------------------------------------------------------------------
# CLI simulation helpers
# ---------------------------------------------------------------------------


def _setup_test_db(context: Context, project_name: str) -> Any:
    """Create a temporary SQLite DB with the ns_projects table."""
    from sqlalchemy import Column, String, Text, create_engine
    from sqlalchemy.orm import Session, declarative_base, sessionmaker

    fd, db_path = tempfile.mkstemp(suffix=".db")
    os.close(fd)
    context._eep_db_path = db_path

    engine = create_engine(f"sqlite:///{db_path}")
    Base = declarative_base()

    class NsProject(Base):  # type: ignore[misc]
        __tablename__ = "ns_projects"
        namespaced_name = Column(String(255), primary_key=True)
        context_policy_json = Column(Text, nullable=True)

    Base.metadata.create_all(engine)
    factory = sessionmaker(bind=engine, expire_on_commit=False)

    session: Session = factory()
    session.execute(
        NsProject.__table__.insert().values(
            namespaced_name=project_name,
            context_policy_json=None,
        )
    )
    session.commit()
    session.close()

    context._eep_session_factory = factory
    return factory


@given('I have a test project "{name}"')
def step_setup_test_project(context: Context, name: str) -> None:
    _setup_test_db(context, name)
    context._eep_project_name = name


def _load_blob(context: Context) -> dict[str, Any]:
    """Load the raw policy JSON from the test DB."""
    from sqlalchemy import text

    session = context._eep_session_factory()
    try:
        row = session.execute(
            text(
                "SELECT context_policy_json FROM ns_projects "
                "WHERE namespaced_name = :ns"
            ),
            {"ns": context._eep_project_name},
        ).fetchone()
        if row is None or row[0] is None:
            return {}
        return json.loads(row[0])
    finally:
        session.close()


def _save_blob(context: Context, blob: dict[str, Any]) -> None:
    """Save raw policy JSON to the test DB."""
    from sqlalchemy import text

    session = context._eep_session_factory()
    try:
        session.execute(
            text(
                "UPDATE ns_projects SET context_policy_json = :blob "
                "WHERE namespaced_name = :ns"
            ),
            {"blob": json.dumps(blob), "ns": context._eep_project_name},
        )
        session.commit()
    finally:
        session.close()


# ---------------------------------------------------------------------------
# CLI validation steps
# ---------------------------------------------------------------------------


@when('I call project context set with execution-env-priority "{priority}"')
def step_call_set_with_priority(context: Context, priority: str) -> None:
    context._eep_error = None
    try:
        ExecutionEnvPriority(priority.lower())
        # Valid -- persist it
        blob = _load_blob(context)
        blob["execution_env_priority"] = priority.lower()
        _save_blob(context, blob)
    except ValueError as exc:
        context._eep_error = exc


@then("a CLI error should be raised for invalid priority")
def step_cli_error_raised(context: Context) -> None:
    assert context._eep_error is not None


@then('the project priority should be persisted as "{expected}"')
def step_check_persisted_priority(context: Context, expected: str) -> None:
    blob = _load_blob(context)
    assert blob.get("execution_env_priority") == expected


# ---------------------------------------------------------------------------
# Persistence round-trip steps
# ---------------------------------------------------------------------------


@when('I set execution-environment "{env}" and priority "{priority}"')
def step_set_both(context: Context, env: str, priority: str) -> None:
    blob = _load_blob(context)
    blob["execution_environment"] = env
    blob["execution_env_priority"] = priority
    _save_blob(context, blob)


@then('the persisted blob should have execution_environment "{expected}"')
def step_blob_has_ee(context: Context, expected: str) -> None:
    blob = _load_blob(context)
    assert blob.get("execution_environment") == expected


@then('the persisted blob should have execution_env_priority "{expected}"')
def step_blob_has_eep(context: Context, expected: str) -> None:
    blob = _load_blob(context)
    assert blob.get("execution_env_priority") == expected


@when('I set execution-environment "{env}" first')
def step_set_ee_first(context: Context, env: str) -> None:
    blob = _load_blob(context)
    blob["execution_environment"] = env
    _save_blob(context, blob)


@when('I set execution-env-priority "{priority}" second')
def step_set_eep_second(context: Context, priority: str) -> None:
    blob = _load_blob(context)
    blob["execution_env_priority"] = priority
    _save_blob(context, blob)


@then('the persisted blob should still have execution_environment "{expected}"')
def step_blob_still_has_ee(context: Context, expected: str) -> None:
    blob = _load_blob(context)
    assert blob.get("execution_environment") == expected, (
        f"Expected '{expected}', got '{blob.get('execution_environment')}'"
    )
