# SPDX-FileCopyrightText: 2025 QinHan
# SPDX-License-Identifier: MPL-2.0
"""
共享术语表持久化存储。

设计要点：
- 进程级单例，所有用户读写同一份 JSON 文件。
- 原子写入（临时文件 + os.replace），asyncio.Lock 保护并发写。
- 三列结构 {zh, en, id}，按目标语言编译为单向 {src: dst}（见 lang_mapping.py）。
"""
import asyncio
import json
import os
import tempfile
from pathlib import Path
from typing import Any

from docutranslate.logger import global_logger

_COLS = ("zh", "en", "id")
_VERSION = 1


def _default_path() -> Path:
    """解析术语表存储路径：优先环境变量，否则工作目录下 ./data/glossary.json"""
    from docutranslate.environment import DOCUTRANSLATE_GLOSSARY_PATH
    if DOCUTRANSLATE_GLOSSARY_PATH:
        p = Path(DOCUTRANSLATE_GLOSSARY_PATH).expanduser()
    else:
        p = Path.cwd() / "data" / "glossary.json"
    p.parent.mkdir(parents=True, exist_ok=True)
    return p


def _normalize_row(raw: Any) -> dict:
    """规范化一行：确保 zh/en/id 三键均为 str（空串允许）。"""
    if not isinstance(raw, dict):
        return {c: "" for c in _COLS}
    return {c: str(raw.get(c, "") or "") for c in _COLS}


class GlossaryStore:
    """进程级共享术语表存储（单例）。"""

    _instance = None

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance._initialized = False
        return cls._instance

    def __init__(self):
        if self._initialized:
            return
        self._initialized = True
        self.logger = global_logger
        self._path = _default_path()
        self._lock = asyncio.Lock()
        self._cache: list[dict] | None = None
        self.logger.info(f"术语表存储路径: {self._path}")

    def _read_from_disk(self) -> list[dict]:
        if not self._path.exists():
            return []
        try:
            with self._path.open("r", encoding="utf-8") as f:
                data = json.load(f)
            rows = data.get("rows", []) if isinstance(data, dict) else []
            return [_normalize_row(r) for r in rows]
        except (OSError, json.JSONDecodeError) as e:
            self.logger.error(f"读取术语表失败，回退为空: {e}")
            return []

    def _write_to_disk(self, rows: list[dict]) -> None:
        payload = {"version": _VERSION, "rows": rows}
        # 原子写：先写临时文件再替换
        fd, tmp_path = tempfile.mkstemp(
            prefix="glossary_", suffix=".json", dir=str(self._path.parent)
        )
        try:
            with os.fdopen(fd, "w", encoding="utf-8") as f:
                json.dump(payload, f, ensure_ascii=False, indent=2)
            os.replace(tmp_path, self._path)
        except Exception:
            if os.path.exists(tmp_path):
                os.remove(tmp_path)
            raise

    def get_rows(self) -> list[dict]:
        """同步读取所有行（带进程内缓存）。"""
        if self._cache is None:
            self._cache = self._read_from_disk()
        return [dict(r) for r in self._cache]

    async def save(self, rows: list[dict]) -> list[dict]:
        """校验并原子保存全部行，返回保存后的规范化行。"""
        normalized = [_normalize_row(r) for r in rows]
        # 过滤掉三列全空的行
        normalized = [r for r in normalized if any(r[c] for c in _COLS)]
        async with self._lock:
            self._write_to_disk(normalized)
            self._cache = [dict(r) for r in normalized]
        return [dict(r) for r in normalized]

    async def reload(self) -> list[dict]:
        """强制从磁盘重新加载（调试/外部编辑后）。"""
        async with self._lock:
            self._cache = self._read_from_disk()
        return [dict(r) for r in self._cache]


def get_glossary_store() -> GlossaryStore:
    """获取全局单例。"""
    return GlossaryStore()
