"""维护调度器 — 独立进程入口

由 reflect() Hook 通过 subprocess.Popen 启动，
start_new_session=True 脱离父进程，Claude Code 退出后继续运行。

用法:
    python -m index1.cognitive.scheduler \
        --collection project-name \
        --session-id abc123 \
        --db-path /path/to/cognitive.db
"""

from __future__ import annotations

import argparse
import asyncio
import fcntl
import logging
import sys
from pathlib import Path

logger = logging.getLogger(__name__)


def _setup_logging(log_dir: Path) -> None:
    """文件日志（不污染 stdout/stderr）"""
    try:
        log_dir.mkdir(parents=True, exist_ok=True)
        log_file = log_dir / "maintenance.log"
        handler = logging.FileHandler(log_file, encoding="utf-8")
        handler.setFormatter(logging.Formatter(
            "%(asctime)s %(levelname)s %(name)s: %(message)s",
        ))
        logging.root.addHandler(handler)
        logging.root.setLevel(logging.INFO)
    except OSError:
        pass


async def _run_maintenance(
    collection: str,
    session_id: str | None,
    db_path: str,
) -> None:
    """执行维护流程"""
    from ..config import INDEX1_HOME, load_config
    from ..embed import create_embedder
    from .db_cog import CogDatabase
    from .maintenance import MaintenanceEngine

    config = load_config()

    # 使用传入的 db_path，而非 config 默认值
    resolved_path = db_path if db_path else str(config.cognitive_db_path_resolved)

    db_cog = CogDatabase(resolved_path, config.effective_embedding_dim)
    try:
        db_cog.connect()

        # Observer — LLM 回顾 session（在 maintenance 之前，产出的 facts 可被维护处理）
        if session_id:
            try:
                from .observer import create_observer
                observer = create_observer(db_cog, config)
                if observer is not None:
                    session_facts = db_cog.get_session_facts(session_id, collection)
                    if session_facts:
                        await observer.observe(session_facts, collection, session_id)
            except Exception as e:
                logger.warning("Observer 失败: %s", e)

        # 创建 embedder 并检测可用性
        embedder = create_embedder(config)
        try:
            await embedder.check_availability()
        except Exception:
            pass  # 不可用时 embedder.available = False
        logger.info(
            "Embedder: backend=%s, available=%s, dim=%d",
            config.embed_backend,
            getattr(embedder, "available", "unknown"),
            config.effective_embedding_dim,
        )

        engine = MaintenanceEngine(db_cog, config, embedder)
        results = await engine.run(collection, session_id)
        logger.info("维护结果: %s", results)

        try:
            await embedder.close()
        except Exception:
            pass
    finally:
        db_cog.close()


def main() -> None:
    """CLI 入口"""
    parser = argparse.ArgumentParser(description="index1 认知维护调度器")
    parser.add_argument("--collection", required=True, help="项目集合名")
    parser.add_argument("--session-id", default="", help="会话 ID")
    parser.add_argument("--db-path", default="", help="认知数据库路径")
    args = parser.parse_args()

    # 文件日志
    from ..config import INDEX1_HOME
    _setup_logging(INDEX1_HOME)

    lock_fd = None
    # ── RC3 修复：flock 进程互斥 ──
    # 防止多个 maintenance 进程并发执行（SessionEnd 可能快速连续触发）
    lock_file = INDEX1_HOME / "maintenance.lock"
    try:
        lock_file.parent.mkdir(parents=True, exist_ok=True)
        lock_fd = open(lock_file, "w")  # noqa: SIM115
        try:
            fcntl.flock(lock_fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
        except OSError:
            logger.info("另一个 maintenance 进程正在运行，跳过")
            lock_fd.close()
            return
    except OSError as e:
        logger.warning("无法创建 lock 文件: %s，继续执行", e)

    session_id = args.session_id or None

    # 2.5 分钟总超时（正常 <60s，超过说明 Ollama 卡死）
    try:
        asyncio.run(asyncio.wait_for(
            _run_maintenance(args.collection, session_id, args.db_path),
            timeout=150,
        ))
    except asyncio.TimeoutError:
        logger.error("维护超时 (150s)，强制退出")
        # 尝试 WAL checkpoint，确保已完成的事务持久化
        try:
            import sqlite3
            db_path = args.db_path
            if db_path:
                conn = sqlite3.connect(db_path, timeout=3.0)
                conn.execute("PRAGMA wal_checkpoint(TRUNCATE)")
                conn.close()
        except Exception:
            pass
    except Exception as e:
        logger.error("维护进程异常: %s", e, exc_info=True)
    finally:
        # 释放 flock
        if lock_fd is not None:
            try:
                fcntl.flock(lock_fd, fcntl.LOCK_UN)
                lock_fd.close()
            except OSError:
                pass


if __name__ == "__main__":
    main()
