"""v3.6 Runtime Harness — Layer 0: Task MD SHA Decision Marker Writer.

chair_authorization_id=CHAIR-AUTH-TASK-2705PLUS1-V36-TASK-MD-SHA-BOOTSTRAP-260528

Writes a 12-field decision marker JSON to
``memory/events/<task_id>.task-md-sha-decision.json`` per spec §7.2.

Safe-fail: every public function returns None on failure. Never raises.
"""
from __future__ import annotations

import json
import os
from datetime import datetime, timedelta, timezone
from typing import Any, Dict, Optional

SCHEMA_VERSION = "v36.task_md_sha_decision.v1"
_DEFAULT_EVENTS_DIR = "/home/jay/workspace/memory/events"
_KST = timezone(timedelta(hours=9))

_REQUIRED_FIELDS = frozenset({
    "schema_version",
    "decision_id",
    "task_id",
    "ts",
    "shas",
    "sizes",
    "mismatch_location",
    "patch_type",
    "content_verbatim_match",
    "continue_allowed",
    "decision_class",
    "reason_code",
    "chair_authorization_id",
    "actor",
})


def write_task_md_sha_marker(
    task_id: str,
    dispatch_pre_sha: Optional[str],
    dispatch_post_sha: Optional[str],
    executor_observed_sha: Optional[str],
    classification: Optional[Dict[str, Any]],
    chair_authorization_id: Optional[str],
    pre_size: Optional[int] = None,
    post_size: Optional[int] = None,
    observed_size: Optional[int] = None,
    actor_persona: Optional[str] = None,
    actor_model: Optional[str] = None,
    actor_role: Optional[str] = None,
    events_dir: Optional[str] = None,
) -> Optional[str]:
    """Write a task md sha decision marker. Returns the absolute marker path or None."""
    try:
        return _write_impl(
            task_id=task_id,
            dispatch_pre_sha=dispatch_pre_sha,
            dispatch_post_sha=dispatch_post_sha,
            executor_observed_sha=executor_observed_sha,
            classification=classification or {},
            chair_authorization_id=chair_authorization_id,
            pre_size=pre_size,
            post_size=post_size,
            observed_size=observed_size,
            actor_persona=actor_persona,
            actor_model=actor_model,
            actor_role=actor_role,
            events_dir=events_dir,
        )
    except Exception:
        return None


def _write_impl(
    task_id: str,
    dispatch_pre_sha: Optional[str],
    dispatch_post_sha: Optional[str],
    executor_observed_sha: Optional[str],
    classification: Dict[str, Any],
    chair_authorization_id: Optional[str],
    pre_size: Optional[int],
    post_size: Optional[int],
    observed_size: Optional[int],
    actor_persona: Optional[str],
    actor_model: Optional[str],
    actor_role: Optional[str],
    events_dir: Optional[str],
) -> Optional[str]:
    now_kst = datetime.now(_KST)
    ts_iso = now_kst.isoformat(timespec="seconds")
    ts_for_id = now_kst.strftime("%y%m%d-%H%M")

    marker = {
        "schema_version": SCHEMA_VERSION,
        "decision_id": f"{task_id}.task-md-sha.{ts_for_id}",
        "task_id": task_id,
        "ts": ts_iso,
        "shas": {
            "dispatch_pre_sha": dispatch_pre_sha,
            "dispatch_post_sha": dispatch_post_sha,
            "executor_observed_sha": executor_observed_sha,
        },
        "sizes": {
            "dispatch_pre_size": pre_size,
            "dispatch_post_size": post_size,
            "executor_observed_size": observed_size,
        },
        "mismatch_location": classification.get("mismatch_location", "UNKNOWN"),
        "patch_type": classification.get("patch_type", "UNKNOWN"),
        "content_verbatim_match": classification.get("content_verbatim_match", "unverifiable"),
        "continue_allowed": classification.get("continue_allowed", "hold"),
        "decision_class": classification.get("decision_class", "HOLD_FOR_CHAIR"),
        "reason_code": classification.get("reason_code", "uncovered_state_hold"),
        "chair_authorization_id": chair_authorization_id,
        "actor": {
            "persona": actor_persona,
            "model": actor_model,
            "role": actor_role,
        },
    }

    missing = _REQUIRED_FIELDS - set(marker.keys())
    if missing:
        return None

    dir_path = events_dir or _DEFAULT_EVENTS_DIR
    os.makedirs(dir_path, exist_ok=True)
    filename = f"{task_id}.task-md-sha-decision.json"
    marker_path = os.path.join(dir_path, filename)
    if os.path.exists(marker_path):
        ts_us = now_kst.strftime("%Y%m%d%H%M%S%f")
        filename = f"{task_id}.task-md-sha-decision.{ts_us}.json"
        marker_path = os.path.join(dir_path, filename)

    tmp_path = marker_path + ".tmp"
    try:
        with open(tmp_path, "w", encoding="utf-8") as fh:
            json.dump(marker, fh, ensure_ascii=False, indent=2)
        os.replace(tmp_path, marker_path)
    finally:
        if os.path.exists(tmp_path):
            try:
                os.unlink(tmp_path)
            except Exception:
                pass

    try:
        from scripts.harness.v36.runtime_decision_logger import log_runtime_event
        log_runtime_event(
            event_type="task_md_sha_decision_written",
            task_id=task_id,
            payload={
                "marker_path": marker_path,
                "decision_class": marker["decision_class"],
                "patch_type": marker["patch_type"],
                "content_verbatim_match": marker["content_verbatim_match"],
            },
        )
    except Exception:
        pass

    return marker_path
