"""Small-threshold execution guard; proximity counts are diagnostics only."""
from __future__ import annotations

import json
from typing import Any, Callable

import numpy as np
import pandas as pd

from strategies.mlp_accounting import window_metrics
from config import SCORE_START, TRAIN_END, SUBPERIOD_SPLIT, WFO_FOLDS

VERSION = 'execution_stability_v1'
THRESHOLDS = ('i_long_entry_activation_threshold', 'i_long_exit_activation_threshold',
              'i_long_exit_activation_confirmation_threshold')
DELTAS = (-1., -.05, .05, 1.)
MAX_WEALTH_LOSS_PCT = 1.
MAX_DRAWDOWN_WORSENING_PP = 1.


def stability_periods() -> list[tuple[str, str, str]]:
    return [('IS', SCORE_START, TRAIN_END),
            ('Sub1', SCORE_START, str(pd.Timestamp(SUBPERIOD_SPLIT) - pd.Timedelta(nanoseconds=1))),
            ('Sub2', SUBPERIOD_SPLIT, TRAIN_END)] + [
            (f'WFO{i}', start, end) for i, (_, start, end) in enumerate(WFO_FOLDS)]


def performance_change(base: dict, changed: dict) -> dict:
    values = [base['pnl_pct'], base['dd_pct'], changed['pnl_pct'], changed['dd_pct']]
    if not np.isfinite(values).all() or base['pnl_pct'] <= -100:
        raise ValueError('Cannot assess nonfinite or insolvent baseline')
    loss = 100 * (1 - (1 + changed['pnl_pct']/100) / (1 + base['pnl_pct']/100))
    dd = base['dd_pct'] - changed['dd_pct']
    return dict(wealth_loss_pct=float(loss), drawdown_worsening_pp=float(dd),
        harmful=bool(loss > MAX_WEALTH_LOSS_PCT + 1e-10 or dd > MAX_DRAWDOWN_WORSENING_PP + 1e-10))


def assess_stability(scored: pd.DataFrame, params: dict, stoch: np.ndarray,
                     close: np.ndarray, high: np.ndarray, signal: Callable,
                     periods: list[tuple[str, str, str]]) -> dict:
    """All periods retain preceding history for carry-in P&L and fee timing."""
    if not periods or not np.isfinite([params[k] for k in THRESHOLDS]).all():
        raise ValueError('Finite thresholds and nonempty periods required')
    base = signal(scored.copy(), params, stoch, close, high)
    baselines = [window_metrics(base, start=start, end=end) for _, start, end in periods]
    trials = []
    for threshold in THRESHOLDS:
        for delta in DELTAS:
            changed = signal(scored.copy(), dict(params, **{threshold: params[threshold]+delta}), stoch, close, high)
            measurements = []
            for (name, start, end), center in zip(periods, baselines):
                candidate = window_metrics(changed, start=start, end=end)
                upper = pd.Timestamp(end)
                mask = (base.time >= pd.Timestamp(start)) & (
                    (base.time < upper + pd.Timedelta(days=1)) if len(end) == 10 else (base.time <= upper))
                actions = (base.execute_entry != changed.execute_entry) | (base.execute_exit != changed.execute_exit)
                metrics = ('pnl_pct', 'dd_pct', 'calmar', 'entries', 'exits')
                measurements.append(dict(name=name, start=start, end=end,
                    center={k:center[k] for k in metrics}, perturbed={k:candidate[k] for k in metrics},
                    changed_action_bars=int((actions & mask).sum()),
                    changed_position_bars=int(((base.position != changed.position) & mask).sum()),
                    **performance_change(center, candidate)))
            trials.append(dict(threshold=threshold, delta=delta, periods=measurements))
    return dict(version=VERSION, passed=not any(p['harmful'] for t in trials for p in t['periods']),
        max_wealth_loss_pct=MAX_WEALTH_LOSS_PCT, max_drawdown_worsening_pp=MAX_DRAWDOWN_WORSENING_PP,
        trials=trials)


def stability_assessed(row: dict[str, Any]) -> bool:
    """Old or partial cached rows cannot silently satisfy the new guard."""
    if (row.get('Stability_Status') not in ('passed', 'failed') or not row.get('Evaluation_Key')
            or not row.get('Comparison_Fingerprint')
            or row.get('Stability_Evaluation_Key') != row['Evaluation_Key']
            or row.get('Stability_Context') != row['Comparison_Fingerprint']):
        return False
    try:
        report = json.loads(row['Stability_Report'])
        if report['version'] != VERSION or type(report['passed']) is not bool:
            return False
        trials = report['trials']
        if len(trials) != 12 or {(t['threshold'], t['delta']) for t in trials} != {(k, d) for k in THRESHOLDS for d in DELTAS}:
            return False
        expected = stability_periods()
        if not all([(p['name'], p['start'], p['end']) for p in t['periods']] == expected for t in trials):
            return False
        passed = all(not performance_change(p['center'], p['perturbed'])['harmful']
                     for t in trials for p in t['periods'])
        return passed == report['passed'] and row['Stability_Status'] == ('passed' if passed else 'failed')
    except (KeyError, TypeError, ValueError, OverflowError):
        return False


def stability_passes(row: dict[str, Any]) -> bool:
    return row.get('Stability_Status') == 'passed' and stability_assessed(row)
