"""Unit tests for the general capture-replay write engine (route_write): the safety walls
(disarmed / off-origin / un-captured all refuse), CSRF-from-cookie derivation, the generic
receipt parse, and the fail-open contract (every failure is a typed ok=False, never a crash,
never a false success). Network is stubbed; the live cross-site round-trip is owed on a healthy
rig (this bench's renderer command path is wedged, same as all browser live-tests)."""
from backend.apps.agents.browser import route_write as rw


def p_arm(monkeypatch):
    monkeypatch.setenv("OSW_ROUTE_WRITE", "1")


def p_reddit_route():
    return [rw.CapturedRoute(method="POST", template="https://www.reddit.com/api/comment")]


# --- safety walls -----------------------------------------------------------
def test_disarmed_by_default_refuses(monkeypatch):
    monkeypatch.delenv("OSW_ROUTE_WRITE", raising=False)
    out = rw.replay_write("POST", "https://www.reddit.com/api/comment", {"text": "hi"},
                          "https://www.reddit.com", p_reddit_route())
    assert out.ok is False and "disarmed" in out.error


def test_off_origin_target_refused(monkeypatch):
    p_arm(monkeypatch)
    out = rw.replay_write("POST", "https://evil.com/api/comment", {"text": "hi"},
                          "https://www.reddit.com", p_reddit_route())
    assert out.ok is False and "same-origin" in out.error


def test_uncaptured_route_refused(monkeypatch):
    p_arm(monkeypatch)
    # same origin, but the site's UI never fired /api/delete_account -> the agent can't invent it
    out = rw.replay_write("POST", "https://www.reddit.com/api/delete_account", {},
                          "https://www.reddit.com", p_reddit_route())
    assert out.ok is False and "captured" in out.error


def test_get_is_not_a_write_route(monkeypatch):
    p_arm(monkeypatch)
    assert rw.route_is_captured("GET", "https://www.reddit.com/api/comment", p_reddit_route()) is False


def test_template_match_ignores_volatile_ids(monkeypatch):
    # A volatile id that IS a full path segment collapses to {id} on both sides (same regex as the
    # capture), so a concrete replay URL matches the captured template but a different path doesn't.
    routes = [rw.CapturedRoute(method="DELETE", template="https://api.site.com/orders/{id}/cancel")]
    assert rw.route_is_captured("DELETE", "https://api.site.com/orders/4821/cancel", routes) is True
    assert rw.route_is_captured("DELETE", "https://api.site.com/refunds/4821/cancel", routes) is False


# --- CSRF-from-cookie derivation --------------------------------------------
def test_csrf_header_derived_from_cookie():
    h = rw.derive_csrf_headers("https://x.com/i/api/graphql/CreateTweet", "ct0=abc123; auth_token=z")
    assert h == {"x-csrf-token": "abc123"}


def test_no_csrf_for_plain_cookie_auth_site():
    assert rw.derive_csrf_headers("https://www.reddit.com/api/comment", "reddit_session=z") == {}


def test_csrf_absent_when_cookie_missing():
    assert rw.derive_csrf_headers("https://x.com/foo", "auth_token=z") == {}


# --- receipt parse ----------------------------------------------------------
def test_receipt_prefers_permalink_then_ids():
    assert rw.receipt_from_json({"json": {"data": {"permalink": "/r/x/c/1", "id": "t1_9"}}}) == "/r/x/c/1"
    assert rw.receipt_from_json({"data": {"create_tweet": {"tweet_results": {"rest_id": "1899"}}}}) == "1899"
    assert rw.receipt_from_json({"nothing": True}) == ""


def test_outcome_2xx_is_ok_with_receipt():
    out = rw.outcome_from_response(200, '{"id_str": "1899"}', 42)
    assert out.ok is True and out.receipt == "1899" and out.status == 200


def test_outcome_2xx_non_json_is_ok_generic_receipt():
    out = rw.outcome_from_response(201, "created", 5)
    assert out.ok is True and out.receipt == "ok"


def test_outcome_4xx_is_error():
    out = rw.outcome_from_response(403, "forbidden csrf", 9)
    assert out.ok is False and "403" in out.error


# --- end-to-end with the network + session stubbed --------------------------
def test_full_replay_success(monkeypatch):
    p_arm(monkeypatch)
    monkeypatch.setattr(rw, "get_session", lambda d: ("ct0=tok; sess=z", "UA/1.0"))
    seen = {}

    def fake_issue(method, url, body, headers):
        seen["method"], seen["url"], seen["body"], seen["headers"] = method, url, body, headers
        return 200, '{"json": {"data": {"things": [{"data": {"name": "t1_new", "permalink": "/r/x/c/a/_/t1_new"}}]}}}'

    monkeypatch.setattr(rw, "issue_request", fake_issue)
    out = rw.replay_write("POST", "https://www.reddit.com/api/comment", {"text": "nice", "thing_id": "t3_a"},
                          "https://www.reddit.com", p_reddit_route())
    assert out.ok is True and out.receipt == "/r/x/c/a/_/t1_new"
    assert seen["method"] == "POST" and "Cookie" in seen["headers"]
    assert "ct0=tok" in seen["headers"]["Cookie"]        # live-borrowed session, not persisted


def test_full_replay_no_session_is_typed_miss(monkeypatch):
    p_arm(monkeypatch)
    def boom(domain):
        raise RuntimeError("Not logged in")
    monkeypatch.setattr(rw, "get_session", boom)
    out = rw.replay_write("POST", "https://www.reddit.com/api/comment", {"text": "hi"},
                          "https://www.reddit.com", p_reddit_route())
    assert out.ok is False and "no borrowable session" in out.error


def test_full_replay_site_reject_is_typed_error(monkeypatch):
    p_arm(monkeypatch)
    monkeypatch.setattr(rw, "get_session", lambda d: ("sess=z", "UA/1.0"))
    monkeypatch.setattr(rw, "issue_request", lambda *a: (429, "rate limited"))
    out = rw.replay_write("POST", "https://www.reddit.com/api/comment", {"text": "hi"},
                          "https://www.reddit.com", p_reddit_route())
    assert out.ok is False and "429" in out.error
