from __future__ import annotations

import hashlib
import os
import re
import shutil
from dataclasses import dataclass
from pathlib import Path

from app.services.knowledge_constants import FIXED_KNOWLEDGE_FOLDERS

PLATFORM_KNOWLEDGE_SCOPE = "platform"
TENANT_KNOWLEDGE_SCOPE = "tenant"
_TENANT_ID_PATTERN = re.compile(r"^[A-Za-z0-9](?:[A-Za-z0-9_.-]{0,62}[A-Za-z0-9])?$")


def require_knowledge_tenant_id(value: object) -> str:
    tenant_id = str(value or "").strip()
    if not tenant_id:
        raise ValueError("知识库 tenant_id 不能为空。")
    if not _TENANT_ID_PATTERN.fullmatch(tenant_id) or tenant_id in {".", ".."}:
        raise ValueError("知识库 tenant_id 格式不合法。")
    return tenant_id


def resolve_lightrag_workspace(*, tenant_id: str | None, scope: str) -> str:
    base = str(os.environ.get("LIGHTRAG_WORKSPACE") or "x_financial_knowledge").strip()
    base = re.sub(r"[^A-Za-z0-9_-]+", "_", base).strip("_") or "x_financial_knowledge"
    if scope == PLATFORM_KNOWLEDGE_SCOPE:
        suffix = "platform"
    else:
        normalized_tenant_id = require_knowledge_tenant_id(tenant_id)
        suffix = f"tenant_{hashlib.sha256(normalized_tenant_id.encode('utf-8')).hexdigest()[:20]}"
    return f"{base[:80]}__{suffix}"


@dataclass(frozen=True, slots=True)
class KnowledgeStorageScope:
    storage_root: Path
    scope: str
    tenant_id: str | None
    library_root: Path
    index_path: Path
    lightrag_root: Path
    workspace: str
    runtime_cache_key: str
    read_only: bool

    @classmethod
    def tenant(cls, storage_root: Path, tenant_id: object) -> KnowledgeStorageScope:
        normalized_tenant_id = require_knowledge_tenant_id(tenant_id)
        root = Path(storage_root).resolve()
        library_root = root / "knowledge" / "tenants" / normalized_tenant_id
        workspace = resolve_lightrag_workspace(
            tenant_id=normalized_tenant_id,
            scope=TENANT_KNOWLEDGE_SCOPE,
        )
        return cls(
            storage_root=root,
            scope=TENANT_KNOWLEDGE_SCOPE,
            tenant_id=normalized_tenant_id,
            library_root=library_root,
            index_path=library_root / ".index.json",
            lightrag_root=library_root / ".lightrag",
            workspace=workspace,
            runtime_cache_key=f"tenant:{hashlib.sha256(normalized_tenant_id.encode('utf-8')).hexdigest()}",
            read_only=False,
        )

    @classmethod
    def platform(cls, storage_root: Path) -> KnowledgeStorageScope:
        root = Path(storage_root).resolve()
        library_root = root / "knowledge" / "platform"
        workspace = resolve_lightrag_workspace(
            tenant_id=None,
            scope=PLATFORM_KNOWLEDGE_SCOPE,
        )
        return cls(
            storage_root=root,
            scope=PLATFORM_KNOWLEDGE_SCOPE,
            tenant_id=None,
            library_root=library_root,
            index_path=library_root / ".index.json",
            lightrag_root=library_root / ".lightrag",
            workspace=workspace,
            runtime_cache_key=PLATFORM_KNOWLEDGE_SCOPE,
            read_only=True,
        )


def migrate_legacy_library_to_platform(scope: KnowledgeStorageScope) -> bool:
    """保留旧文件并复制到显式 platform 空间，避免升级时丢失现有制度资料。"""
    if scope.scope != PLATFORM_KNOWLEDGE_SCOPE or scope.index_path.exists():
        return False

    legacy_root = scope.storage_root / "knowledge"
    legacy_index = legacy_root / ".index.json"
    has_legacy_documents = any(
        (legacy_root / folder_name).is_dir() for folder_name in FIXED_KNOWLEDGE_FOLDERS
    )
    if not legacy_index.exists() and not has_legacy_documents:
        return False

    scope.library_root.mkdir(parents=True, exist_ok=True)
    for folder_name in FIXED_KNOWLEDGE_FOLDERS:
        source = legacy_root / folder_name
        target = scope.library_root / folder_name
        if source.is_dir():
            shutil.copytree(source, target, dirs_exist_ok=True)

    legacy_lightrag_root = legacy_root / ".lightrag"
    if legacy_lightrag_root.is_dir():
        shutil.copytree(
            legacy_lightrag_root,
            scope.lightrag_root,
            dirs_exist_ok=True,
        )
        legacy_workspace = legacy_lightrag_root / scope.workspace.split("__", maxsplit=1)[0]
        platform_workspace = scope.lightrag_root / scope.workspace
        if legacy_workspace.is_dir() and not platform_workspace.exists():
            shutil.copytree(legacy_workspace, platform_workspace)
    if legacy_index.exists():
        shutil.copy2(legacy_index, scope.index_path)
    return True
