"""Regression coverage for #2965 streaming SessionDB profile isolation."""

from __future__ import annotations

import os
import queue
import sys
import types
from pathlib import Path


REPO = Path(__file__).resolve().parent.parent
if str(REPO) not in sys.path:
    sys.path.insert(0, str(REPO))


def test_streaming_sessiondb_uses_session_profile_state_db(tmp_path, monkeypatch):
    """WebUI streaming must not rely on hermes_state.DEFAULT_DB_PATH.

    ``hermes_state.DEFAULT_DB_PATH`` is frozen at import time. A non-default
    WebUI profile therefore needs the streaming attach path to pass the already
    resolved profile home to SessionDB explicitly.
    """
    from api import config as cfg
    from api import oauth
    from api import profiles
    from api import streaming

    profile_home = tmp_path / "hermes-home" / "profiles" / "zhangtingban"
    profile_home.mkdir(parents=True)

    class FakeSession:
        session_id = "issue2965-session"
        title = "Issue 2965"
        workspace = str(tmp_path)
        model = "test-model"
        model_provider = None
        profile = "zhangtingban"
        personality = None
        messages = []
        context_messages = []
        tool_calls = []
        input_tokens = 0
        output_tokens = 0
        estimated_cost = None
        context_length = 0
        threshold_tokens = 0
        last_prompt_tokens = 0
        active_stream_id = "issue2965-stream"
        pending_user_message = None
        pending_attachments = []
        pending_started_at = None
        llm_title_generated = True

        def save(self, *args, **kwargs):
            return None

        def compact(self):
            return {
                "session_id": self.session_id,
                "title": self.title,
                "workspace": self.workspace,
                "model": self.model,
                "created_at": 0,
                "updated_at": 0,
                "pinned": False,
                "archived": False,
                "project_id": None,
                "profile": self.profile,
                "input_tokens": self.input_tokens,
                "output_tokens": self.output_tokens,
                "estimated_cost": self.estimated_cost,
                "personality": self.personality,
            }

    class FakeSessionDB:
        def __init__(self, db_path=None):
            self.db_path = db_path

        def close(self):
            return None

    class CapturingAgent:
        def __init__(self, **kwargs):
            self.session_db = kwargs.get("session_db")
            self._session_db = self.session_db
            self.session_prompt_tokens = 0
            self.session_completion_tokens = 0
            self.session_estimated_cost_usd = None
            self.context_compressor = None
            self._last_error = None
            self.ephemeral_system_prompt = None

        def run_conversation(self, **kwargs):
            history = list(kwargs.get("conversation_history") or [])
            return {
                "messages": history
                + [
                    {"role": "user", "content": kwargs.get("persist_user_message", "")},
                    {"role": "assistant", "content": "ok"},
                ]
            }

        def interrupt(self, _message):
            return None

    session_db_instances = []

    def fake_session_db(db_path=None):
        db = FakeSessionDB(db_path=db_path)
        session_db_instances.append(db)
        return db

    fake_runtime_module = types.ModuleType("hermes_cli.runtime_provider")
    fake_runtime_module.resolve_runtime_provider = lambda requested=None: {
        "provider": requested or "test-provider",
        "api_key": "synthetic-key",
        "base_url": None,
    }
    fake_hermes_cli = types.ModuleType("hermes_cli")
    fake_hermes_cli.runtime_provider = fake_runtime_module
    fake_hermes_state = types.ModuleType("hermes_state")
    fake_hermes_state.SessionDB = fake_session_db

    fake_session = FakeSession()

    monkeypatch.setattr(streaming, "get_session", lambda _sid: fake_session)
    monkeypatch.setattr(streaming, "_get_ai_agent", lambda: CapturingAgent)
    monkeypatch.setattr(
        streaming,
        "resolve_model_provider",
        lambda _model: ("test-model", "test-provider", None),
    )
    monkeypatch.setattr(streaming, "_maybe_schedule_title_refresh", lambda *args, **kwargs: None)
    monkeypatch.setattr(profiles, "get_hermes_home_for_profile", lambda _profile: profile_home)
    monkeypatch.setattr(profiles, "get_profile_runtime_env", lambda _home: {})
    monkeypatch.setattr(
        oauth,
        "resolve_runtime_provider_with_anthropic_env_lock",
        lambda _resolver, requested=None: {
            "provider": requested or "test-provider",
            "api_key": "synthetic-key",
            "base_url": None,
        },
    )
    monkeypatch.setattr("api.config.get_config", lambda: {})
    monkeypatch.setattr("api.config._resolve_cli_toolsets", lambda _cfg: [])
    monkeypatch.setattr("api.config.load_settings", lambda: {})
    monkeypatch.setitem(sys.modules, "hermes_cli", fake_hermes_cli)
    monkeypatch.setitem(sys.modules, "hermes_cli.runtime_provider", fake_runtime_module)
    monkeypatch.setitem(sys.modules, "hermes_state", fake_hermes_state)

    with cfg.SESSION_AGENT_CACHE_LOCK:
        cfg.SESSION_AGENT_CACHE.clear()
    streaming.STREAMS.clear()
    streaming.CANCEL_FLAGS.clear()
    streaming.AGENT_INSTANCES.clear()
    streaming.STREAM_PARTIAL_TEXT.clear()
    streaming.STREAM_REASONING_TEXT.clear()
    streaming.STREAM_LIVE_TOOL_CALLS.clear()

    streaming.STREAMS[fake_session.active_stream_id] = queue.Queue()
    old_home = os.environ.get("HERMES_HOME")
    try:
        streaming._run_agent_streaming(
            session_id=fake_session.session_id,
            msg_text="hello from zhangtingban",
            model="test-model",
            model_provider="test-provider",
            workspace=str(tmp_path),
            stream_id=fake_session.active_stream_id,
        )
    finally:
        if old_home is None:
            os.environ.pop("HERMES_HOME", None)
        else:
            os.environ["HERMES_HOME"] = old_home

    assert session_db_instances, "streaming should construct a SessionDB for session_search"
    assert session_db_instances[0].db_path == profile_home / "state.db"
    with cfg.SESSION_AGENT_CACHE_LOCK:
        agent = cfg.SESSION_AGENT_CACHE[fake_session.session_id][0]
    assert agent._session_db.db_path == profile_home / "state.db"
