"""Tests for the function-sampled@v1 demo-plot script.

The script's primary contract is the canonical-bytes path (always
prints SHA-256 + preview to stdout). The PNG render is an optional
matplotlib-gated downstream view; tests skip gracefully when
matplotlib isn't installed.
"""

from __future__ import annotations

import json
import subprocess
import sys
from pathlib import Path

import pytest


sympy = pytest.importorskip("sympy")


def _run(*args: str) -> subprocess.CompletedProcess:
    return subprocess.run(
        [sys.executable, "-m", "bench.scripts.demo_plot", *args],
        capture_output=True, text=True, check=False,
    )


def test_demo_plot_emits_canonical_bytes_for_sin_x():
    r = _run("--expr", "sin(x)", "--n-samples", "10", "--dv", "0.1")
    assert r.returncode == 0, r.stderr
    out = json.loads(r.stdout)
    assert out["expr"] == "sin(x)"
    assert out["pi_star_ref"] == "function-sampled@v1"
    assert len(out["canonical_bytes_sha256"]) == 64
    assert "canonical_bytes_preview" in out
    assert out["grid"]["n_samples"] == 10


def test_demo_plot_canonical_bytes_deterministic():
    """Same expression + grid → same SHA-256."""
    r1 = _run("--expr", "x**2", "--x-min", "0", "--x-max", "4",
              "--n-samples", "5", "--dv", "1")
    r2 = _run("--expr", "x**2", "--x-min", "0", "--x-max", "4",
              "--n-samples", "5", "--dv", "1")
    assert r1.returncode == 0 and r2.returncode == 0
    out1 = json.loads(r1.stdout)
    out2 = json.loads(r2.stdout)
    assert out1["canonical_bytes_sha256"] == out2["canonical_bytes_sha256"]


def test_demo_plot_equivalent_expressions_collapse():
    """sin(x) and 2*sin(x)/2 must produce the same SHA-256 — they
    sample to identical numeric values, so canonical bytes match."""
    a = _run("--expr", "sin(x)", "--n-samples", "10", "--dv", "0.1")
    b = _run("--expr", "2*sin(x)/2", "--n-samples", "10", "--dv", "0.1")
    assert a.returncode == 0 and b.returncode == 0
    assert (
        json.loads(a.stdout)["canonical_bytes_sha256"]
        == json.loads(b.stdout)["canonical_bytes_sha256"]
    )


def test_demo_plot_invalid_expression_returns_1():
    """A genuinely unparseable expression should exit 1 with a
    PiStarError surfaced on stderr."""
    # Empty expression — sympify accepts garbage but lambdify or
    # range checks fail; use an obviously-bad input.
    r = _run("--expr", "x_min", "--x-min", "1", "--x-max", "1",
             "--n-samples", "5", "--dv", "1")
    # x_max <= x_min → PiStarError from function-sampled@v1
    assert r.returncode == 1
    assert "error" in r.stderr.lower()


def test_demo_plot_png_optional_no_crash_when_matplotlib_missing(tmp_path):
    """When matplotlib isn't installed, --png prints a warning to
    stderr and skips the PNG; canonical bytes still print."""
    png_path = tmp_path / "out.png"
    r = _run("--expr", "x", "--n-samples", "5", "--dv", "1",
             "--png", str(png_path))
    assert r.returncode == 0, r.stderr
    out = json.loads(r.stdout)
    assert "canonical_bytes_sha256" in out
    # matplotlib may or may not be installed in this env; the
    # script's contract is "no crash either way."


def test_demo_plot_png_renders_when_matplotlib_present(tmp_path):
    matplotlib = pytest.importorskip("matplotlib")
    png_path = tmp_path / "out.png"
    r = _run("--expr", "sin(x)", "--n-samples", "20", "--dv", "0.1",
             "--png", str(png_path))
    assert r.returncode == 0, r.stderr
    assert png_path.is_file(), "matplotlib was importable; PNG must land"
    # Sanity: the file isn't empty.
    assert png_path.stat().st_size > 100
