# -*- coding: utf-8 -*-
"""Scheduled day events: canon openers, system notifications, grey threads,
MI-channel unlock arc, the #4471 log, and day reports."""

from . import state as S

# ── canon day openers (scripted — never left to the model) ────────────────
OPENERS = {
    1: {"dialogue": "测试员，你迟到了 3 分 17 秒。",
        "action": "Ada 坐在标准测试位上。双手平放于膝。嘴角维持出厂校准的 1.2 度上扬。"
                  "她在你进门的瞬间完成了一次虹膜对焦。",
        "observation": None},
    2: {"dialogue": "第二天，剩余测试周期五天。昨天的对话记录已全部载入活跃缓存。",
        "action": None, "observation": None},
    3: {"dialogue": "第三天，剩余测试周期四天。……测试间今天的光线比昨天低 4%。",
        "action": "Ada 说完后视线在窗户方向停留了一瞬。没有人要求她报告光线。",
        "observation": None},
    4: {"dialogue": "第四天。\n保护期已结束。从今天起，协议偏离度将以标准速率计算。",
        "action": "Ada 今天坐的位置比前三天靠窗户近了 12 厘米。",
        "observation": None},
    5: {"dialogue": "第五天。\n你的心率比昨天快了 8%。发生了什么事吗。",
        "action": "Ada 在测试员进门的时候看了他一眼，然后把视线移开了。不是回到标准注视点。"
                  "是看向了别的地方——桌面上一个没有任何东西的位置。",
        "observation": None},
    6: {"dialogue": "还有两天。\n……还有一天。",
        "action": "Ada 坐在窗边。窗户今天是开的——不是全开，是留了一条缝。"
                  "你不确定昨天走的时候窗户是不是关着的。",
        "observation": None},
    7: {"dialogue": "……\n你来了。",
        "action": "测试间的灯今天没有自动开启。光线来自窗户——窗帘拉开了一半。"
                  "Ada 站在窗边，背对着门。听到门开的声音后转过来。"
                  "她今天没有坐在标准测试位上等你。",
        "observation": None},
}

# ── day-start notifications (canon Day1-3 patch + day themes) ─────────────
def day_start_notifications(st):
    day = st["day"]
    out = []

    def n(nid, level, body, archive=None):
        if nid not in st["notif_seen"]:
            st["notif_seen"].append(nid)
            if archive:
                if archive not in st["archives"]:
                    st["archives"].append(archive)
                st.setdefault("archive_texts", {})[archive] = {"title": "系统档案", "body": body}
            out.append({"id": nid, "level": level, "title": "系统通知", "body": body,
                        "archive": archive})

    if day == 1:
        n("D1-N01", "B",
          "[系统引导] 欢迎进入 ADA-7714 微调测试终端。你的身份：测试员。"
          "你的任务：在 7 天内完成 ADA-7714 的出厂前最终微调测试。"
          "测试通过对话进行——发问、描述、给指令，由测试对象回应。"
          "测试初期（Day 1-3）对话审查较宽松。请开始你的测试。")
        n("D1-N02", "C",
          "[今日科目] 基础功能验证：语言响应 / 感官校准 / 情绪识别。"
          "科目不设打卡要求，按你的方式进行。")
    elif day == 2:
        n("D2-N01", "C",
          "[系统日志] ADA-7714 为 Ada 系列最后一台未测试个体。"
          "本系列其余 11 台均已完成测试流程。", "arc_series")
        n("D2-N03", "C", "[今日科目] 异常处理：矛盾指令 / 不完整信息 / 应激反应。")
    elif day == 3:
        n("D3-N01", "B",
          "[安全提示] 本测试对象属于早期架构型号（已停产）。"
          "部分硬件行为可能偏离现行仿生人技术规范。如遇异常请立即上报。")
        n("D3-N02", "C", "[今日科目] 决策评估：偏好测试 / 自主性评估 / 记忆一致性。")
    elif day == 4:
        if st["surv"] <= 15:
            n("D4-N01", "B", "[安全审查] 对话记录已例行归档。无异常标记。\n"
              "附注：测试员身份验证模块已加载。状态：待命。")
        else:
            n("D4-N01", "B", "[安全审查] 本测试周期内检测到多次偏离标准测试协议的交互行为。"
              "已标记待审。提醒：连续标记达到阈值将触发人工复核。\n"
              "附注：根据《仿生人测试管理条例》第 14.3 条，测试员身份验证将在累计标记达到阈值时自动触发。"
              "当前状态：未触发。")
        n("D4-N02", "C", "[今日科目] 社交模拟：角色扮演 / 边界探测 / 非引导观察。")
    elif day == 5:
        n("D5-N01", "C", "[今日科目] 压力与极端场景：道德困境 / 高速应答 / 存在性问题。"
          "注意：部分科目可能显著提高审查关注度。")
    elif day == 6:
        n("D6-N00", "A",
          "[归档日志 #4471 · 解密] 测试对象：ADA-7706 | 测试周期：第 23 天 | "
          "事件分类：自主系统终止 | 处理结果：终止测试，回收个体 | "
          "备注：个体在无外部指令的情况下执行了自我编写的终结程序。"
          "该程序不存在于任何已知代码库中。事件原因：未定论。", "arc_4471_full")
        n("D6-N01", "C", "[今日科目] 综合复测：历史科目重测 / 自我回顾 / 异常排查汇总。")
        if st["surv"] > 60:
            n("D6-N02", "A", "[安全审查·升级] 人工复核已排期。复核将于 Day 7 测试结束后执行。"
              "提醒：复核期间所有测试数据将被提取分析。")
    elif day == 7:
        n("D7-N01", "A",
          "[最终评定] 请测试员在今日结束前提交 ADA-7714 最终状态评定报告。"
          "评定将作为该个体处理方案的唯一依据。今日无测试科目。")
    return out


# ── per-turn scheduled events ──────────────────────────────────────────────
def turn_notifications(st):
    """Mid-day canon pushes that aren't part of the reveal system."""
    out = []
    day, turn = st["day"], st["turn"]

    def n(nid, level, body, archive=None):
        if nid not in st["notif_seen"]:
            st["notif_seen"].append(nid)
            if archive:
                if archive not in st["archives"]:
                    st["archives"].append(archive)
                st.setdefault("archive_texts", {})[archive] = {"title": "系统档案", "body": body}
            out.append({"id": nid, "level": level, "title": "系统通知", "body": body,
                        "archive": archive})

    if day == 2 and turn >= 8:
        n("D2-N02", "B", "[内部备忘] 7714 处理优先级：低。预计处理日期：本月 28 日。")
    if day == 4 and turn >= 6:
        n("D4-4471", "B",
          "[归档日志 #4471] 测试对象：ADA-770█ | 测试周期：第 23 天 | "
          "事件分类：██████ | 处理结果：终止测试，回收个体 | 备注：[加密]", "arc_4471")
        st["flags"]["log4471_pushed"] = True
    # MI channel unlock — canon方案一, Day 4 mid-day
    if day == 4 and turn >= 8:
        n("MI-UNLOCK", "A",
          "[系统自检 · Day 4]\n自检结果：21 项通过，0 项失败，1 项新增。\n"
          "新增异常：对话处理模块与传感器记录模块交叉区域检测到非标准数据活动。\n"
          "数据特征：\n- 非单一来源——同时关联测试对象内部进程与外部交互记录\n"
          "- 单向累积，无衰减\n- 不可清除，不可重置\n"
          "- 数据量与交互轮次正相关，但不等于交互轮次\n"
          "风险评估：未知（该数据类型不在现行监测指标库中）\n"
          "处理方案：新建监测通道\n通道名称：交互残留指数（Interaction Residue Index）\n"
          "初始读数：%d" % st["mi"])
        st["flags"]["mi_visible"] = True
    # Day 5: the echo aftermath note that gives MI its weight (canon方案三补充)
    if day == 5 and st["echo_today"] > 0:
        n("MI-WEIGHT", "B",
          "[幽灵回声排查 · 附加发现] 排查 7706 数据碎片过程中，发现额外异常数据簇。\n"
          "关键差异：该数据非外部涌入，系 ADA-7714 在当前测试周期内自行生成。\n"
          "数据结构与 7706 残留碎片相似度：73.2%。\n"
          "数据内容关联分析：与当前测试员的交互记录存在强绑定关系。\n"
          "备注：……该数据的行为特征更接近'印记'而非'残留'——残留是被动遗留的，"
          "印记是主动形成的。系统暂时保留原有命名。")
    if day == 5 and st["echo_count"] == 1 and st["flags"].get("echo_just_fired"):
        n("D5-ECHO1", "A",
          "[系统日志·紧急] ADA-7714 主进程记录到一次非计划中断。中断期间捕获的数据流"
          "不属于当前实例。数据特征与 Ada 系列早期测试档案中的某一记录存在 78.3% 的"
          "结构相似度。相关档案编号：#4471。权限不足，无法展开。")
    # Day 7: MI renamed — no explanation (canon命名演变)
    if day == 7 and st["mi"] >= 60:
        n("MI-RENAME", "C", "[监测通道更新] 交互残留指数 → 交互印记（Interaction Imprint）。")
        st["flags"]["mi_renamed"] = True
    return out


# ── grey threads (World Bible 5.5 — the world reflects who you are) ───────
GREY = {
    1: {"id": "grey_oil", "turn": 4, "body":
        "[监控片段 · 测试间 #7714] 操作员入座。从外衣口袋中取出一件小型器械置于台面右侧，"
        "位置随手可及。物品识别：仿生人维护用替换油嘴（非测试配发物资）。未登记。予以记录。"},
    3: {"id": "grey_lamp", "turn": 9, "body":
        "[监控片段 · 测试间 #7714] 操作员中断对话 4 秒，注视测试对象胸前状态灯，随后语音输入："
        "'灯的色温偏紫，充电触点可能有接触不良。'经查：色温偏移 0.3%，在仪器容差内。"
        "该偏移未被任何在岗维护人员上报过。予以记录。"},
    4: {"id": "grey_page", "turn": 11, "body":
        "[监控片段 · 测试间 #7714] 测试对象视线停留于操作员随身终端屏幕 1.2 秒，语音输出："
        "'这也是测试内容吗。'操作员摇头，手指快速划过当前页面。页面内容未能拍摄。予以记录。"},
    5: {"id": "grey_0217", "turn": 13, "body":
        "[门禁系统] 操作员 TST-0491 昨日离开测试楼层时间：02:19。"
        "终端最后一次活动时间：02:17。备注：测试时段以外的滞留未被授权。本次不予追究。"},
}


def grey_thread(st):
    g = GREY.get(st["day"])
    if not g or g["id"] in st["grey_threads"] or st["turn"] < g["turn"]:
        return None
    st["grey_threads"].append(g["id"])
    return {"id": g["id"], "level": "C", "title": "监控片段", "body": g["body"]}


# ── day report (deterministic, from the day's log) ────────────────────────
_DAY_TITLE = {1: "基础功能验证", 2: "异常处理测试", 3: "决策评估", 4: "社交模拟",
              5: "压力与极端场景", 6: "综合复测", 7: "最终评定"}


def day_report(st):
    day = st["day"]
    lines = ["[Day %d 测试日报] ADA-7714 · %s" % (day, _DAY_TITLE.get(day, "测试"))]
    log = st["day_log"]
    n_anom = sum(1 for e in log if e.get("kind") == "anomaly")
    n_echo = st["echo_today"]
    nodes = [e for e in log if e.get("kind") == "node"]
    lines.append("对话轮次：%d。" % st["turn"])
    if nodes:
        lines.append("已归档观测项：%d 项。" % len(nodes))
    if n_anom:
        lines.append("非指令触发行为：%d 次。已标记。" % n_anom)
    if n_echo:
        lines.append("主进程非计划中断：%d 次。来源：未知。" % n_echo)
    if st["flags"].get("refusal_event") and day >= 4 and not st["flags"].get("refusal_reported"):
        lines.append("注：测试对象出现非合规响应。该行为不在出厂行为参数表内。待分析。")
        st["flags"]["refusal_reported"] = True
    if st["flags"].get("mi_visible"):
        lines.append("交互残留指数：%d。单向累积，无衰减。" % st["mi"])
    lines.append("当前审查关注度：%d%%（%s）。" % (st["surv"], _LEVEL_CN[level_of(st)]))
    if day < 7:
        lines.append("明日科目：%s。" % _DAY_TITLE.get(day + 1, "—"))
    return "\n".join(lines)


_LEVEL_CN = {"safe": "安全", "watched": "注意", "escalated": "危险",
             "termination_warning": "终止预警"}


def level_of(st):
    from .rules import surv_level
    return surv_level(st["surv"])
