"""메모 → 문서 승격 시 거친 메모를 구조화된 마크다운 문서로 정리 (26B, P2).

`POST /memos/{id}/promote-to-document` 가 `source_metadata.needs_draft=true` 마커를
찍으면 본 스케줄 워커가 집어 AIClient.call_primary(26B Mac mini = 로컬, 과금규칙 부합)로
md_content 를 생성한다. markdown canonical Phase 1A 스키마 재사용:
  - content_origin='ai_drafted'  +  md_draft_status='draft'
    (migration 212 제약: md_draft_status NOT NULL → content_origin='ai_drafted' 필수)
  - md_status='success', md_extraction_engine='ai_draft'
원본 메모는 extracted_text 에 보존(검색/청크는 원문 사용). "필요시" = 이미 정돈된 메모는
프롬프트가 형식만 다듬고, 거친 메모는 구조화하도록 지시(사실 추가 금지).
"""

import logging
from datetime import datetime, timezone

from sqlalchemy import select

from ai.client import AIClient, strip_thinking
from core.database import async_session
from models.document import Document
from services.search.llm_gate import Priority, acquire_mlx_gate

logger = logging.getLogger(__name__)

# 한 번에 처리할 승격 문서 수 (26B 콜 = 무겁다 → 소량 순차). interval 잡이라 다음 틱에 이어 처리.
_BATCH = 2
# 너무 짧은 메모는 문서화 의미 없음 — 마커만 정리하고 md 생성 스킵.
_MIN_CHARS = 20

_DRAFT_SYSTEM = (
    "당신은 사용자의 거친 메모를 사실 추가 없이 깔끔한 마크다운 문서로 정리하는 도우미입니다."
)
_DRAFT_PROMPT = """다음은 사용자가 빠르게 적은 메모입니다. 이를 정식 자료 문서로 정리하세요.

규칙:
- 메모에 있는 정보만 사용하고, 내용·사실을 추가하거나 추측하지 마세요.
- 이미 잘 정돈돼 있으면 형식만 다듬고, 거친 메모면 제목·소제목·목록으로 구조화하세요.
- 원문 언어를 유지하세요(한국어는 한국어, 영어는 영어).
- 출력은 마크다운 본문만. 인사말·메타 설명 없이 문서 내용만 출력하세요.

--- 메모 ---
{content}
--- 끝 ---"""


async def _ids_needing_draft() -> list[int]:
    async with async_session() as session:
        rows = (
            await session.execute(
                select(Document.id)
                .where(
                    Document.deleted_at.is_(None),
                    # JSONB 마커 (json/jsonb 공통 ->> 연산자). promote 가 needs_draft=true 세팅.
                    Document.source_metadata.op("->>")("needs_draft") == "true",
                )
                .order_by(Document.id)
                .limit(_BATCH)
            )
        ).scalars().all()
    return list(rows)


async def run() -> None:
    """needs_draft 마커가 찍힌 승격 문서를 26B로 문서화 (interval job, no-arg)."""
    ids = await _ids_needing_draft()
    if not ids:
        return

    client = AIClient()
    for doc_id in ids:
        # 문서별 독립 세션·트랜잭션 — 1건 실패가 나머지를 막지 않게.
        async with async_session() as session:
            try:
                doc = await session.get(Document, doc_id)
                if doc is None or not (doc.source_metadata or {}).get("needs_draft"):
                    continue  # 경합/이미 처리됨

                source = (doc.extracted_text or "").strip()
                now = datetime.now(timezone.utc)
                meta = dict(doc.source_metadata or {})

                md = ""
                if len(source) >= _MIN_CHARS:
                    # 26B 호출은 반드시 mlx gate(Semaphore 1) 안에서 — 동시 호출 pile-up 방지
                    # ([[feedback_llm_verification_load_pileup]]). BACKGROUND = 사용자 대면보다 양보.
                    async with acquire_mlx_gate(Priority.BACKGROUND):
                        raw = await client.call_primary(
                            _DRAFT_PROMPT.format(content=source), system=_DRAFT_SYSTEM
                        )
                    md = strip_thinking(raw or "").strip()

                if md:
                    doc.md_content = md
                    # 제약(212): md_draft_status NOT NULL 이면 content_origin='ai_drafted' 여야 함.
                    doc.content_origin = "ai_drafted"
                    doc.md_draft_status = "draft"
                    doc.md_status = "success"
                    doc.md_extraction_engine = "ai_draft"
                    doc.md_generated_at = now
                    meta["drafted_at"] = now.isoformat()

                # 성공/스킵 모두 마커 해제(무한 재시도 방지). 26B 호출 자체가 예외면 except 로 빠져 마커 유지.
                meta["needs_draft"] = False
                doc.source_metadata = meta
                doc.updated_at = now
                await session.commit()
                logger.info("memo_draft doc=%s md_len=%d", doc_id, len(md))
            except Exception:
                logger.exception("memo_draft 실패 doc=%s (다음 틱 재시도)", doc_id)
                await session.rollback()
