from __future__ import annotations

from dataclasses import dataclass
from typing import Any
from urllib.parse import quote

import jwt
from sqlalchemy.orm import Session

from app.api.deps import CurrentUserContext
from app.core.config import get_settings
from app.core.logging import get_logger
from app.schemas.knowledge import KnowledgeOnlyOfficeConfigRead
from app.services.knowledge_constants import (
    EXCEL_EXTENSIONS,
    ONLYOFFICE_EDITABLE_EXTENSIONS,
    PPT_EXTENSIONS,
    WORD_EXTENSIONS,
)
from app.services.knowledge_file_utils import extract_extension
from app.services.knowledge_onlyoffice_security import (
    KnowledgeOnlyOfficeSessionService,
    OnlyOfficeValidatedSession,
)
from app.services.knowledge_tenant_scope import (
    PLATFORM_KNOWLEDGE_SCOPE,
    TENANT_KNOWLEDGE_SCOPE,
    require_knowledge_tenant_id,
)
from app.services.settings import resolve_onlyoffice_settings

logger = get_logger("app.services.knowledge")


@dataclass(slots=True)
class OnlyOfficeCallbackPayload:
    status: int
    download_url: str
    users: list[str]


def parse_onlyoffice_callback(payload: dict[str, Any]) -> OnlyOfficeCallbackPayload:
    status = int(payload.get("status") or 0)
    download_url = str(payload.get("url") or "").strip()
    users = [str(item).strip() for item in payload.get("users") or [] if str(item).strip()]
    return OnlyOfficeCallbackPayload(status=status, download_url=download_url, users=users)


def build_onlyoffice_document_key(entry: dict[str, Any]) -> str:
    version = int(entry.get("version_number", 1))
    checksum = str(entry.get("sha256") or "")[:12]
    return f"{entry['id']}-v{version}-{checksum or 'nochecksum'}"


def build_onlyoffice_config(
    *,
    db: Session,
    document_id: str,
    entry: dict[str, Any],
    current_user: CurrentUserContext,
    resource_scope: str,
    tenant_id: str | None,
    editable: bool = False,
) -> KnowledgeOnlyOfficeConfigRead:
    settings = get_settings()
    onlyoffice_settings = resolve_onlyoffice_settings(db)
    if not onlyoffice_settings.enabled:
        logger.warning(
            "ONLYOFFICE disabled in runtime config doc=%s enabled=%s "
            "public_url=%s backend_url=%s jwt_set=%s",
            document_id,
            onlyoffice_settings.enabled,
            onlyoffice_settings.public_url,
            onlyoffice_settings.backend_url,
            bool(onlyoffice_settings.jwt_secret),
        )
        raise ValueError("ONLYOFFICE 预览未启用。")
    if not onlyoffice_settings.public_url or not onlyoffice_settings.backend_url:
        logger.warning(
            "ONLYOFFICE config incomplete doc=%s enabled=%s "
            "public_url=%s backend_url=%s jwt_set=%s",
            document_id,
            onlyoffice_settings.enabled,
            onlyoffice_settings.public_url,
            onlyoffice_settings.backend_url,
            bool(onlyoffice_settings.jwt_secret),
        )
        raise ValueError("ONLYOFFICE 地址配置不完整。")
    if not onlyoffice_settings.jwt_secret:
        logger.warning(
            "ONLYOFFICE JWT missing doc=%s enabled=%s public_url=%s backend_url=%s jwt_set=%s",
            document_id,
            onlyoffice_settings.enabled,
            onlyoffice_settings.public_url,
            onlyoffice_settings.backend_url,
            bool(onlyoffice_settings.jwt_secret),
        )
        raise ValueError("ONLYOFFICE JWT 密钥未配置。")

    extension = extract_extension(entry["original_name"])
    if extension not in ONLYOFFICE_EDITABLE_EXTENSIONS:
        raise ValueError("当前文件格式不支持 ONLYOFFICE 预览。")
    requester_tenant_id = require_knowledge_tenant_id(current_user.tenant_id)
    if resource_scope == TENANT_KNOWLEDGE_SCOPE:
        normalized_tenant_id = require_knowledge_tenant_id(tenant_id)
        if requester_tenant_id != normalized_tenant_id:
            raise ValueError("当前用户不能访问其他租户的知识文档。")
    elif resource_scope == PLATFORM_KNOWLEDGE_SCOPE:
        if editable:
            raise ValueError("平台知识文档为只读资源，不能编辑。")
    else:
        raise ValueError("知识文档 scope 不合法。")
    if editable and not (
        current_user.is_admin
        or "manager" in {str(item).strip().lower() for item in current_user.role_codes}
    ):
        raise ValueError("只有租户管理员可以编辑知识文档。")

    backend_base_url = onlyoffice_settings.backend_url.rstrip("/")
    public_url = onlyoffice_settings.public_url.rstrip("/")
    session_tokens = KnowledgeOnlyOfficeSessionService(db).issue(
        resource_scope=resource_scope,
        tenant_id=requester_tenant_id,
        document_id=document_id,
        entry=entry,
        current_user=current_user,
        editable=editable,
    )
    document_url = (
        f"{backend_base_url}{settings.api_v1_prefix}/knowledge/documents/{document_id}/onlyoffice/content"
        f"?access_token={quote(session_tokens.content_token, safe='')}"
    )
    callback_url = (
        f"{backend_base_url}{settings.api_v1_prefix}/knowledge/documents/{document_id}/onlyoffice/callback"
        f"?callback_token={quote(session_tokens.callback_token, safe='')}"
    )

    config: dict[str, Any] = {
        "documentType": resolve_onlyoffice_document_type(extension),
        "document": {
            "fileType": extension,
            "key": build_onlyoffice_document_key(entry),
            "title": entry["original_name"],
            "url": document_url,
            "permissions": {
                "download": True,
                "edit": editable,
                "print": True,
                "copy": True,
            },
        },
        "editorConfig": {
            "mode": "edit" if editable else "view",
            "lang": "zh-CN",
            "callbackUrl": callback_url,
            "user": {
                "id": current_user.username,
                "name": current_user.name,
            },
            "customization": {
                "compactHeader": True,
                "compactToolbar": True,
                "toolbarNoTabs": False,
                "autosave": editable,
                "forcesave": editable,
            },
        },
        "width": "100%",
        "height": "100%",
    }
    config["token"] = jwt.encode(config, onlyoffice_settings.jwt_secret, algorithm="HS256")
    return KnowledgeOnlyOfficeConfigRead(documentServerUrl=public_url, config=config)


def validate_onlyoffice_access_token(
    db: Session,
    document_id: str,
    access_token: str,
) -> OnlyOfficeValidatedSession:
    return KnowledgeOnlyOfficeSessionService(db).validate_content(
        document_id=document_id,
        token=access_token,
    )


def resolve_onlyoffice_document_type(extension: str) -> str:
    if extension in WORD_EXTENSIONS:
        return "word"
    if extension in EXCEL_EXTENSIONS:
        return "cell"
    if extension in PPT_EXTENSIONS:
        return "slide"
    raise ValueError("当前文件格式不支持 ONLYOFFICE 预览。")
