from __future__ import annotations

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 ModelConfig, SystemConfig
from app.models.chat import ChatMessage, ChatSession
from app.services.chat_context_service import ChatContextService
from app.services.model_service import (
    ModelClientService,
    _fit_source_messages,
    _max_output_tokens,
    _openai_messages,
    _rough_token_count,
    _system_and_turn_messages,
)
from app.services.rag_service import PromptService, RagResult


def _database(context_count: str = "10") -> Session:
    engine = create_engine("sqlite:///:memory:", connect_args={"check_same_thread": False}, poolclass=StaticPool)
    Base.metadata.create_all(engine)
    db = Session(engine)
    db.add(SystemConfig(id=1, config_key="chat_context_message_count", config_value=context_count))
    db.commit()
    return db


def _messages(count: int) -> list[ChatMessage]:
    result = []
    for index in range(1, count + 1):
        result.append(
            ChatMessage(
                id=index,
                session_id=1,
                user_id=1,
                role="user" if index % 2 else "assistant",
                content=f"消息{index}",
            )
        )
    return result


def test_context_message_count_is_read_at_runtime_and_zero_disables_all_memory():
    with _database("2") as db:
        history = _messages(4)
        messages = PromptService.build_messages(db, "现在的问题", [], history, "旧摘要")
        assert [item["content"] for item in messages if item["role"] in {"user", "assistant"}] == [
            "消息3",
            "消息4",
            "现在的问题",
        ]
        assert any("旧摘要" in item["content"] for item in messages)

        config = db.query(SystemConfig).filter_by(config_key="chat_context_message_count").one()
        config.config_value = "0"
        db.commit()
        messages = PromptService.build_messages(db, "现在的问题", [], history, "旧摘要")
        assert [item["content"] for item in messages if item["role"] in {"user", "assistant"}] == ["现在的问题"]
        assert all("旧摘要" not in item["content"] for item in messages)


def test_messages_leaving_window_are_summarized_immediately_and_summary_is_rewritten(monkeypatch):
    with _database("4") as db:
        session = ChatSession(id=1, user_id=1, title="测试", summary="已有摘要", message_count=6)
        captured = {}

        def summarize(_db, messages_text, previous_summary=None):
            captured["messages"] = messages_text
            captured["previous"] = previous_summary
            return "覆盖后的完整摘要"

        monkeypatch.setattr(ModelClientService, "summarize_or_raise", summarize)
        result = ChatContextService.update_summary(db, session, _messages(6))

        assert captured == {"messages": "用户：消息1\n大本营答疑助手：消息2", "previous": "已有摘要"}
        assert session.summary == "覆盖后的完整摘要"
        assert session.summary_up_to_message_id == 2
        assert result.trace["status"] == "success"


def test_messages_already_covered_by_summary_are_not_sent_twice_after_limit_increases():
    with _database("4") as db:
        messages = PromptService.build_messages(
            db,
            "现在的问题",
            [],
            _messages(4),
            "已经覆盖消息1到消息3的摘要",
            summary_up_to_message_id=3,
        )
        turns = [item["content"] for item in messages if item["role"] in {"user", "assistant"}]
        assert turns == ["消息4", "现在的问题"]


def test_summary_failure_is_visible_and_does_not_break_chat(monkeypatch, caplog):
    with _database("2") as db:
        session = ChatSession(id=9, user_id=1, title="测试", summary=None, message_count=4)

        def fail(*_args, **_kwargs):
            raise RuntimeError("摘要服务不可用")

        monkeypatch.setattr(ModelClientService, "summarize_or_raise", fail)
        result = ChatContextService.update_summary(db, session, _messages(4))

        assert session.summary is None
        assert result.trace["status"] == "fallback"
        assert result.trace["error"] == "摘要服务不可用"
        assert "Chat history summary failed" in caplog.text


def test_provider_payload_uses_native_roles_and_normalizes_leading_assistant():
    rag = RagResult(
        question="当前问题",
        knowledge_scopes=[],
        chunks=[],
        prompt="兼容文本",
        messages=[
            {"role": "system", "content": "系统规则"},
            {"role": "assistant", "content": "窗口从助手消息开始"},
            {"role": "user", "content": "历史问题"},
            {"role": "assistant", "content": "历史回答"},
            {"role": "system", "content": "知识上下文"},
            {"role": "user", "content": "当前问题"},
        ],
    )

    openai_messages = _openai_messages(rag)
    system, turns = _system_and_turn_messages(rag)
    assert [item["role"] for item in openai_messages] == ["system", "user", "assistant", "user"]
    assert "窗口从助手消息开始" in system
    assert "知识上下文" in system
    assert turns[-1] == {"role": "user", "content": "当前问题"}


def test_provider_messages_are_trimmed_to_configured_model_context_window():
    rag = RagResult(
        question="当前问题",
        knowledge_scopes=[],
        chunks=[],
        prompt="兼容文本",
        messages=[
            {"role": "system", "content": "规则" * 500},
            {"role": "user", "content": "很早的问题" * 200},
            {"role": "assistant", "content": "很早的回答" * 200},
            {"role": "system", "content": "知识" * 500},
            {"role": "user", "content": "当前问题"},
        ],
    )
    model = ModelConfig(
        provider="test",
        display_name="test",
        api_type="openai_compatible",
        model_name="test",
        context_window=1000,
        max_token=600,
    )

    fitted = _fit_source_messages(rag, model)
    input_tokens = sum(_rough_token_count(item["content"]) for item in fitted)
    assert input_tokens + _max_output_tokens(model) + 64 <= model.context_window
    assert fitted[-1] == {"role": "user", "content": "当前问题"}
