from __future__ import annotations

from datetime import UTC, datetime, timedelta

from sqlalchemy import create_engine
from sqlalchemy.orm import Session
from sqlalchemy.pool import StaticPool

from app.models import Base
from app.models.ai_config import SystemConfig
from app.models.chat import ChatMessage, ChatSession, TopicSession
from app.models.entitlement import EntitlementPlan
from app.models.growth import GrowthProfileRevision, TopicSummary, UserGrowthProfile
from app.models.user import User
from app.services.entitlement_service import EntitlementService
from app.services.growth_profile_service import GrowthProfileService, _parse_summary_json
from app.services.rag_service import PromptService
from app.services.topic_settlement_worker import TopicSettlementWorker


def _db() -> Session:
    engine = create_engine("sqlite:///:memory:", connect_args={"check_same_thread": False}, poolclass=StaticPool)
    Base.metadata.create_all(engine)
    return Session(engine)


def _now() -> datetime:
    return datetime.now(UTC).replace(tzinfo=None)


def test_finish_topic_enqueues_summary_and_worker_updates_growth_profile_for_enabled_plan():
    with _db() as db:
        db.add(SystemConfig(config_key="mock_model_enabled", config_value="true"))
        db.add(EntitlementPlan(id=10, name="深度陪伴版", plan_type="deep", monthly_topic_limit=90, enable_growth_profile=1, status=1))
        user = User(id=1, phone="13800000001", name="学员", daily_chat_limit=100, daily_chat_used=0)
        session = ChatSession(id=1, user_id=1, title="阴影人格", message_count=2, last_message_at=_now(), is_deleted=0)
        topic = TopicSession(
            id=1,
            user_id=1,
            chat_session_id=1,
            title="阴影人格练习",
            core_question="阴影人格练习步骤是什么",
            status="active",
            message_count=2,
            quota_deducted=1,
            started_at=_now(),
        )
        db.add_all([user, session, topic])
        db.add_all(
            [
                ChatMessage(id=1, session_id=1, topic_session_id=1, user_id=1, role="user", content="我做阴影人格会抗拒，身体发紧", created_at=_now()),
                ChatMessage(id=2, session_id=1, topic_session_id=1, user_id=1, role="assistant", content="先回到身体感受，观察抗拒，不急着分析。", created_at=_now()),
            ]
        )
        db.commit()
        EntitlementService.assign_user_plan(db, user=user, plan_id=10, operated_by=1)
        db.commit()

        result = GrowthProfileService.finish_active_topic(db, user=user, session=session)

        assert result["topic"]["status"] == "completed"
        assert result["settlementStatus"] == "pending"
        assert result["summary"]["summary"] == ""
        assert result["profile"] is None
        assert db.query(UserGrowthProfile).filter_by(user_id=1).count() == 0

        summary_id = TopicSettlementWorker.claim_next(db, worker_id="test-worker", now=_now())
        assert summary_id == result["summary"]["id"]
        completed = TopicSettlementWorker.execute_claimed(db, summary_id=summary_id, worker_id="test-worker")

        assert completed is not None
        assert completed.status == "success"
        assert completed.summary
        profile = db.query(UserGrowthProfile).filter_by(user_id=1).one()
        assert profile.schema_version == 2
        assert "本次主要谈到" in (profile.recent_review or "")
        assert profile.current_focus == "我做阴影人格会抗拒，身体发紧"
        assert profile.profile_text == ""
        assert profile.common_emotions is None
        assert profile.body_patterns is None
        assert profile.homework_done is None
        assert profile.effective_homework is None
        assert profile.recent_progress is None
        assert db.query(UserGrowthProfile).filter_by(user_id=1).count() == 1
        assert db.query(GrowthProfileRevision).filter_by(user_id=1).count() == 1


def test_finish_topic_is_idempotent_and_does_not_call_model_in_request(monkeypatch):
    with _db() as db:
        now = _now()
        user = User(id=1, phone="13800000001", name="学员", daily_chat_limit=100, daily_chat_used=0)
        session = ChatSession(id=1, user_id=1, title="测试", message_count=1, last_message_at=now, is_deleted=0)
        topic = TopicSession(
            id=1,
            user_id=1,
            chat_session_id=1,
            title="测试主题",
            core_question="我想沉淀一下",
            status="active",
            message_count=1,
            started_at=now,
        )
        message = ChatMessage(id=1, session_id=1, topic_session_id=1, user_id=1, role="user", content="我想沉淀一下", created_at=now)
        db.add_all([user, session, topic, message])
        db.commit()
        monkeypatch.setattr(
            "app.services.tracked_generation_service.TrackedGenerationService.generate",
            lambda *_args, **_kwargs: (_ for _ in ()).throw(AssertionError("结束主题接口不应调用模型")),
        )

        first = GrowthProfileService.finish_active_topic(db, user=user, session=session)
        second = GrowthProfileService.finish_active_topic(db, user=user, session=session)

        assert first["summary"]["id"] == second["summary"]["id"]
        assert first["settlementStatus"] == "pending"
        assert second["settlementStatus"] == "pending"
        assert db.query(TopicSummary).count() == 1


def test_failed_topic_settlement_is_retried(monkeypatch):
    with _db() as db:
        now = _now()
        user = User(id=1, phone="13800000001", name="学员", daily_chat_limit=100, daily_chat_used=0)
        session = ChatSession(id=1, user_id=1, title="测试", message_count=1, last_message_at=now, is_deleted=0)
        topic = TopicSession(id=1, user_id=1, chat_session_id=1, title="测试", core_question="测试", status="active", started_at=now)
        message = ChatMessage(id=1, session_id=1, topic_session_id=1, user_id=1, role="user", content="测试", created_at=now)
        db.add_all([user, session, topic, message])
        db.commit()
        result = GrowthProfileService.finish_active_topic(db, user=user, session=session)
        monkeypatch.setattr(
            GrowthProfileService,
            "process_topic_settlement",
            staticmethod(lambda *_args, **_kwargs: (_ for _ in ()).throw(RuntimeError("模型暂时不可用"))),
        )

        summary_id = TopicSettlementWorker.claim_next(db, worker_id="retry-worker", now=_now())
        retried = TopicSettlementWorker.execute_claimed(db, summary_id=summary_id, worker_id="retry-worker")

        assert summary_id == result["summary"]["id"]
        assert retried is not None
        assert retried.status == "pending"
        assert retried.attempt_count == 1
        assert retried.next_run_at is not None
        assert "模型暂时不可用" in (retried.error_message or "")


def test_stale_topic_settlement_is_recovered_after_restart():
    with _db() as db:
        now = _now()
        user = User(id=1, phone="13800000001", name="学员", daily_chat_limit=100, daily_chat_used=0)
        session = ChatSession(id=1, user_id=1, title="测试", message_count=1, last_message_at=now, is_deleted=0)
        topic = TopicSession(id=1, user_id=1, chat_session_id=1, title="测试", core_question="测试", status="completed", started_at=now, ended_at=now)
        summary = TopicSummary(
            id=1,
            topic_session_id=1,
            user_id=1,
            summary="",
            status="running",
            attempt_count=1,
            max_attempts=3,
            locked_by="stopped-worker",
            locked_at=now - timedelta(minutes=31),
        )
        db.add_all([user, session, topic, summary])
        db.commit()

        recovered = TopicSettlementWorker.recover_stale_jobs(db, now=now)
        db.commit()
        db.refresh(summary)

        assert recovered == 1
        assert summary.status == "pending"
        assert summary.locked_by is None
        assert summary.next_run_at == now
        assert "自动恢复" in (summary.error_message or "")


def test_prompt_includes_only_v2_recent_review_without_replacing_knowledge_context():
    with _db() as db:
        user = User(id=1, phone="13800000001", name="学员", daily_chat_limit=100, daily_chat_used=0)
        db.add(user)
        db.add(
            UserGrowthProfile(
                user_id=1,
                schema_version=2,
                recent_review="最近谈到面对领导时不敢表达。",
                current_focus="面对领导时如何回到当下",
                profile_text="旧画像不应进入提示词",
                common_emotions="紧张",
                body_patterns="身体紧绷",
            )
        )
        db.commit()

        growth_context = GrowthProfileService.prompt_context(db, user)
        messages = PromptService.build_messages(
            db,
            "我现在又表达不出来了怎么办",
            [],
            growth_context=growth_context,
        )

        content = "\n".join(item["content"] for item in messages)
        assert "[近期实修回顾]" in content
        assert "面对领导时如何回到当下" in content
        assert "旧画像不应进入提示词" not in content
        assert "身体紧绷" not in content
        assert "常见情绪" not in content
        assert "[本轮可靠知识上下文]" in content


def test_legacy_growth_profile_is_not_injected_before_v2_rebuild():
    with _db() as db:
        user = User(id=1, phone="13800000001", name="学员", daily_chat_limit=100, daily_chat_used=0)
        db.add_all(
            [
                user,
                UserGrowthProfile(
                    user_id=1,
                    schema_version=1,
                    profile_text="旧版长期人格画像",
                    common_emotions="焦虑",
                ),
            ]
        )
        db.commit()

        assert GrowthProfileService.prompt_context(db, user) is None


def test_summary_json_parser_ignores_model_reasoning_and_uses_final_json():
    raw = """
<think>
先分析一下，并给出一个未完成草稿：
```json
{"reviewText": "不应采用的草稿"}
```
</think>
```json
{
  "reviewText": "只保留最终近期回顾",
  "currentFocus": ["当下正在谈到的问题"]
}
```
"""

    parsed = _parse_summary_json(raw)

    assert parsed is not None
    assert parsed["reviewText"] == "只保留最终近期回顾"
    assert "think" not in str(parsed).lower()
