from __future__ import annotations

from pathlib import Path
from typing import Any

from sqlalchemy.orm import Session

from app.services.knowledge import KnowledgeService
from app.services.knowledge_onlyoffice_security import (
    KnowledgeOnlyOfficeSessionService,
    OnlyOfficeSecurityError,
    OnlyOfficeValidatedSession,
    download_onlyoffice_document,
)
from app.services.knowledge_tenant_scope import PLATFORM_KNOWLEDGE_SCOPE

_WRITE_CALLBACK_STATUSES = {2, 6}


def resolve_onlyoffice_content(
    *,
    db: Session,
    storage_root: Path | None,
    document_id: str,
    access_token: str,
) -> tuple[Path, str, str]:
    validated = KnowledgeOnlyOfficeSessionService(db).validate_content(
        document_id=document_id,
        token=access_token,
    )
    service = _service_for_session(
        db=db,
        storage_root=storage_root,
        session=validated,
    )
    entry = service.get_document_entry(document_id)
    if (
        service._build_onlyoffice_document_key(entry) != validated.document_key
        or int(entry.get("version_number") or 1) != validated.document_version
    ):
        raise OnlyOfficeSecurityError("ONLYOFFICE 会话绑定的文档版本已失效。")
    return service.get_document_content(document_id)


def handle_onlyoffice_callback(
    *,
    db: Session,
    storage_root: Path | None,
    document_id: str,
    callback_token: str,
    payload: dict[str, Any],
) -> None:
    session_service = KnowledgeOnlyOfficeSessionService(db)
    session_service.validate_callback(
        document_id=document_id,
        token=callback_token,
    )
    try:
        callback_status = int(payload.get("status") or 0)
    except (TypeError, ValueError) as exc:
        raise ValueError("ONLYOFFICE 回调状态不合法。") from exc
    if callback_status not in _WRITE_CALLBACK_STATUSES:
        return

    download_url = str(payload.get("url") or "").strip()
    if not download_url:
        raise ValueError("ONLYOFFICE 回写回调缺少下载 URL。")
    claimed = session_service.claim_callback(
        document_id=document_id,
        token=callback_token,
        payload_document_key=str(payload.get("key") or "").strip(),
    )
    try:
        service = _service_for_session(
            db=db,
            storage_root=storage_root,
            session=claimed,
        )
        entry = service.get_document_entry(document_id)
        if (
            service._build_onlyoffice_document_key(entry) != claimed.document_key
            or int(entry.get("version_number") or 1) != claimed.document_version
        ):
            raise OnlyOfficeSecurityError("ONLYOFFICE 编辑基线已失效。")
        content = download_onlyoffice_document(
            download_url,
            expected_filename=str(entry.get("original_name") or ""),
        )
        users = [str(item).strip() for item in payload.get("users") or [] if str(item).strip()]
        service.replace_document_content_from_onlyoffice(
            document_id,
            content,
            actor_name=users[0] if users else "ONLYOFFICE",
            expected_document_key=claimed.document_key,
            expected_version=claimed.document_version,
        )
    except Exception as exc:
        session_service.finish_callback(
            claimed.jti,
            succeeded=False,
            failure_reason=type(exc).__name__,
        )
        raise
    session_service.finish_callback(claimed.jti, succeeded=True)


def _service_for_session(
    *,
    db: Session,
    storage_root: Path | None,
    session: OnlyOfficeValidatedSession,
) -> KnowledgeService:
    if session.resource_scope == PLATFORM_KNOWLEDGE_SCOPE:
        return KnowledgeService(
            storage_root=storage_root,
            db=db,
            scope=PLATFORM_KNOWLEDGE_SCOPE,
        )
    if session.tenant_id is None:
        raise ValueError("ONLYOFFICE 租户会话缺少 tenant_id。")
    return KnowledgeService(
        storage_root=storage_root,
        db=db,
        tenant_id=session.tenant_id,
    )
