"""Diversification gate: admit a forward-survivor only if left-tail corr is low AND it adds Sharpe.
Fail-SAFE: if the tail can't be estimated, do NOT admit."""
from __future__ import annotations

import numpy as np

from fxhnt.domain.diversification.gate import diversification_gate


def test_admits_uncorrelated_additive_sleeve() -> None:
    rng = np.random.default_rng(0)
    book = rng.normal(0.0005, 0.01, 1000)
    sleeve = rng.normal(0.0005, 0.01, 1000)
    v = diversification_gate(sleeve, book, max_tail_corr=0.5, min_marginal=0.0, tail_frac=0.1)
    assert v.admit is True


def test_rejects_tail_correlated_sleeve() -> None:
    rng = np.random.default_rng(1)
    book = rng.normal(0.0005, 0.01, 1000)
    sleeve = book.copy()                            # perfectly correlated incl. tail
    v = diversification_gate(sleeve, book, max_tail_corr=0.5, min_marginal=0.0, tail_frac=0.1)
    assert v.admit is False and "tail" in v.reason.lower()


def test_failsafe_when_tail_unestimable() -> None:
    book = np.array([0.01, 0.02, -0.01])           # too few tail days
    sleeve = np.array([0.0, 0.0, 0.0])
    v = diversification_gate(sleeve, book, max_tail_corr=0.5, min_marginal=0.0, tail_frac=0.1)
    assert v.admit is False and "unestimable" in v.reason.lower()


def test_unestimable_marginal_reason_is_distinct() -> None:
    """A non-finite marginal Sharpe must report 'unestimable', not the '< threshold' message.

    Scenario: sleeve = -4 * book so the blended series (1-0.2)*book + 0.2*sleeve = 0 everywhere
    → blended std == 0 → _sharpe returns NaN → marginal_sharpe returns NaN.
    The left-tail correlation is -1 (finite, passes < 0.5 gate), so we reach the marginal branch.
    """
    rng = np.random.default_rng(9)
    n = 1000
    book = rng.normal(0.0005, 0.01, n)
    sleeve = -4.0 * book                            # blended = 0.8*book + 0.2*(-4*book) = 0
    v = diversification_gate(sleeve, book, max_tail_corr=0.5, min_marginal=0.05, tail_frac=0.1)
    assert v.admit is False
    assert "unestimable" in v.reason.lower()
    assert "marginal" in v.reason.lower()
