"""checkpoint_io 공통 유틸 테스트.

Phase 1.4.
"""
import json
from pathlib import Path

import pytest

from app.core.checkpoint_io import atomic_write_json, read_json_safe


def test_atomic_write_creates_parent(tmp_path: Path):
    target = tmp_path / "nested" / "dir" / "manifest.json"
    atomic_write_json(target, {"ok": True})
    assert target.exists()
    assert json.loads(target.read_text(encoding="utf-8")) == {"ok": True}


def test_atomic_write_replaces_existing(tmp_path: Path):
    target = tmp_path / "manifest.json"
    target.write_text('{"old": true}', encoding="utf-8")
    atomic_write_json(target, {"new": True})
    assert json.loads(target.read_text(encoding="utf-8")) == {"new": True}


def test_atomic_write_no_tmp_leftover(tmp_path: Path):
    target = tmp_path / "manifest.json"
    atomic_write_json(target, {"ok": True})
    # uuid suffix 형태: manifest.json.{8hex}.tmp
    leftovers = list(tmp_path.glob("manifest.json.*.tmp"))
    assert not leftovers, f"tmp leftovers: {leftovers}"


def test_atomic_write_unique_tmp_across_calls(tmp_path: Path):
    """동시 쓰기 방어: 각 호출이 다른 tmp 파일을 사용해야 함."""
    import unittest.mock as mock
    target = tmp_path / "a.json"
    captured_tmps: list = []
    real_replace = __import__("os").replace

    def spy(src, dst):
        captured_tmps.append(str(src))
        return real_replace(src, dst)

    with mock.patch("app.core.checkpoint_io.os.replace", side_effect=spy):
        atomic_write_json(target, {"n": 1})
        atomic_write_json(target, {"n": 2})

    assert len(captured_tmps) == 2
    assert captured_tmps[0] != captured_tmps[1], f"same tmp path: {captured_tmps}"


def test_atomic_write_preserves_utf8(tmp_path: Path):
    target = tmp_path / "k.json"
    atomic_write_json(target, {"이름": "김한글", "emoji": "★"})
    raw = target.read_text(encoding="utf-8")
    assert "김한글" in raw
    assert "\\u" not in raw  # ensure_ascii=False 확인


def test_read_json_safe_returns_none_for_missing(tmp_path: Path):
    assert read_json_safe(tmp_path / "does_not_exist.json") is None


def test_read_json_safe_returns_none_for_invalid(tmp_path: Path):
    broken = tmp_path / "broken.json"
    broken.write_text("{not valid}", encoding="utf-8")
    assert read_json_safe(broken) is None


def test_read_json_safe_roundtrip(tmp_path: Path):
    target = tmp_path / "rt.json"
    payload = {"a": 1, "b": ["c", "d"]}
    atomic_write_json(target, payload)
    assert read_json_safe(target) == payload
