from __future__ import annotations

from datetime import UTC, datetime

from sqlalchemy import extract, func, select
from sqlalchemy.orm import Session

from app.models.chat import ChatMessage, ChatSession, TopicSession
from app.models.growth import TopicSummary
from app.models.user import User


class TopicSessionService:
    @staticmethod
    def active_for_session(db: Session, *, user: User, session: ChatSession) -> TopicSession | None:
        return db.scalar(
            select(TopicSession)
            .where(
                TopicSession.user_id == user.id,
                TopicSession.chat_session_id == session.id,
                TopicSession.status == "active",
            )
            .order_by(TopicSession.created_at.desc(), TopicSession.id.desc())
            .limit(1)
        )

    @staticmethod
    def monthly_used_count(db: Session, user_id: int, *, at: datetime | None = None) -> int:
        current = at or _now()
        return int(
            db.scalar(
                select(func.count(TopicSession.id)).where(
                    TopicSession.user_id == user_id,
                    extract("year", TopicSession.started_at) == current.year,
                    extract("month", TopicSession.started_at) == current.month,
                    TopicSession.quota_deducted == 1,
                )
            )
            or 0
        )

    @staticmethod
    def get_or_create_active(
        db: Session,
        *,
        user: User,
        session: ChatSession,
        question: str,
        deduct_quota: bool,
    ) -> TopicSession:
        topic = TopicSessionService.active_for_session(db, user=user, session=session)
        if topic is not None:
            return topic
        # A finished topic is a real memory boundary. Its durable summary lives in
        # TopicSummary / growth profile; the rolling ChatSession summary must start
        # clean for the next topic in the same chat window.
        session.summary = None
        session.summary_up_to_message_id = None
        db.add(session)
        topic = TopicSession(
            user_id=user.id,
            chat_session_id=session.id,
            title=_title_from_question(question),
            core_question=question.strip(),
            status="active",
            message_count=0,
            token_input=0,
            token_output=0,
            quota_deducted=1 if deduct_quota else 0,
            started_at=_now(),
        )
        db.add(topic)
        db.flush()
        return topic

    @staticmethod
    def attach_user_message(message: ChatMessage, topic: TopicSession) -> None:
        message.topic_session_id = topic.id
        topic.message_count += 1

    @staticmethod
    def attach_assistant_message(
        message: ChatMessage,
        topic: TopicSession,
        *,
        token_input: int | None,
        token_output: int | None,
    ) -> None:
        message.topic_session_id = topic.id
        topic.message_count += 1
        topic.token_input += int(token_input or 0)
        topic.token_output += int(token_output or 0)

    @staticmethod
    def topic_dict(topic: TopicSession) -> dict:
        return {
            "id": topic.id,
            "userId": topic.user_id,
            "chatSessionId": topic.chat_session_id,
            "title": topic.title,
            "coreQuestion": topic.core_question,
            "status": topic.status,
            "messageCount": topic.message_count,
            "tokenInput": topic.token_input,
            "tokenOutput": topic.token_output,
            "quotaDeducted": bool(topic.quota_deducted),
            "helpCardGenerated": bool(topic.help_card_generated),
            "shareDraftGenerated": bool(topic.share_draft_generated),
            "startedAt": topic.started_at,
            "endedAt": topic.ended_at,
            "updatedAt": topic.updated_at,
        }

    @staticmethod
    def prompt_context(db: Session, topic: TopicSession) -> tuple[str, bool]:
        """Build bounded topic context shared by formal chat and admin preview."""
        summary = db.scalar(
            select(TopicSummary)
            .where(TopicSummary.topic_session_id == topic.id)
            .order_by(TopicSummary.generated_at.desc(), TopicSummary.id.desc())
            .limit(1)
        )
        lines = [
            "[当前对话主题]",
            "以下信息用于延续当前主题，不能替代本轮可靠知识，也不得据此臆造课程内容。",
            f"主题：{_limit(topic.title, 240)}",
            f"核心问题：{_limit(topic.core_question, 1200)}",
            f"主题状态：{topic.status}",
        ]
        if summary is not None and summary.schema_version >= 2 and summary.status in {"success", "fallback"}:
            lines.extend(
                [
                    "\n[所选主题摘要]",
                    _limit(summary.summary, 2400),
                ]
            )
            details = [
                ("本次关注", summary.main_events),
                ("可以继续留意", summary.next_observation),
            ]
            lines.extend(f"{label}：{_limit(value, 800)}" for label, value in details if value)
            return "\n".join(lines), True
        return "\n".join(lines), False


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


def _title_from_question(question: str) -> str:
    title = question.strip().replace("\n", " ")
    return title[:40] if title else "新主题"


def _limit(value: str, limit: int) -> str:
    text = value.strip()
    return text if len(text) <= limit else text[:limit].rstrip() + "…"
