"""Regression coverage for imported-session title generation after CLI import (#3987)."""

from __future__ import annotations

import io
import json
from collections import OrderedDict
from pathlib import Path

import api.models as models
import api.routes as routes


ROOT = Path(__file__).resolve().parents[1]
ROUTES_PY = (ROOT / "api" / "routes.py").read_text(encoding="utf-8")
SESSIONS_JS = (ROOT / "static" / "sessions.js").read_text(encoding="utf-8")


class _FakeHandler:
    def __init__(self):
        self.status = None
        self.headers = {}
        self.wfile = io.BytesIO()

    def send_response(self, status):
        self.status = status

    def send_header(self, key, value):
        self.headers[key] = value

    def end_headers(self):
        pass

    def json_body(self):
        return json.loads(self.wfile.getvalue().decode("utf-8"))


def test_import_cli_handler_queues_default_titles_after_persisting_import():
    handler_idx = ROUTES_PY.index("def _handle_session_import_cli")
    next_handler_idx = ROUTES_PY.index("def _handle_session_import(", handler_idx)
    block = ROUTES_PY[handler_idx:next_handler_idx]
    queue_idx = block.index("_queue_generated_title_for_imported_session(")
    publish_idx = block.index('publish_session_list_changed(\n        "session_import_cli",')
    response_idx = block.index("return j(", queue_idx)
    assert publish_idx < queue_idx < response_idx
    queue_window = block[queue_idx:queue_idx + 400]
    assert '"title": cli_title' in queue_window
    assert '"read_only": cli_read_only' in queue_window


def test_import_cli_queue_helper_is_guarded_and_runs_in_background():
    helper_idx = ROUTES_PY.index("def _queue_generated_title_for_imported_session")
    next_helper_idx = ROUTES_PY.index("def _gateway_sse_probe_payload", helper_idx)
    block = ROUTES_PY[helper_idx:next_helper_idx]
    assert "cli_meta.get(\"read_only\")" in block
    assert "not _looks_like_default_cli_title(cli_meta)" in block
    assert "if not _looks_like_default_cli_title(current_meta):" in block
    assert "generate_session_title_for_session(current)" in block
    assert "require_default_title=True" in block
    assert "threading.Thread(target=_run, daemon=True" in block


def test_import_cli_queue_helper_generates_title_once_for_placeholder_session(monkeypatch):
    persisted = []
    generated = []

    class FakeSession:
        def __init__(self, title):
            self.session_id = "cli_queued_title"
            self.title = title
            self.source_tag = "cli"
            self.raw_source = "cli"
            self.session_source = "external_agent"
            self.source_label = "CLI"
            self.read_only = False

    current = FakeSession("CLI Session")

    class InlineThread:
        def __init__(self, *, target, daemon, name):
            self._target = target

        def start(self):
            self._target()

    monkeypatch.setattr(routes.threading, "Thread", InlineThread)
    monkeypatch.setattr(routes.Session, "load", classmethod(lambda _cls, sid: current if sid == current.session_id else None))
    monkeypatch.setattr(routes, "_ensure_full_session_before_mutation", lambda sid, session: session)
    monkeypatch.setattr(routes, "generate_session_title_for_session", lambda session: (generated.append(session.session_id) or "Better imported title", "llm", "raw"))
    monkeypatch.setattr(
        routes,
        "_persist_generated_session_title",
        lambda session, title, *, event_reason, require_default_title=False: persisted.append(
            (session.session_id, title, event_reason, require_default_title)
        ),
    )

    routes._queue_generated_title_for_imported_session(current, {"title": "CLI Session", "source_tag": "cli"})

    assert generated == [current.session_id]
    assert persisted == [(current.session_id, "Better imported title", "session_title_regenerate", True)]


def test_import_cli_queue_helper_skips_sessions_that_already_have_real_titles(monkeypatch):
    generated = []
    persisted = []

    class FakeSession:
        def __init__(self):
            self.session_id = "cli_real_title"
            self.title = "Useful imported title"
            self.source_tag = "cli"
            self.raw_source = "cli"
            self.session_source = "external_agent"
            self.source_label = "CLI"
            self.read_only = False

    current = FakeSession()

    class InlineThread:
        def __init__(self, *, target, daemon, name):
            self._target = target

        def start(self):
            self._target()

    monkeypatch.setattr(routes.threading, "Thread", InlineThread)
    monkeypatch.setattr(routes.Session, "load", classmethod(lambda _cls, sid: current if sid == current.session_id else None))
    monkeypatch.setattr(routes, "_ensure_full_session_before_mutation", lambda sid, session: session)
    monkeypatch.setattr(
        routes,
        "generate_session_title_for_session",
        lambda session: generated.append(session.session_id) or ("Unexpected generated title", "llm", "raw"),
    )
    monkeypatch.setattr(
        routes,
        "_persist_generated_session_title",
        lambda session, title, *, event_reason, require_default_title=False: persisted.append(
            (session.session_id, title, event_reason, require_default_title)
        ),
    )

    routes._queue_generated_title_for_imported_session(
        current,
        {"title": "CLI Session", "source_tag": "cli"},
    )

    assert generated == []
    assert persisted == []


def test_generated_title_persist_reloads_latest_session_before_saving(tmp_path, monkeypatch):
    session_dir = tmp_path / "sessions"
    session_dir.mkdir()
    cache = OrderedDict()

    monkeypatch.setattr(models, "SESSION_DIR", session_dir)
    monkeypatch.setattr(models, "SESSIONS", cache, raising=False)
    monkeypatch.setattr(routes, "SESSIONS", cache, raising=False)
    monkeypatch.setattr(routes, "_sync_session_title_to_insights", lambda session: None)
    monkeypatch.setattr(routes, "_publish_session_list_changed", lambda *args, **kwargs: None)

    stale = models.Session(
        session_id="cli_import_race",
        title="CLI Session",
        workspace=".",
        model="test-model",
        messages=[{"role": "user", "content": "first"}],
        source_tag="cli",
        raw_source="cli",
        session_source="external_agent",
        source_label="CLI",
    )
    stale.save(skip_index=True)
    stale_snapshot = models.Session.load(stale.session_id)

    latest = models.Session(
        session_id=stale.session_id,
        title="CLI Session",
        workspace=".",
        model="test-model",
        messages=[
            {"role": "user", "content": "first"},
            {"role": "assistant", "content": "reply"},
            {"role": "user", "content": "second"},
        ],
        source_tag="cli",
        raw_source="cli",
        session_source="external_agent",
        source_label="CLI",
    )
    latest.save(skip_index=True)
    cache[latest.session_id] = latest

    saved_title = routes._persist_generated_session_title(
        stale_snapshot,
        "Better imported title",
        event_reason="session_title_regenerate",
        require_default_title=True,
    )

    reloaded = models.Session.load(stale.session_id)
    assert saved_title == "Better imported title"
    assert reloaded.title == "Better imported title"
    assert len(reloaded.messages) == 3
    assert reloaded.llm_title_generated is True
    assert reloaded.manual_title is False
    assert len(cache[stale.session_id].messages) == 3
    assert stale_snapshot.title == "Better imported title"


def test_regenerate_endpoint_only_blocks_read_only_imported_sessions():
    endpoint_idx = ROUTES_PY.index('"/api/session/title/regenerate"')
    next_endpoint_idx = ROUTES_PY.index('"/api/personality/set"', endpoint_idx)
    block = ROUTES_PY[endpoint_idx:next_endpoint_idx]
    assert "_get_or_materialize_session(sid)" in block
    assert "except PermissionError:" in block


def test_sessions_ui_keeps_regenerate_action_for_writable_imports():
    regen_idx = SESSIONS_JS.index("api('/api/session/title/regenerate'")
    window = SESSIONS_JS[regen_idx - 500:regen_idx]
    assert "session.is_imported" not in window
    assert "_isReadOnlySession(session)" in SESSIONS_JS
