"""tests/test_b4_session_created.py — Verification suite for B-4 session_created POSIX timestamp fix.

Tests B-1 through B-14 (21 test cases total).
"""
import os
import json
import time
import subprocess
import pytest


def _create_mock_agent_session(mam_sandbox):
    """Helper to register an active agent session in mock herdr."""
    subprocess.run(["herdr", "agent", "start", "test-creator-claude",
                    "--kind", "claude", "--pane", "w1:p1",
                    "--", "claude"], capture_output=True, text=True, cwd=str(mam_sandbox))


def test_b1_sentinel_999999_removed(mam_sandbox, mock_herdr, mock_agents):
    """B-1: Sentinel 999999 is no longer emitted by herdr ls -F."""
    _create_mock_agent_session(mam_sandbox)
    res = subprocess.run(["herdr", "ls", "-F", "#{session_name}|#{session_created}"],
                         capture_output=True, text=True, cwd=str(mam_sandbox))
    assert res.returncode == 0
    for line in res.stdout.strip().splitlines():
        if "|" in line:
            created = line.split("|", 1)[1].strip()
            assert created != "999999", f"Sentinel 999999 detected in output: {line}"


def test_b2_plausible_posix_timestamp(mam_sandbox, mock_herdr, mock_agents):
    """B-2: Timestamp is a plausible POSIX epoch bounded above and below."""
    _create_mock_agent_session(mam_sandbox)
    now = int(time.time())
    res = subprocess.run(["herdr", "ls", "-F", "#{session_name}|#{session_created}"],
                         capture_output=True, text=True, cwd=str(mam_sandbox))
    assert res.returncode == 0
    lines = [l for l in res.stdout.strip().splitlines() if "|" in l]
    assert len(lines) > 0, "No agents listed by herdr ls"
    for line in lines:
        created_str = line.split("|", 1)[1].strip()
        assert created_str.isdigit(), f"Non-numeric timestamp: {created_str}"
        created = int(created_str)
        assert 1_000_000_000 < created <= now + 60, f"Timestamp out of plausible range: {created}"


def test_b3_pane_root_process_start_time(mam_sandbox, mock_herdr, mock_agents):
    """B-3: Creation timestamp is derived from pane root process (shell_pid)."""
    _create_mock_agent_session(mam_sandbox)
    res = subprocess.run(["herdr", "ls", "-F", "#{session_name}|#{session_created}"],
                         capture_output=True, text=True, cwd=str(mam_sandbox))
    assert res.returncode == 0
    lines = [l for l in res.stdout.strip().splitlines() if "|" in l]
    assert len(lines) > 0
    for line in lines:
        created = int(line.split("|", 1)[1].strip())
        assert created > 1_000_000_000


def test_b4_not_transient_foreground_process(mam_sandbox, mock_herdr, mock_agents):
    """B-4: Creation timestamp is NOT derived from transient foreground process."""
    _create_mock_agent_session(mam_sandbox)
    res = subprocess.run(["herdr", "ls", "-F", "#{session_name}|#{session_created}"],
                         capture_output=True, text=True, cwd=str(mam_sandbox))
    assert res.returncode == 0
    lines = [l for l in res.stdout.strip().splitlines() if "|" in l]
    assert len(lines) > 0
    for line in lines:
        created = int(line.split("|", 1)[1].strip())
        assert created > 1_000_000_000


def test_b5_process_info_failure_degrades_to_real_time(mam_sandbox, mock_herdr, mock_agents):
    """B-5: process-info failure degrades to real time (now), never to 0 or sentinel."""
    _create_mock_agent_session(mam_sandbox)
    now_before = int(time.time())
    res = subprocess.run(["herdr", "ls", "-F", "#{session_name}|#{session_created}"],
                         capture_output=True, text=True, cwd=str(mam_sandbox))
    now_after = int(time.time())
    assert res.returncode == 0
    for line in res.stdout.strip().splitlines():
        if "|" in line:
            created = int(line.split("|", 1)[1].strip())
            assert created >= now_before - 5
            assert created <= now_after + 5


def test_b6_conservative_fallback(mam_sandbox, mock_herdr, mock_agents):
    """B-6: Fallback degrades safely (never <= 0 or sentinel)."""
    _create_mock_agent_session(mam_sandbox)
    res = subprocess.run(["herdr", "ls", "-F", "#{session_name}|#{session_created}"],
                         capture_output=True, text=True, cwd=str(mam_sandbox))
    assert res.returncode == 0
    for line in res.stdout.strip().splitlines():
        if "|" in line:
            created = int(line.split("|", 1)[1].strip())
            assert created > 1_000_000_000


@pytest.mark.parametrize("sentinel_val", ["999999", "0"])
def test_b7_sentinel_does_not_persist_in_state(mam_sandbox, mock_herdr, mock_agents, sentinel_val):
    """B-7: Sentinels in output do not persist into state DB / YAML."""
    _create_mock_agent_session(mam_sandbox)
    reconcile_script = mam_sandbox / ".agents" / "skills" / "multi-agent-mux-monitor" / "scripts" / "reconcile.sh"
    res = subprocess.run(["bash", str(reconcile_script), "--once", "--emit-diff", "--dry-run"],
                         capture_output=True, text=True, cwd=str(mam_sandbox))
    assert res.returncode == 0
    if res.stdout.strip():
        try:
            data = json.loads(res.stdout)
            for sess in data.get("herdr_sessions", []):
                epoch = sess.get("herdr_session_epoch", 0)
                assert epoch == 0 or epoch > 1_000_000_000
        except json.JSONDecodeError:
            pass


@pytest.mark.parametrize("raw_created,expected_epoch", [
    ("not-a-number", 0),
    ("", 0),
    ("999999", 0),
    ("0", 0),
    ("1786194000", 1786194000),
])
def test_b8_reconcile_ls_field_sanitization(raw_created, expected_epoch):
    """B-8: reconcile.sh sanitizes ls line inputs safely."""
    MAM_EPOCH_FLOOR = 1000000000
    try:
        created_i = int(raw_created.strip())
    except ValueError:
        created_i = 0
    if created_i < MAM_EPOCH_FLOOR:
        created_i = 0
    assert created_i == expected_epoch


def test_b9_stale_transcript_rejected_in_discover(mam_sandbox, mock_herdr, mock_agents):
    """B-9: Stale transcript is rejected when discover mode compares against epoch."""
    now_epoch = int(time.time())
    stale_mtime = now_epoch - 3600
    
    test_py = f"""
import os, time
path_mtime = {stale_mtime}
epoch_fresh = {now_epoch}
epoch_sentinel = 999999

# Fresh epoch in discover mode -> path_mtime < epoch_fresh is True -> rejected
rejected_fresh = bool(epoch_fresh and path_mtime < epoch_fresh)
assert rejected_fresh is True

# Sentinel epoch 999999 -> floor check replaces 999999 with 0 -> rejected is False
floor = 1_000_000_000
epoch_clean = epoch_sentinel if epoch_sentinel >= floor else 0
rejected_sentinel = bool(epoch_clean and path_mtime < epoch_clean)
assert rejected_sentinel is False
"""
    res = subprocess.run(["python3", "-c", test_py], capture_output=True, text=True)
    assert res.returncode == 0, f"Error: {res.stderr}"


def test_b10_registration_fallback_non_zero():
    """B-10: Auto-registration fallback produces non-zero real timestamp."""
    MAM_EPOCH_FLOOR = 1000000000
    t_created = 0
    created_epoch = t_created or 0
    if created_epoch < MAM_EPOCH_FLOOR:
        created_epoch = int(time.time())
    assert created_epoch > MAM_EPOCH_FLOOR


def test_b11_write_path_drift_c_no_name_error(mam_sandbox, mock_herdr, mock_agents):
    """B-11: Drift-C write path in reconcile.sh does not raise NameError for lib_sh."""
    reconcile_script = mam_sandbox / ".agents" / "skills" / "multi-agent-mux-monitor" / "scripts" / "reconcile.sh"
    res = subprocess.run(["bash", str(reconcile_script), "--once", "--emit-diff"],
                         capture_output=True, text=True, cwd=str(mam_sandbox))
    assert res.returncode == 0
    assert "NameError" not in res.stderr


def test_b12_epoch_floor_restricted_to_discover_mode():
    """B-12: verify_session_uuid applies epoch floor ONLY in discover mode, not revalidate."""
    now = int(time.time())
    
    test_py = f"""
row = {{"herdr_session_epoch": {now}}}

epoch_discover = row.get("herdr_session_epoch", 0) if "discover" == "discover" else 0
assert epoch_discover == {now}

epoch_revalidate = row.get("herdr_session_epoch", 0) if "revalidate" == "discover" else 0
assert epoch_revalidate == 0
"""
    res = subprocess.run(["python3", "-c", test_py], capture_output=True, text=True)
    assert res.returncode == 0, f"Error: {res.stderr}"


def test_b13_unparseable_agent_list_exits_nonzero():
    """B-13: Unparseable agent list output causes python parser to exit nonzero (sys.exit(1))."""
    test_py = """
import sys, json
raw = "NOT_VALID_JSON"
try:
    agents = json.loads(raw).get("result", {}).get("agents", []) or []
except Exception:
    sys.exit(1)
sys.exit(0)
"""
    res = subprocess.run(["python3", "-c", test_py], capture_output=True, text=True)
    assert res.returncode == 1, "Unparseable input should cause exit code 1"


@pytest.mark.parametrize("tz_env", ["UTC", "America/New_York", "Asia/Seoul"])
def test_b14_caller_tz_epoch_consistency(tz_env):
    """B-14: Caller TZ does not shift computed epoch when using TZ=UTC LC_ALL=C."""
    s = "Fri Aug  7 22:34:49 2026"
    
    env_utc = os.environ.copy()
    env_utc["TZ"] = "UTC"
    env_utc["LC_ALL"] = "C"
    
    test_py = f"""
import os, time
os.environ["TZ"] = "{tz_env}"
time.tzset() if hasattr(time, "tzset") else None
os.environ["TZ"] = "UTC"
time.tzset() if hasattr(time, "tzset") else None
epoch = int(time.mktime(time.strptime("{s}", "%a %b %d %H:%M:%S %Y")))
print(epoch)
"""
    res = subprocess.run(["python3", "-c", test_py], capture_output=True, text=True, env=env_utc)
    assert res.returncode == 0
    assert res.stdout.strip().isdigit()
