import json

import numpy as np
import pandas as pd
import pytest


def test_identical_paths_pass_despite_raw_proximity():
    from strategies.mlp_execution_stability import assess_stability
    from strategies.strategy_mlp_scores import _score_to_signals
    frame = pd.DataFrame(dict(time=pd.date_range('2020-01-01', periods=5, freq='6h'),
        activation_score=[200., 50., -260.01, 220., 40.],
        close=[100., 101., 102., 101., 104.], high=[100., 101., 102., 101., 104.]))
    params = dict(i_long_entry_activation_threshold=90., i_long_exit_activation_threshold=210.,
        i_long_exit_activation_confirmation_threshold=-260., i_use_long_exit_confirmation=False,
        i_trailing_stop_threshold=20.)
    report = assess_stability(frame, params, np.ones(5), frame.close.to_numpy(), frame.high.to_numpy(),
        _score_to_signals, [('IS', '2020-01-01', '2020-01-02')])
    assert report['passed']
    assert len(report['trials']) == 12
    assert all(t['periods'][0]['changed_action_bars'] == 0 for t in report['trials'])


def test_harm_is_relative_wealth_not_absolute_compounded_return():
    from strategies.mlp_execution_stability import performance_change
    base = {'pnl_pct': 10000., 'dd_pct': -20.}
    assert not performance_change(base, {'pnl_pct': 9950., 'dd_pct': -20.})['harmful']
    assert performance_change(base, {'pnl_pct': 9800., 'dd_pct': -20.})['harmful']
    assert performance_change(base, {'pnl_pct': 10000., 'dd_pct': -21.1})['harmful']
    assert not performance_change(base, {'pnl_pct': 12000., 'dd_pct': -19.})['harmful']
    with pytest.raises(ValueError):
        performance_change(base, {'pnl_pct': float('nan'), 'dd_pct': -20.})


def test_smoothed_entry_with_zero_proximity_can_fail_execution_guard():
    from strategies.mlp_execution_stability import assess_stability
    from strategies.strategy_mlp_scores import _score_to_signals
    frame = pd.DataFrame(dict(time=pd.date_range('2020-01-01', periods=4, freq='6h'),
        activation_score=[200., .04, 199.92, 200.], close=[100.,100.,50.,50.], high=[100.,100.,50.,50.]))
    params = dict(i_long_entry_activation_threshold=100., i_long_exit_activation_threshold=900.,
        i_long_exit_activation_confirmation_threshold=-900., i_entry_score_window=2, i_trailing_stop_threshold=20.)
    report = assess_stability(frame, params, np.zeros(4), frame.close.to_numpy(), frame.high.to_numpy(),
        _score_to_signals, [('IS', '2020-01-01', '2020-01-01')])
    assert not report['passed']
    harmful = [p for t in report['trials'] for p in t['periods'] if p['harmful']]
    assert max(p['wealth_loss_pct'] for p in harmful) > 40
    assert all(p['changed_action_bars'] > 0 for p in harmful)


def test_gate_requires_current_bound_complete_evidence():
    from strategies.mlp_execution_stability import stability_passes, stability_periods, VERSION, THRESHOLDS, DELTAS
    row = dict(Evaluation_Key='row', Comparison_Fingerprint='context')
    assert not stability_passes(row)
    period = dict(center={'pnl_pct': 100., 'dd_pct': -10.}, perturbed={'pnl_pct': 100., 'dd_pct': -10.})
    report = dict(version=VERSION, passed=True,
        trials=[dict(threshold=k, delta=d, periods=[dict(period, name=n, start=s, end=e)
            for n, s, e in stability_periods()]) for k in THRESHOLDS for d in DELTAS])
    row.update(Stability_Evaluation_Key='row', Stability_Context='context', Stability_Status='passed',
               Stability_Report=json.dumps(report))
    assert stability_passes(row)
    assert not stability_passes(dict(row, Evaluation_Key='other'))
    assert not stability_passes(dict(row, Stability_Status='unassessed_ineligible'))
    assert not stability_passes(dict(row, Stability_Report='{}'))
