from __future__ import annotations

import json
import os
import stat
import subprocess
import sys
from concurrent.futures import ThreadPoolExecutor
from pathlib import Path

import pytest

from scopewrit.errors import LedgerError
from scopewrit.ledger import FlightRecorder, verify_ledger


def test_independent_instances_and_processes_share_one_contiguous_chain(tmp_path):
    path = tmp_path / "events.jsonl"
    original = FlightRecorder(path)
    original.append("initial", {"value": 0})
    code = (
        "from scopewrit.ledger import FlightRecorder; import sys; "
        "r=FlightRecorder(sys.argv[1]); "
        '[(r.append("worker", {"worker":sys.argv[2],"index":i})) for i in range(20)]'
    )
    env = {**os.environ, "PYTHONPATH": str(Path(__file__).parents[1] / "src")}

    def worker(index):
        return subprocess.run(
            [sys.executable, "-c", code, str(path), str(index)],
            env=env,
            capture_output=True,
            text=True,
            timeout=30,
        )

    with ThreadPoolExecutor(max_workers=4) as pool:
        results = list(pool.map(worker, range(4)))
    assert all(r.returncode == 0 for r in results), [r.stderr for r in results]
    status = verify_ledger(path)
    assert status["event_count"] == 81
    assert original.event_count == 81 and original.head_sha256 == status["head_sha256"]
    rows = [json.loads(line) for line in path.read_text().splitlines()]
    assert {row["payload"]["worker"] for row in rows[1:]} == {"0", "1", "2", "3"}


def test_verification_is_read_only_and_needs_no_sidecar(tmp_path):
    path = tmp_path / "readonly.jsonl"
    FlightRecorder(path).append("snapshot", {"value": 1})
    previous = path.read_bytes()
    path.chmod(stat.S_IREAD)
    try:
        assert verify_ledger(path)["valid"]
        assert path.read_bytes() == previous
        assert {p.name for p in tmp_path.iterdir()} == {"readonly.jsonl"}
    finally:
        path.chmod(stat.S_IREAD | stat.S_IWRITE)


def test_stale_writer_detects_tampered_prefix_before_appending(tmp_path):
    path = tmp_path / "events.jsonl"
    writer = FlightRecorder(path)
    writer.append("initial", {"value": 1})
    data = path.read_text().replace('"value":1', '"value":2')
    path.write_text(data)
    with pytest.raises(LedgerError, match="digest mismatch"):
        writer.append("next", {})
    assert path.read_text() == data


@pytest.mark.parametrize("raw", ["null\n", "[]\n", '{"sequence":true}\n', "{incomplete"])
def test_malformed_ledgers_fail_as_controlled_errors(tmp_path, raw):
    path = tmp_path / "bad.jsonl"
    path.write_text(raw)
    with pytest.raises(LedgerError):
        verify_ledger(path)
