"""script_run 临时脚本命名与执行后归档到工作区 ``aicd/bak/``。"""

from __future__ import annotations

import re
import shutil
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Callable


def policy_allow_under_ai_output_or_workspace(
    ai_output: Path,
    workspace_policy: Callable[[Path], None],
) -> Callable[[Path], None]:
    """
    归档目标在 ``ai_output`` 目录树下时直接允许（用于 ``aicd/bak``，子 Agent 工作区在别处也能写入父级 aicd）。
    否则回退到常规 workspace 策略。
    """

    root = ai_output.expanduser().resolve()

    def _check(p: Path) -> None:
        r = p.expanduser().resolve()
        try:
            r.relative_to(root)
            return
        except ValueError:
            pass
        workspace_policy(r)

    return _check


def sanitize_doc_label(raw: str | None, max_len: int = 56) -> str:
    """文件名安全片段：路径分隔符与非法字符转为下划线。"""
    if raw is None or not str(raw).strip():
        return "adhoc"
    s = str(raw).strip().replace("\\", "_").replace("/", "_")
    s = re.sub(r"[:*?\"<>|]+", "_", s)
    s = "".join(c if c.isalnum() or c in "._-" else "_" for c in s)
    parts = [x for x in s.split("_") if x]
    s = "_".join(parts) if parts else "adhoc"
    s = s[:max_len].strip("._-") or "adhoc"
    return s


def format_script_run_filename(
    ts: datetime,
    kind: str,
    ext: str,
    related_doc: str | None,
    unique_suffix: str,
) -> str:
    """
    ``{YYYYMMDDTHHMMSS}Z_{kind}_{doc}_{uniq}{ext}``

    ``ts`` 应为 UTC；``unique_suffix`` 建议 8 位 hex 避免同秒碰撞。
    """
    k = re.sub(r"[^a-z0-9]+", "", str(kind).lower()) or "run"
    if not ext.startswith("."):
        ext = "." + ext
    doc = sanitize_doc_label(related_doc)
    u = re.sub(r"[^a-z0-9]+", "", str(unique_suffix).lower())[:12] or "x"
    stamp = ts.astimezone(timezone.utc).strftime("%Y%m%dT%H%M%S")
    return f"{stamp}Z_{k}_{doc}_{u}{ext}"


def archive_script_to_bak(
    script_path: Path,
    backup_root: Path,
    policy_check: Callable[[Path], None],
) -> dict[str, Any]:
    """
    将已执行脚本复制到 ``backup_root``（通常为 ``<workspace>/aicd/bak``），再删除临时文件。

    复制失败时保留临时文件，并在返回中给出 ``script_backup_error``。
    """
    script_path = script_path.resolve()
    backup_root = backup_root.expanduser().resolve()
    backup_root.mkdir(parents=True, exist_ok=True)
    dest = (backup_root / script_path.name).resolve()
    policy_check(dest)
    try:
        shutil.copy2(script_path, dest)
    except OSError as e:
        return {
            "script_bak_path": None,
            "script_backup_ok": False,
            "script_backup_error": str(e),
            "script_temp_kept": str(script_path),
        }
    try:
        script_path.unlink(missing_ok=True)
    except OSError as e:
        return {
            "script_bak_path": str(dest),
            "script_backup_ok": True,
            "script_temp_unlink_warning": str(e),
        }
    return {"script_bak_path": str(dest), "script_backup_ok": True}
