from __future__ import annotations

import sys
from datetime import date
from pathlib import Path

ROOT = Path(__file__).resolve().parents[2]
if str(ROOT) not in sys.path:
    sys.path.insert(0, str(ROOT))

from src.quant_engine.qualitative_sell_strategy_v1 import (
    classify_market_regime,
    compute_microstructure_pressure_from_orderbook,
    compute_qualitative_sell_strategy,
    compute_satellite_candidate_score,
    compute_short_interest_composite,
)


def test_classify_market_regime():
    assert classify_market_regime("RISING") == "PERFORMANCE_MARKET"
    assert classify_market_regime("FLAT") == "TECHNICAL_MARKET"
    assert classify_market_regime("FALLING") == "TECHNICAL_MARKET"
    assert classify_market_regime(None) == "NEUTRAL"
    assert classify_market_regime("garbage") == "NEUTRAL"


def test_short_interest_composite_data_missing_without_estimating():
    result = compute_short_interest_composite({"short_balance_ratio": 0.6})
    assert result["status"] == "DATA_MISSING"
    assert "short_turnover_share" in result["missing_inputs"]
    assert result["short_interest_pressure"] is None


def test_short_interest_composite_low_balance_regime_reweights():
    low_balance = compute_short_interest_composite({
        "short_balance_ratio": 0.6, "short_balance_ratio_chg_20d": 0.1,
        "short_turnover_share": 14.0, "relative_return_20d": -8.0,
        "volume_ratio_5d": 1.8, "earnings_outlook": "DETERIORATING",
    })
    assert low_balance["low_balance_regime"] is True
    assert low_balance["weights_used"]["balance"] < low_balance["weights_used"]["turnover"]
    assert low_balance["label"] == "ELEVATED_SHORT_PRESSURE"


def test_confluence_requires_minimum_three_agreeing_factors():
    # 2개 팩터만 매도방향(macro, short_interest) 합의 — 3개 미달이므로 매도 액션 금지
    ctx = {
        "macro_pressure": 0.5, "short_interest_pressure": 0.6,
        "fundamental_trajectory": -0.5, "microstructure_pressure": -0.4,
        "liquidity_rotation_risk": 0.1,
    }
    out = compute_qualitative_sell_strategy(ctx)
    assert out["action"] not in {"EXIT_REVIEW_FULL", "TRIM_REVIEW_PARTIAL"}


def test_confluence_triggers_trim_when_three_factors_agree():
    ctx = {
        "macro_pressure": 0.5, "short_interest_pressure": 0.5,
        "fundamental_trajectory": 0.4, "microstructure_pressure": 0.1,
        "liquidity_rotation_risk": 0.0,
    }
    out = compute_qualitative_sell_strategy(ctx)
    assert out["action"] == "TRIM_REVIEW_PARTIAL"
    assert set(out["sell_agreeing_factors"]) == {"macro_pressure", "short_interest_pressure", "fundamental_trajectory"}


def test_insufficient_data_does_not_fabricate_action():
    out = compute_qualitative_sell_strategy({"macro_pressure": 0.9})
    assert out["action"] == "INSUFFICIENT_DATA_NO_ACTION"
    assert out["mechanical_sell_prohibited"] is True


def test_review_window_pre_earnings_when_outlook_deteriorating():
    ctx = {
        "macro_pressure": 0.5, "fundamental_trajectory": 0.5, "short_interest_pressure": 0.5,
        "earnings_outlook": "DETERIORATING",
        "next_earnings_date": date(2026, 7, 24),
        "today": date(2026, 6, 21),
    }
    out = compute_qualitative_sell_strategy(ctx)
    assert out["review_window"]["window_basis"] == "PRE_EARNINGS_EXIT_BEFORE_SURPRISE_RISK"
    assert out["review_window"]["review_window_end"] < "2026-07-24"


def test_review_window_defers_past_earnings_when_outlook_improving():
    ctx = {
        "macro_pressure": -0.5, "fundamental_trajectory": -0.5, "short_interest_pressure": -0.5,
        "earnings_outlook": "IMPROVING",
        "next_earnings_date": date(2026, 7, 24),
        "today": date(2026, 6, 21),
    }
    out = compute_qualitative_sell_strategy(ctx)
    assert out["action"] == "HOLD_ADD_CONVICTION"
    assert out["review_window"]["window_basis"] == "REASSESS_AFTER_EARNINGS_CONFIRM"


def test_regime_weighting_shifts_composite_score_without_changing_confluence_count():
    base_ctx = {
        "macro_pressure": 0.4, "fundamental_trajectory": 0.6, "short_interest_pressure": 0.35,
        "microstructure_pressure": 0.1, "liquidity_rotation_risk": 0.0,
    }
    performance = compute_qualitative_sell_strategy({**base_ctx, "rate_trend": "RISING"})
    technical = compute_qualitative_sell_strategy({**base_ctx, "rate_trend": "FALLING"})
    assert performance["market_regime"] == "PERFORMANCE_MARKET"
    assert technical["market_regime"] == "TECHNICAL_MARKET"
    assert performance["sell_agreeing_factors"] == technical["sell_agreeing_factors"]
    assert performance["composite_score"] != technical["composite_score"]


def test_satellite_candidate_score_insufficient_data():
    out = compute_satellite_candidate_score({"fundamental_trajectory": 0.2})
    assert out["satellite_action"] == "INSUFFICIENT_DATA_NO_ACTION"


def test_satellite_candidate_score_buy_candidate_on_strong_export_and_fundamentals():
    out = compute_satellite_candidate_score({
        "sector_export_trend": 12.0, "fundamental_trajectory": -0.4,
        "relative_return_20d": 3.0, "rate_trend": "RISING",
    })
    assert out["satellite_action"] == "BUY_CANDIDATE"
    assert out["market_regime"] == "PERFORMANCE_MARKET"


def test_microstructure_pressure_from_orderbook_ask_heavy_is_positive():
    out = compute_microstructure_pressure_from_orderbook({"total_askp_rsqn": "300000", "total_bidp_rsqn": "100000"})
    assert out["status"] == "OK"
    assert out["microstructure_pressure"] > 0


def test_microstructure_pressure_from_orderbook_bid_heavy_is_negative():
    out = compute_microstructure_pressure_from_orderbook({"total_askp_rsqn": "100000", "total_bidp_rsqn": "300000"})
    assert out["microstructure_pressure"] < 0


def test_microstructure_pressure_from_orderbook_missing_fields():
    out = compute_microstructure_pressure_from_orderbook({})
    assert out["status"] == "DATA_MISSING"
    assert out["microstructure_pressure"] is None


def test_map_universe_sector_to_hs_sector_substring_match():
    from tools.build_satellite_candidate_recommendations_v1 import map_universe_sector_to_hs_sector

    assert map_universe_sector_to_hs_sector("반도체/PCB") == "반도체"
    assert map_universe_sector_to_hs_sector("자동차/부품") == "자동차"
    assert map_universe_sector_to_hs_sector("AI전력/기기") is None
    assert map_universe_sector_to_hs_sector(None) is None
