from __future__ import annotations

from datetime import datetime, timezone
from decimal import Decimal

from src.hyperliquid.market_data import summarize_l2_book
from src.market.confluence import build_market_confluence_report, score_coin_confluence
from src.market.context import CoinMarketContext, MarketContextSnapshot
from src.market.event_risk import event_risk_from_news_items, load_event_risk_context
from src.market.fundamentals import CoinGeckoFundamentalsClient
from src.market.snapshot import build_normalized_market_snapshot
from src.tools.market_confluence_report import _build_research_sentiment_context
from src.tools.derivatives_history_collector import append_snapshot, enrich_with_derivatives_deltas, write_latest
from src.tools.news_event_risk_collector import collect_news_event_risk, parse_rss_items
from src.tools.coin_opportunity_radar import classify_market_rows
from src.tools.confluence_gate_impact_report import build_report


def ctx(**overrides):
    data = {
        "coin": "BTC",
        "rsi": Decimal("55"),
        "sma_fast": Decimal("101"),
        "sma_slow": Decimal("100"),
        "atr": Decimal("1.2"),
        "funding": Decimal("0"),
        "volume": Decimal("50000000"),
        "mid": Decimal("102"),
        "spread_pct": Decimal("0.03"),
        "timestamp": datetime.now(timezone.utc),
        "reliability_score": Decimal("0.99"),
        "stale_data": False,
        "l2_available": True,
        "open_interest": Decimal("12345.67"),
        "premium": Decimal("0.00002"),
        "oracle_px": Decimal("102.0"),
        "mark_px": Decimal("102.05"),
        "prev_day_px": Decimal("100"),
        "day_ntl_vlm": Decimal("75000000"),
        "bid_depth_notional_5": Decimal("50000"),
        "ask_depth_notional_5": Decimal("50000"),
        "buy_impact_1k_pct": Decimal("0.02"),
        "sell_impact_1k_pct": Decimal("0.02"),
        "buy_impact_5k_pct": Decimal("0.04"),
        "sell_impact_5k_pct": Decimal("0.04"),
    }
    data.update(overrides)
    return CoinMarketContext(**data)


def test_score_coin_confluence_accepts_clean_retest_context():
    score = score_coin_confluence(ctx())

    assert score.final_score >= Decimal("60")
    assert score.recommendation == "paper_candidate"
    assert score.live_order_allowed is False
    assert score.mainnet_signed_action is False
    assert score.blockers == ()


def test_score_coin_confluence_blocks_overheated_or_illiquid_context():
    score = score_coin_confluence(ctx(rsi=Decimal("82"), volume=Decimal("5000000"), day_ntl_vlm=Decimal("5000000"), spread_pct=Decimal("0.20"), l2_available=False, buy_impact_1k_pct=Decimal("0.20"), sell_impact_1k_pct=Decimal("0.18")))

    assert score.recommendation == "block_new_entry"
    assert "momentum_overheated" in score.blockers
    assert "weak_liquidity" in score.blockers
    assert "spread_too_wide" in score.blockers
    assert "l2_unavailable" in score.blockers
    assert "impact_slippage_too_high" in score.blockers


def test_summarize_l2_book_calculates_depth_and_impact():
    summary = summarize_l2_book({"levels": [
        [{"px": "99", "sz": "20"}, {"px": "98", "sz": "20"}],
        [{"px": "101", "sz": "20"}, {"px": "102", "sz": "20"}],
    ]})

    assert summary.spread_pct is not None
    assert summary.bid_depth_notional_5 == Decimal("3940")
    assert summary.ask_depth_notional_5 == Decimal("4060")
    assert summary.buy_impact_1k_pct is not None
    assert summary.sell_impact_1k_pct is not None


def test_score_coin_confluence_blocks_crowded_derivatives_context():
    score = score_coin_confluence(ctx(funding=Decimal("0.00018"), premium=Decimal("0.0015"), oracle_px=Decimal("100"), mark_px=Decimal("100.4")))

    assert score.recommendation == "block_new_entry"
    assert score.derivatives_score < Decimal("60")
    assert "crowded_positive_funding" in score.blockers
    assert "premium_too_high" in score.blockers
    assert "mark_oracle_basis_wide" in score.blockers


def test_build_market_confluence_report_sets_regime_and_serializes():
    snapshot = MarketContextSnapshot.from_coin_contexts([
        ctx(coin="BTC"),
        ctx(coin="ETH", sma_fast=Decimal("101"), sma_slow=Decimal("100"), mid=Decimal("103")),
        ctx(coin="WLD", rsi=Decimal("80"), volume=Decimal("3000000"), spread_pct=Decimal("0.15")),
    ])

    report = build_market_confluence_report(snapshot)
    data = report.to_dict()

    assert report.market_regime in {"risk_on", "selective"}
    assert report.eligible_count >= 2
    assert data["live_order_allowed"] is False
    assert data["mainnet_signed_action"] is False
    assert data["score_version"] == "confluence_score.v1"
    assert "risk" in data["weights"]
    assert data["scores"]["BTC"]["recommendation"] == "paper_candidate"
    assert "derivatives_score" in data["scores"]["BTC"]
    assert "sentiment_score" in data["scores"]["BTC"]
    assert "event_risk_score" in data["scores"]["BTC"]
    assert "portfolio_score" in data["scores"]["BTC"]
    assert "risk_score" in data["scores"]["BTC"]
    assert data["scores"]["BTC"]["final_trade_score"] == data["scores"]["BTC"]["final_score"]
    assert data["scores"]["WLD"]["recommendation"] == "block_new_entry"


def test_build_normalized_market_snapshot_exposes_phase2_schema_and_derivatives():
    snapshot = MarketContextSnapshot.from_coin_contexts([
        ctx(coin="BTC", funding=Decimal("0.00012"), premium=Decimal("0.0012")),
        ctx(coin="ETH", spread_pct=Decimal("0.12"), l2_available=True),
    ])
    confluence = build_market_confluence_report(snapshot)

    normalized = build_normalized_market_snapshot(snapshot, confluence).to_dict()
    btc = normalized["coins"]["BTC"]

    assert normalized["schema_version"] == "market_snapshot.v1"
    assert normalized["live_order_allowed"] is False
    assert normalized["mainnet_signed_action"] is False
    assert set(["price", "ohlcv", "trend", "momentum", "volatility", "liquidity", "derivatives", "fundamentals", "sentiment", "event_risk", "regime", "risk", "confluence"]).issubset(btc)
    assert btc["derivatives"]["source"] == "hyperliquid_metaAndAssetCtxs"
    assert btc["derivatives"]["funding"] == "0.00012000"
    assert btc["derivatives"]["open_interest"] == "12345.67"
    assert Decimal(btc["derivatives"]["score"]) > Decimal("0")
    assert Decimal(btc["liquidity"]["buy_impact_1k_pct"]) == Decimal("0.0200")
    assert Decimal(btc["liquidity"]["sell_impact_1k_pct"]) == Decimal("0.0200")
    assert btc["derivatives"]["crowding_state"] == "crowded_long"
    assert btc["fundamentals"]["status"] == "not_loaded"
    assert btc["sentiment"]["status"] == "not_loaded"
    assert btc["event_risk"]["status"] == "not_loaded"
    assert Decimal(btc["sentiment"]["score"]) == Decimal("50.00")
    assert Decimal(btc["risk"]["score"]) > Decimal("0")
    assert btc["confluence"]["score_version"] == "confluence_score.v1"
    assert btc["confluence"]["final_trade_score"] == btc["confluence"]["final_score"]
    assert btc["confluence"]["recommendation"] == "block_new_entry"


def test_coingecko_fundamentals_client_maps_public_market_fields():
    class FakeTransport:
        def get(self, url, *, params, timeout=10):
            assert "bitcoin" in params["ids"]
            return [{
                "id": "bitcoin",
                "market_cap": 1000,
                "fully_diluted_valuation": 1200,
                "total_volume": 50,
                "market_cap_rank": 1,
                "price_change_percentage_24h_in_currency": 2.5,
                "price_change_percentage_7d_in_currency": -1.25,
                "price_change_percentage_1h_in_currency": 0.5,
                "price_change_percentage_30d_in_currency": 12.5,
                "ath": 100,
                "atl": 1,
                "circulating_supply": 19,
                "total_supply": 21,
            }]

    snapshot = CoinGeckoFundamentalsClient(FakeTransport()).collect(("BTC",))
    btc = snapshot.for_coin("BTC")

    assert snapshot.status == "loaded"
    assert btc["source"] == "coingecko_markets"
    assert btc["status"] == "loaded"
    assert btc["coingecko_id"] == "bitcoin"
    assert btc["market_cap_usd"] == "1000"
    assert btc["market_cap_rank"] == 1
    assert btc["price_change_1h_pct"] == "0.5"
    assert btc["price_change_30d_pct"] == "12.5"
    assert btc["ath_usd"] == "100"
    assert btc["atl_usd"] == "1"
    assert btc["fdv_to_market_cap"] == "1.2"


def test_normalized_market_snapshot_accepts_loaded_fundamentals():
    snapshot = MarketContextSnapshot.from_coin_contexts([ctx(coin="BTC")])
    confluence = build_market_confluence_report(snapshot)
    normalized = build_normalized_market_snapshot(
        snapshot,
        confluence,
        fundamentals={"BTC": {"source": "coingecko_markets", "status": "loaded", "market_cap_rank": 1}},
        sentiment={"BTC": {"source": "tradingview_community_research_context", "status": "loaded_research_only", "score": "50.00", "recent_ideas_count": 2}},
        event_risk={"BTC": {"source": "news_search_research_context", "status": "loaded_research_only", "risk_level": "green", "score": "80.00"}},
    ).to_dict()

    assert normalized["coins"]["BTC"]["fundamentals"]["status"] == "loaded"
    assert normalized["coins"]["BTC"]["fundamentals"]["market_cap_rank"] == 1
    assert normalized["coins"]["BTC"]["sentiment"]["status"] == "loaded_research_only"
    assert normalized["coins"]["BTC"]["sentiment"]["recent_ideas_count"] == 2
    assert normalized["coins"]["BTC"]["event_risk"]["risk_level"] == "green"


def test_research_sentiment_context_is_research_only(tmp_path):
    research_dir = tmp_path / "research"
    research_dir.mkdir()
    (research_dir / "tradingview_community_ideas.jsonl").write_text('{"coin":"BTC","bias":"bullish","live_order_allowed":false,"mainnet_signed_action":false}\n', encoding="utf-8")

    rows, summary = _build_research_sentiment_context(coins=("BTC", "ETH"), runtime_dir=str(tmp_path))

    assert rows["BTC"]["source"] == "tradingview_community_research_context"
    assert rows["BTC"]["recent_ideas_count"] == 1
    assert rows["BTC"]["research_only"] is True
    assert rows["BTC"]["live_order_allowed"] is False
    assert summary["research_only"] is True


def test_news_event_risk_collector_parses_rss_and_stays_research_only():
    rss = """<?xml version='1.0'?><rss><channel>
    <item><title>Bitcoin rallies before FOMC decision</title><description>Crypto traders watch CPI and Fed risk.</description><link>https://example.test/btc</link></item>
    <item><title>Solana bridge exploit reported</title><description>SOL ecosystem exploit risk.</description><link>https://example.test/sol</link></item>
    </channel></rss>"""

    items = parse_rss_items(rss, source="unit", coins=("BTC", "SOL"))
    payload = collect_news_event_risk(feeds=(), coins=("BTC", "SOL"))
    manual_btc = event_risk_from_news_items("BTC", items).to_dict()
    manual_sol = event_risk_from_news_items("SOL", items).to_dict()

    assert len(items) == 2
    assert manual_btc["risk_level"] in {"yellow", "red"}
    assert manual_sol["risk_level"] == "red"
    assert payload["live_order_allowed"] is False
    assert payload["mainnet_signed_action"] is False


def test_derivatives_history_writers_emit_jsonl_and_latest(tmp_path):
    payload = {"timestamp": "2026-07-05T00:00:00+00:00", "coins": {"BTC": {"funding": "0", "open_interest": "1"}}, "live_order_allowed": False, "mainnet_signed_action": False}
    history = tmp_path / "derivatives_history.jsonl"
    latest = tmp_path / "derivatives_latest.json"

    append_snapshot(history, payload)
    write_latest(latest, payload)

    assert history.read_text(encoding="utf-8").count("\n") == 1
    loaded = __import__("json").loads(latest.read_text(encoding="utf-8"))
    assert loaded["live_order_allowed"] is False
    assert loaded["mainnet_signed_action"] is False


def test_derivatives_history_enriches_oi_funding_deltas():
    payload = {"timestamp": "2026-07-05T01:00:00+00:00", "coins": {"BTC": {"open_interest": "110", "funding": "0.00006", "premium": "0.0006"}}, "live_order_allowed": False, "mainnet_signed_action": False}
    history = [{"timestamp": "2026-07-05T00:00:00+00:00", "coins": {"BTC": {"open_interest": "100", "funding": "0.00001", "premium": "0.0001"}}}]

    enriched = enrich_with_derivatives_deltas(payload, history, windows=(60,))
    btc = enriched["coins"]["BTC"]

    assert btc["derivatives_deltas"]["60m"]["open_interest_change_pct"] == "10.0000"
    assert btc["derivatives_deltas"]["60m"]["funding_change"] == "0.00005000"
    assert btc["crowding_alert"] == "neutral"


def test_event_risk_market_macro_is_yellow_not_coin_red():
    items = [{"symbols": ["MARKET"], "title": "Crypto market watches FOMC and CPI", "summary": "Fed macro risk", "category": "crypto_news_rss"}]
    btc = event_risk_from_news_items("BTC", items).to_dict()

    assert btc["risk_level"] == "yellow"
    assert btc["score"] == "50.00"


def test_event_risk_coin_exploit_stays_red():
    items = [{"symbols": ["SOL"], "title": "Solana bridge exploit reported", "summary": "SOL ecosystem exploit risk", "category": "crypto_news_rss"}]
    sol = event_risk_from_news_items("SOL", items).to_dict()

    assert sol["risk_level"] == "red"
    assert sol["score"] == "20.00"


def test_event_risk_ignores_stale_old_news():
    items = [{"symbols": ["SOL"], "published_at": "2026-07-01T00:00:00+00:00", "title": "Solana bridge exploit reported", "summary": "old item", "category": "crypto_news_rss"}]
    sol = event_risk_from_news_items("SOL", items, now=datetime(2026, 7, 5, tzinfo=timezone.utc)).to_dict()

    assert sol["risk_level"] == "green"
    assert sol["score"] == "80.00"


def test_coin_opportunity_radar_detects_hl_movers_research_only():
    rows = [
        {"symbol": "btc", "id": "bitcoin", "name": "Bitcoin", "market_cap_rank": 1, "total_volume": 500000000, "price_change_percentage_1h_in_currency": 6, "price_change_percentage_24h_in_currency": 12, "market_cap": 1000},
        {"symbol": "illiquid", "id": "illiquid", "name": "Illiquid", "market_cap_rank": 900, "total_volume": 1000, "price_change_percentage_1h_in_currency": 50, "price_change_percentage_24h_in_currency": 80},
        {"symbol": "sol", "id": "solana", "name": "Solana", "market_cap_rank": 6, "total_volume": 200000000, "price_change_percentage_1h_in_currency": -6, "price_change_percentage_24h_in_currency": -18},
    ]

    candidates = classify_market_rows(rows, hl_symbols={"BTC", "SOL"}, trending_symbols={"SOL"})

    assert [row["symbol"] for row in candidates] == ["SOL", "BTC"]
    assert "sharp_1h_selloff" in candidates[0]["reasons"]
    assert "coingecko_trending" in candidates[0]["reasons"]
    assert candidates[0]["live_order_allowed"] is False
    assert candidates[0]["mainnet_signed_action"] is False


def test_confluence_gate_impact_report_summarizes_blockers(tmp_path):
    exp = tmp_path / "experiments" / "candidate_v76_fee_aware_anti_chase"
    exp.mkdir(parents=True)
    (exp / "signal_journal.jsonl").write_text(
        '{"final_decision":"blocked:confluence_block_new_entry","would_enter":false,"block_reason":["confluence_block_new_entry","tv_bias_bearish"],"confluence_gate":{"score":"55"},"tradingview_context":{"available":true}}\n',
        encoding="utf-8",
    )
    (exp / "trade_journal.jsonl").write_text('{"event":"exit","net_pnl_usd":"0.10"}\n', encoding="utf-8")
    (tmp_path / "reports").mkdir()
    (tmp_path / "reports" / "market_confluence_latest.json").write_text('{"confluence":{"market_regime":"risk_off","eligible_count":0}}', encoding="utf-8")

    report = build_report(runtime_root=tmp_path, experiment_root=tmp_path / "experiments", limit=10)
    row = report["strategies"]["candidate_v76_fee_aware_anti_chase"]

    assert report["live_order_allowed"] is False
    assert row["signals_seen"] == 1
    assert row["top_blockers"][0][0] == "confluence_block_new_entry"
    assert row["tv_available_signals"] == 1
