from __future__ import annotations

from dataclasses import dataclass
from typing import Any

import jwt

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.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_access_token(document_id: str) -> str:
    onlyoffice_settings = resolve_onlyoffice_settings()
    payload = {
        "scope": "onlyoffice-content",
        "document_id": document_id,
    }
    return jwt.encode(payload, onlyoffice_settings.jwt_secret, algorithm="HS256")


def build_onlyoffice_config(
    *,
    document_id: str,
    entry: dict[str, Any],
    current_user: CurrentUserContext,
) -> KnowledgeOnlyOfficeConfigRead:
    settings = get_settings()
    onlyoffice_settings = resolve_onlyoffice_settings()
    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 预览。")

    backend_base_url = onlyoffice_settings.backend_url.rstrip("/")
    public_url = onlyoffice_settings.public_url.rstrip("/")
    access_token = build_onlyoffice_access_token(document_id)
    document_url = (
        f"{backend_base_url}{settings.api_v1_prefix}/knowledge/documents/{document_id}/onlyoffice/content"
        f"?access_token={access_token}"
    )
    callback_url = (
        f"{backend_base_url}{settings.api_v1_prefix}/knowledge/documents/{document_id}/onlyoffice/callback"
    )

    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": False,
                "print": True,
                "copy": True,
            },
        },
        "editorConfig": {
            "mode": "view",
            "lang": "zh-CN",
            "callbackUrl": callback_url,
            "user": {
                "id": current_user.username,
                "name": current_user.name,
            },
            "customization": {
                "compactHeader": True,
                "compactToolbar": True,
                "toolbarNoTabs": False,
                "autosave": False,
                "forcesave": False,
            },
        },
        "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(document_id: str, access_token: str) -> None:
    onlyoffice_settings = resolve_onlyoffice_settings()
    try:
        payload = jwt.decode(
            access_token,
            onlyoffice_settings.jwt_secret,
            algorithms=["HS256"],
        )
    except jwt.PyJWTError as exc:
        raise ValueError("ONLYOFFICE 文件访问令牌无效。") from exc

    if payload.get("scope") != "onlyoffice-content" or payload.get("document_id") != document_id:
        raise ValueError("ONLYOFFICE 文件访问令牌无效。")


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 预览。")
