"""submit_if_changed: the shared skip-when-unchanged logic behind the CLI's
--if-changed flag and the worker scheduler's periodic ping (no network —
submit_all/url_signature are stubbed)."""
import indexnow


def _ok(total=3):
    return {"submitted": total, "total": total, "batches": 1, "statuses": [200]}


def test_skips_when_url_set_unchanged(monkeypatch, tmp_path):
    monkeypatch.setattr(indexnow, "_STATE_PATH", str(tmp_path / "state.txt"))
    monkeypatch.setattr(indexnow, "url_signature", lambda: "same-sig")
    (tmp_path / "state.txt").write_text("same-sig")

    def boom(*a, **k):
        raise AssertionError("must not submit when the URL set is unchanged")

    monkeypatch.setattr(indexnow, "submit_all", boom)
    assert indexnow.submit_if_changed("https://example.org") is None


def test_submits_and_persists_state_when_changed(monkeypatch, tmp_path):
    state = tmp_path / "state.txt"
    monkeypatch.setattr(indexnow, "_STATE_PATH", str(state))
    monkeypatch.setattr(indexnow, "url_signature", lambda: "new-sig")
    state.write_text("old-sig")

    calls = []
    monkeypatch.setattr(indexnow, "submit_all", lambda base, **kw: calls.append(base) or _ok())
    result = indexnow.submit_if_changed("https://example.org")
    assert calls == ["https://example.org"]
    assert result["submitted"] == result["total"] == 3
    assert state.read_text() == "new-sig"


def test_partial_failure_does_not_persist_state(monkeypatch, tmp_path):
    """A partial/failed submission must leave the prior state in place so the
    next attempt retries, rather than silently giving up on the missed URLs."""
    state = tmp_path / "state.txt"
    monkeypatch.setattr(indexnow, "_STATE_PATH", str(state))
    monkeypatch.setattr(indexnow, "url_signature", lambda: "new-sig")
    state.write_text("old-sig")

    monkeypatch.setattr(indexnow, "submit_all",
                        lambda base, **kw: {"submitted": 1, "total": 3, "batches": 1, "statuses": [429]})
    result = indexnow.submit_if_changed("https://example.org")
    assert result["submitted"] < result["total"]
    assert state.read_text() == "old-sig"


def test_default_base_url_falls_back_to_env(monkeypatch, tmp_path):
    monkeypatch.setattr(indexnow, "_STATE_PATH", str(tmp_path / "state.txt"))
    monkeypatch.setattr(indexnow, "url_signature", lambda: "sig")
    monkeypatch.setenv("THERMOGRAPH_BASE_URL", "https://from-env.example")
    calls = []
    monkeypatch.setattr(indexnow, "submit_all", lambda base, **kw: calls.append(base) or _ok())
    indexnow.submit_if_changed()
    assert calls == ["https://from-env.example"]


# --- submit_all / url_signature / state file, direct (ported from
# tests/web/test_content.py, repo-split Stage 4) ----------------------------

def test_indexnow_submit_all_builds_payload(monkeypatch):
    calls = []

    class _Resp:
        status_code = 200
        text = "ok"

    monkeypatch.setattr(indexnow._client, "post",
                        lambda url, json=None, **kw: (calls.append((url, json)), _Resp())[1])
    res = indexnow.submit_all("https://thermograph.org")
    assert res["submitted"] == res["total"] > 100
    first = calls[0][1]
    assert first["host"] == "thermograph.org"
    assert first["keyLocation"] == f"https://thermograph.org/{indexnow.key()}.txt"
    assert first["urlList"][0] == "https://thermograph.org/"
    assert all(len(c[1]["urlList"]) <= 10000 for c in calls)   # batched under the IndexNow cap


def test_indexnow_if_changed_state(monkeypatch, tmp_path):
    monkeypatch.setattr(indexnow, "_STATE_PATH", str(tmp_path / "state.txt"))
    sig = indexnow.url_signature()
    assert len(sig) == 40 and sig == indexnow.url_signature()   # stable sha1 of the URL set
    assert indexnow._read_state() == ""                         # first deploy → would submit
    indexnow._write_state(sig)
    assert indexnow._read_state() == sig                        # unchanged → deploy would skip


# --- key() race: atomic first-writer-wins on a cold /state volume -----------
# Same race, same fix as push.py's VAPID keypair (see tests/notifications/
# test_push.py) — several workers can hit the missing-key-file branch at once;
# only one may actually create the file, everyone else must read IT back.

def _reset_key_state(monkeypatch, path):
    monkeypatch.setattr(indexnow, "_KEY_PATH", str(path))
    monkeypatch.setattr(indexnow, "_DATA_DIR", str(path.parent))
    monkeypatch.setattr(indexnow, "_key", None)
    monkeypatch.delenv("THERMOGRAPH_INDEXNOW_KEY", raising=False)


def test_claim_file_first_writer_wins(monkeypatch, tmp_path):
    path = tmp_path / "indexnow_key.txt"
    _reset_key_state(monkeypatch, path)

    result = indexnow._claim_file("key-a")

    assert result == "key-a"
    assert path.read_text() == "key-a"
    assert list(tmp_path.iterdir()) == [path]   # no leftover temp file


def test_claim_file_reads_back_winner_on_race(monkeypatch, tmp_path):
    path = tmp_path / "indexnow_key.txt"
    _reset_key_state(monkeypatch, path)
    path.write_text("key-winner")   # simulates another process winning first

    result = indexnow._claim_file("key-loser")

    assert result == "key-winner"
    assert path.read_text() == "key-winner"   # the loser never touched the file


def test_key_caches_winner_not_its_own_generation(monkeypatch, tmp_path):
    path = tmp_path / "indexnow_key.txt"
    _reset_key_state(monkeypatch, path)

    def _lose_the_race(*a, **k):
        path.write_text("key-winner")
        return "a" * 32   # what secrets.token_hex(16) would have produced locally

    monkeypatch.setattr(indexnow.secrets, "token_hex", _lose_the_race)

    result = indexnow.key()

    assert result == "key-winner"
    assert indexnow._key == "key-winner"   # the process-wide cache matches the file
