"""从线上 ECDICT 词库下载并导入 MySQL。

数据源: https://github.com/skywind3000/ECDICT （开源英汉词典，约 340 万词条）

用法（在 backend 目录下）:
  python -m scripts.import_dictionary_online
  python -m scripts.import_dictionary_online --preset full
  python -m scripts.import_dictionary_online --preset standard --skip-download
"""

from __future__ import annotations

import argparse
import sqlite3
import sys
import zipfile
from pathlib import Path
from typing import Optional
from urllib.request import urlretrieve

BACKEND_DIR = Path(__file__).resolve().parent.parent
CACHE_DIR = BACKEND_DIR / "data" / "cache"
ECDICT_ZIP_URL = (
    "https://github.com/skywind3000/ECDICT/releases/download/1.0.28/ecdict-sqlite-28.zip"
)
ECDICT_ZIP_PATH = CACHE_DIR / "ecdict-sqlite-28.zip"
ECDICT_DB_PATH = CACHE_DIR / "stardict.db"

sys.path.insert(0, str(BACKEND_DIR))

from database import SessionLocal  # noqa: E402
from scripts.dictionary_importer import upsert_entries  # noqa: E402

PRESET_SQL = {
    # 常用词：Collins / 牛津核心词，或词频排名前 5 万
    "standard": """
        translation IS NOT NULL AND translation != ''
        AND (
            collins >= 1 OR oxford = 1
            OR (frq IS NOT NULL AND frq <= 50000)
        )
    """,
    # 核心词：Collins 星级或牛津 3000
    "core": """
        translation IS NOT NULL AND translation != ''
        AND (collins >= 1 OR oxford = 1)
    """,
    # 全量（约 338 万，耗时较长）
    "full": """
        translation IS NOT NULL AND translation != ''
    """,
}


def download(url: str, dest: Path) -> None:
    dest.parent.mkdir(parents=True, exist_ok=True)
    print(f"正在下载 ECDICT SQLite（约 207MB）…")
    print(f"  {url}")

    def progress(block_num: int, block_size: int, total_size: int) -> None:
        if total_size <= 0:
            return
        done = block_num * block_size
        pct = min(100, done * 100 // total_size)
        mb = done / 1024 / 1024
        total_mb = total_size / 1024 / 1024
        print(f"\r  进度: {pct:3d}% ({mb:.1f}/{total_mb:.1f} MB)", end="", flush=True)

    urlretrieve(url, dest, reporthook=progress)
    print()


def ensure_ecdict_db(skip_download: bool) -> Path:
    if ECDICT_DB_PATH.exists():
        return ECDICT_DB_PATH

    if not ECDICT_ZIP_PATH.exists():
        if skip_download:
            print(f"错误: 未找到本地词库，请先下载或去掉 --skip-download", file=sys.stderr)
            sys.exit(1)
        download(ECDICT_ZIP_URL, ECDICT_ZIP_PATH)

    print(f"正在解压 {ECDICT_ZIP_PATH.name} …")
    CACHE_DIR.mkdir(parents=True, exist_ok=True)
    with zipfile.ZipFile(ECDICT_ZIP_PATH, "r") as zf:
        zf.extract("stardict.db", CACHE_DIR)
    print(f"词库就绪: {ECDICT_DB_PATH}")
    return ECDICT_DB_PATH


def count_entries(db_path: Path, where: str) -> int:
    conn = sqlite3.connect(db_path)
    try:
        cur = conn.execute(f"SELECT COUNT(*) FROM stardict WHERE {where}")
        return int(cur.fetchone()[0])
    finally:
        conn.close()


def iter_ecdict_rows(db_path: Path, where: str, limit: Optional[int]):
    conn = sqlite3.connect(db_path)
    conn.row_factory = sqlite3.Row
    try:
        sql = f"""
            SELECT word, phonetic, translation
            FROM stardict
            WHERE {where}
            ORDER BY COALESCE(frq, 9999999), word
        """
        if limit:
            sql += f" LIMIT {int(limit)}"
        cur = conn.execute(sql)
        while True:
            rows = cur.fetchmany(5000)
            if not rows:
                break
            for row in rows:
                yield {
                    "word": row["word"],
                    "phonetic": row["phonetic"],
                    "translation": row["translation"],
                }
    finally:
        conn.close()


def import_from_ecdict(
    db_path: Path,
    preset: str,
    limit: Optional[int],
    batch_size: int,
) -> int:
    where = PRESET_SQL[preset]
    total_expected = count_entries(db_path, where)
    if limit:
        total_expected = min(total_expected, limit)
    print(f"预设: {preset}，预计导入 {total_expected:,} 条")

    session = SessionLocal()
    imported = 0
    buffer: list[dict] = []
    try:
        for row in iter_ecdict_rows(db_path, where, limit):
            buffer.append(row)
            if len(buffer) >= batch_size:
                imported += upsert_entries(session, buffer, source="ecdict", batch_size=batch_size)
                buffer.clear()
                print(f"\r  已导入 {imported:,} / {total_expected:,}", end="", flush=True)
        if buffer:
            imported += upsert_entries(session, buffer, source="ecdict", batch_size=batch_size)
        print()
    except Exception:
        session.rollback()
        raise
    finally:
        session.close()
    return imported


def main() -> None:
    parser = argparse.ArgumentParser(description="从线上 ECDICT 下载并导入离线翻译词典")
    parser.add_argument(
        "--preset",
        choices=list(PRESET_SQL.keys()),
        default="standard",
        help="standard=常用约 83 万 | core=核心约 1.4 万 | full=全量约 338 万（默认 standard）",
    )
    parser.add_argument("--limit", type=int, default=None, help="最多导入条数（调试用）")
    parser.add_argument("--batch-size", type=int, default=2000, help="MySQL 批量写入大小")
    parser.add_argument("--skip-download", action="store_true", help="跳过下载，使用本地 cache")
    args = parser.parse_args()

    db_path = ensure_ecdict_db(args.skip_download)
    imported = import_from_ecdict(db_path, args.preset, args.limit, args.batch_size)
    print(f"导入完成: {imported:,} 条（source=ecdict, preset={args.preset}）")


if __name__ == "__main__":
    main()
