import json
import subprocess
from pathlib import Path


ROOT = Path(__file__).parents[1]


def run_php(source: str):
    completed = subprocess.run(
        ["php", "-r", source], cwd=ROOT, text=True, capture_output=True, check=True
    )
    return json.loads(completed.stdout)


def test_amount_threshold_above_runtime_baseline_is_rejected():
    source = """
    require 'classes/CapitalLossGuard.php';
    try {
        $guard = new CapitalLossGuard([
            'enabled' => true, 'mode' => 'amount', 'threshold' => 101,
        ]);
        $guard->start(100);
        echo json_encode('accepted');
    } catch (Throwable $error) {
        echo json_encode('rejected');
    }
    """
    assert run_php(source) == "rejected"


def test_negative_floor_in_legacy_snapshot_is_rejected_instead_of_bypassing():
    source = """
    require 'classes/CapitalLossGuard.php';
    try {
        CapitalLossGuard::fromSnapshot([
            'capital_loss_guard' => [
                'enabled' => true, 'mode' => 'percent', 'threshold' => 101,
            ],
            'capital_loss_guard_baseline_equity' => 100,
            'capital_loss_guard_status' => 'armed',
            'capital_loss_guard_last_equity' => 100,
        ]);
        echo json_encode('accepted');
    } catch (Throwable $error) {
        echo json_encode('rejected');
    }
    """
    assert run_php(source) == "rejected"


def test_threshold_requires_finite_value_and_percent_cap():
    source = """
    require 'classes/CapitalLossGuard.php';
    $results = [];
    foreach ([
        ['mode' => 'percent', 'threshold' => 101],
        ['mode' => 'percent', 'threshold' => INF],
        ['mode' => 'percent', 'threshold' => NAN],
        ['mode' => 'amount', 'threshold' => INF],
        ['mode' => 'amount', 'threshold' => NAN],
    ] as $candidate) {
        try {
            new CapitalLossGuard([
                'enabled' => true,
                'mode' => $candidate['mode'],
                'threshold' => $candidate['threshold'],
            ]);
            $results[] = 'accepted';
        } catch (Throwable $error) {
            $results[] = 'rejected';
        }
    }
    echo json_encode($results);
    """
    assert run_php(source) == ["rejected"] * 5


def test_malformed_enabled_snapshot_is_rejected_instead_of_disabling_guard():
    source = """
    require 'classes/CapitalLossGuard.php';
    try {
        $guard = CapitalLossGuard::fromSnapshot([
            'capital_loss_guard' => ['mode' => 'percent', 'threshold' => 5],
            'capital_loss_guard_baseline_equity' => 100,
            'capital_loss_guard_status' => 'armed',
            'capital_loss_guard_last_equity' => 100,
        ]);
        echo json_encode($guard->observe(100)['reason']);
    } catch (Throwable $error) {
        echo json_encode('rejected');
    }
    """
    assert run_php(source) == "rejected"


def test_enabled_snapshot_requires_explicit_mode_and_threshold():
    source = """
    require 'classes/CapitalLossGuard.php';
    $results = [];
    foreach ([
        ['enabled' => true, 'threshold' => 5],
        ['enabled' => true, 'mode' => 'percent'],
    ] as $settings) {
        try {
            CapitalLossGuard::fromSnapshot([
                'capital_loss_guard' => $settings,
                'capital_loss_guard_baseline_equity' => 100,
                'capital_loss_guard_status' => 'armed',
                'capital_loss_guard_last_equity' => 100,
            ]);
            $results[] = 'accepted';
        } catch (Throwable $error) {
            $results[] = 'rejected';
        }
    }
    echo json_encode($results);
    """
    assert run_php(source) == ["rejected", "rejected"]


def test_disabled_snapshot_with_missing_runtime_fields_is_rejected():
    source = """
    require 'classes/CapitalLossGuard.php';
    try {
        CapitalLossGuard::fromSnapshot([
            'capital_loss_guard' => [
                'enabled' => false, 'mode' => 'percent', 'threshold' => 0,
            ],
            'capital_loss_guard_status' => 'disabled',
        ]);
        echo json_encode('accepted');
    } catch (Throwable $error) {
        echo json_encode('rejected');
    }
    """
    assert run_php(source) == "rejected"


def test_malformed_snapshot_settings_are_rejected_instead_of_disabling_guard():
    source = """
    require 'classes/CapitalLossGuard.php';
    $results = [];
    foreach ([
        ['enabled' => [], 'mode' => 'percent', 'threshold' => 0],
        ['enabled' => false, 'mode' => null, 'threshold' => 0],
        ['enabled' => false, 'mode' => 'percent', 'threshold' => INF],
    ] as $settings) {
        try {
            CapitalLossGuard::fromSnapshot([
                'capital_loss_guard' => $settings,
                'capital_loss_guard_baseline_equity' => null,
                'capital_loss_guard_status' => 'disabled',
                'capital_loss_guard_last_equity' => null,
            ]);
            $results[] = 'accepted';
        } catch (Throwable $error) {
            $results[] = 'rejected';
        }
    }
    echo json_encode($results);
    """
    assert run_php(source) == ["rejected"] * 3


def test_threshold_equal_to_baseline_keeps_zero_floor_equality_trigger():
    source = """
    require 'classes/CapitalLossGuard.php';
    $percent = new CapitalLossGuard([
        'enabled' => true, 'mode' => 'percent', 'threshold' => 100,
    ]);
    $percent->start(100);
    $amount = new CapitalLossGuard([
        'enabled' => true, 'mode' => 'amount', 'threshold' => 100,
    ]);
    $amount->start(100);
    echo json_encode([
        $percent->floor(), $percent->observe(0)['reason'],
        $amount->floor(), $amount->observe(0)['reason'],
    ]);
    """
    assert run_php(source) == [0, "capital_loss_limit", 0, "capital_loss_limit"]
