#!/usr/bin/env python3
"""Episode source 级重建工具。"""

from __future__ import annotations

import argparse
import asyncio
import sys
from typing import Any, Dict, List

from _bootstrap import DEFAULT_CONFIG_PATH, DEFAULT_DATA_DIR, resolve_repo_path

try:
    import tomlkit  # type: ignore
except Exception:  # pragma: no cover
    tomlkit = None

from A_memorix.core.storage import MetadataStore
from A_memorix.core.utils.episode_service import EpisodeService


def _build_arg_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(description="Rebuild A_Memorix episodes by source")
    parser.add_argument("--data-dir", default=str(DEFAULT_DATA_DIR), help="插件数据目录")
    parser.add_argument("--source", type=str, help="指定单个 source 入队/重建")
    parser.add_argument("--all", action="store_true", help="对所有 source 入队/重建")
    parser.add_argument("--wait", action="store_true", help="在脚本内同步执行重建")
    return parser


if any(arg in {"-h", "--help"} for arg in sys.argv[1:]):
    _build_arg_parser().print_help()
    raise SystemExit(0)


def _load_plugin_config() -> Dict[str, Any]:
    config_path = DEFAULT_CONFIG_PATH
    if tomlkit is None or not config_path.exists():
        return {}
    try:
        with open(config_path, "r", encoding="utf-8") as handle:
            parsed = tomlkit.load(handle)
        return dict(parsed) if isinstance(parsed, dict) else {}
    except Exception:
        return {}


def _resolve_sources(store: MetadataStore, *, source: str | None, rebuild_all: bool) -> List[str]:
    if rebuild_all:
        return list(store.list_episode_sources_for_rebuild())
    token = str(source or "").strip()
    if not token:
        raise ValueError("必须提供 --source 或 --all")
    return [token]


async def _run_rebuilds(store: MetadataStore, plugin_config: Dict[str, Any], sources: List[str]) -> int:
    service = EpisodeService(metadata_store=store, plugin_config=plugin_config)
    failures: List[str] = []
    for source in sources:
        started = store.mark_episode_source_running(source)
        if not started:
            failures.append(f"{source}: unable_to_mark_running")
            continue
        try:
            result = await service.rebuild_source(source)
            store.mark_episode_source_done(source)
            print(
                "rebuilt"
                f" source={source}"
                f" paragraphs={int(result.get('paragraph_count') or 0)}"
                f" groups={int(result.get('group_count') or 0)}"
                f" episodes={int(result.get('episode_count') or 0)}"
                f" fallback={int(result.get('fallback_count') or 0)}"
            )
        except Exception as exc:
            err = str(exc)[:500]
            store.mark_episode_source_failed(source, err)
            failures.append(f"{source}: {err}")
            print(f"failed source={source} error={err}")

    if failures:
        for item in failures:
            print(item)
        return 1
    return 0


def main() -> int:
    parser = _build_arg_parser()
    args = parser.parse_args()
    if bool(args.all) == bool(args.source):
        parser.error("必须且只能选择一个：--source 或 --all")

    store = MetadataStore(data_dir=resolve_repo_path(args.data_dir, fallback=DEFAULT_DATA_DIR) / "metadata")
    store.connect()
    try:
        sources = _resolve_sources(store, source=args.source, rebuild_all=bool(args.all))
        if not sources:
            print("no sources to rebuild")
            return 0

        enqueued = 0
        reason = "script_rebuild_all" if args.all else "script_rebuild_source"
        for source in sources:
            enqueued += int(store.enqueue_episode_source_rebuild(source, reason=reason))
        print(f"enqueued={enqueued} sources={len(sources)}")

        if not args.wait:
            return 0

        plugin_config = _load_plugin_config()
        return asyncio.run(_run_rebuilds(store, plugin_config, sources))
    finally:
        store.close()


if __name__ == "__main__":
    raise SystemExit(main())
