import json
from pathlib import Path

import pandas as pd
import pytest

from psx_signal.data.point_in_time import map_sectors_as_of, membership_as_of
from psx_signal.paper import PaperSignalRecord, PaperSignalStore
from psx_signal.strategies import load_frozen_strategy


def _paper_record() -> PaperSignalRecord:
    return PaperSignalRecord.create(
        strategy_version="psx_xgb_5d_topn_v1", strategy_fingerprint="abc",
        model_version="model-1", dataset_version="data-1", as_of_date="2026-08-13",
        symbol="SYS", rank=1, model_score=0.6123, market_regime="NEUTRAL",
        signal_close=150.0, planned_entry_session="2026-08-14",
        planned_horizon_sessions=5, selection_memberships=("TOP_1", "TOP_3"),
    )


def test_frozen_strategy_fingerprint_is_valid() -> None:
    strategy = load_frozen_strategy("psx_xgb_5d_topn_v1")
    assert strategy["fingerprint"] == "a8cf67171c3e6d2e478b923d071bc5c281ccbf7afe7cd4f7129ff7029378d733"


def test_paper_signal_store_appends_but_rejects_identity_rewrite(tmp_path: Path) -> None:
    store = PaperSignalStore(tmp_path / "signals.jsonl")
    record = _paper_record()
    store.append([record])
    before = (tmp_path / "signals.jsonl").read_text()
    with pytest.raises(ValueError, match="immutable"):
        store.append([record])
    assert (tmp_path / "signals.jsonl").read_text() == before
    assert json.loads(before)["model_score"] == pytest.approx(0.6123)


def test_point_in_time_membership_does_not_backfill_future_snapshot() -> None:
    intervals = pd.DataFrame({
        "effective_from": ["2024-04-01", "2025-10-01"],
        "effective_to": ["2025-09-30", None], "symbol": ["AAA", "BBB"],
        "index": ["KSE100", "KSE100"],
        "verification_status": ["VERIFIED_MEMBER", "VERIFIED_MEMBER"],
    })
    assert membership_as_of(intervals, "2024-06-01")["symbol"].tolist() == ["AAA"]
    assert membership_as_of(intervals, "2023-06-01").empty


def test_point_in_time_membership_accepts_normalized_membership_status() -> None:
    intervals = pd.DataFrame({
        "effective_from": ["2025-04-01", "2025-04-01"],
        "effective_to": ["2025-09-30", "2025-09-30"],
        "symbol": ["AAA", "BBB"], "index": ["KSE100", "KSE100"],
        "membership_status": ["VERIFIED_MEMBER", "UNKNOWN"],
    })
    assert membership_as_of(intervals, "2025-06-01")["symbol"].tolist() == ["AAA"]


def test_sector_mapping_respects_effective_interval() -> None:
    observations = pd.DataFrame({
        "date": pd.to_datetime(["2024-06-01", "2025-06-01"]), "symbol": ["AAA", "AAA"],
    })
    sectors = pd.DataFrame({
        "symbol": ["AAA", "AAA"], "sector": ["OLD", "NEW"],
        "effective_from": ["2020-01-01", "2025-01-01"],
        "effective_to": ["2024-12-31", None], "source": ["MANUAL", "MANUAL"],
    })
    assert map_sectors_as_of(observations, sectors)["sector"].tolist() == ["OLD", "NEW"]
