import streamlit as st
import pandas as pd
import plotly.express as px
from datetime import datetime, timedelta


def _check_openbb_installed() -> bool:
    try:
        # OpenBB v4 推荐用法: from openbb import obb
        from openbb import obb  # noqa: F401
        return True
    except Exception:
        return False


def _load_price_data(symbol: str, days: int = 365) -> pd.DataFrame:
    """Fetch OHLCV using OpenBB v4 when available; otherwise return demo/synthetic data."""
    end = datetime.utcnow().date()
    start = end - timedelta(days=days)

    # 优先使用 OpenBB v4
    try:
        from openbb import obb

        # 先尝试股票路由
        try:
            out = obb.equity.price.historical(
                symbol,
                start_date=str(start),
                end_date=str(end),
            )
        except Exception:
            out = None

        # 若股票无数据，再尝试 ETF 路由
        if out is None or (hasattr(out, "is_empty") and out.is_empty):
            try:
                out = obb.etf.price.historical(
                    symbol,
                    start_date=str(start),
                    end_date=str(end),
                )
            except Exception:
                out = None

        if out is not None:
            if hasattr(out, "to_df"):
                df = out.to_df()
            elif hasattr(out, "to_dataframe"):
                df = out.to_dataframe()
            else:
                # 兜底: 有些 provider 返回可序列化对象
                df = pd.DataFrame(out)  # type: ignore[arg-type]

            # 规格化列名
            if not isinstance(df, pd.DataFrame) or df.empty:
                raise ValueError("OpenBB 返回空数据")

            # 有的表以 index 为日期
            if 'date' in df.columns:
                df['Date'] = pd.to_datetime(df['date'])
            elif df.index.name in ('date', 'Date') or isinstance(df.index, pd.DatetimeIndex):
                df = df.copy()
                df['Date'] = pd.to_datetime(df.index)
            else:
                # 尝试查找常见日期列
                for cand in ['timestamp', 'time', 'datetime']:
                    if cand in df.columns:
                        df['Date'] = pd.to_datetime(df[cand])
                        break

            # 归一化收盘价列
            close_col = None
            for cand in ['adj_close', 'close', 'Close', 'price', 'close_price', 'c']:
                if cand in df.columns:
                    close_col = cand
                    break
            if close_col is None:
                raise ValueError("未找到收盘价列")
            df['Close'] = pd.to_numeric(df[close_col], errors='coerce')

            # 仅保留需要列并清洗
            if 'Date' not in df.columns:
                raise ValueError("未找到日期列")
            df = df[['Date', 'Close']].dropna()
            df = df.sort_values('Date').reset_index(drop=True)
            # 限定时间窗口（有些 provider 可能返回更长区间）
            df = df[df['Date'].dt.date.between(start, end)]

            if df.empty:
                raise ValueError("清洗后为空")
            return df

    except Exception:
        # 如果 OpenBB 不可用或调用失败，进入本地演示/合成数据兜底
        pass

    # Fallback to demo from examples/data
    try:
        from pathlib import Path
        root = Path(__file__).resolve().parents[2]
        demo_map = {
            'AAPL': root / 'examples' / 'data' / 'demo_results_aapl.json',
            'MSFT': root / 'examples' / 'data' / 'demo_results_msft.json',
            'TSLA': root / 'examples' / 'data' / 'demo_results_tsla.json',
        }
        path = demo_map.get(symbol.upper())
        if path and path.exists():
            df = pd.read_json(path)
            if 'date' in df.columns:
                df['Date'] = pd.to_datetime(df['date'])
            if 'close' in df.columns:
                df['Close'] = df['close']
            df = df[['Date', 'Close']].dropna().sort_values('Date').reset_index(drop=True)
            # 裁剪到时间窗口
            df = df[df['Date'].dt.date.between(start, end)]
            return df
    except Exception:
        pass

    # Last resort: minimal synthetic data（避免 FutureWarning）
    dates = pd.date_range(end=end, periods=min(days, 180))
    return pd.DataFrame({
        'Date': dates,
        'Close': pd.Series(range(len(dates))).rolling(5).mean().bfill()
    })


def _kpis_from_df(df: pd.DataFrame) -> dict:
    if df.empty or 'Close' not in df.columns:
        return {"最新价": "-", "近30日涨幅": "-", "最大回撤(近90日)": "-"}
    latest = float(df['Close'].iloc[-1])
    last_30 = df.tail(30)
    if len(last_30) > 1:
        pct_30 = (last_30['Close'].iloc[-1] / last_30['Close'].iloc[0] - 1) * 100
    else:
        pct_30 = 0.0
    # max drawdown over last 90 days
    lookback = df.tail(90)['Close']
    roll_max = lookback.cummax()
    drawdown = (lookback / roll_max - 1).min() * 100
    return {
        "最新价": f"{latest:,.2f}",
        "近30日涨幅": f"{pct_30:.2f}%",
        "最大回撤(近90日)": f"{drawdown:.2f}%",
    }


def render_openbb_tab():
    st.write("使用 OpenBB（如可用）或演示数据展示市场概览。")

    col_a, col_b = st.columns([2, 1])
    with col_b:
        symbol = st.text_input("股票/ETF 代码", value="AAPL")
        days = st.slider("时间窗口（天）", 90, 720, 365, step=30)
        obb_ready = _check_openbb_installed()
        if obb_ready:
            st.success("OpenBB 已安装 ✅")
        else:
            st.info("未检测到 OpenBB，将使用演示数据。可在 requirements.txt 中加入 openbb 后安装启用。")

    with col_a:
        df = _load_price_data(symbol, days)
        if df is None or df.empty:
            st.warning("未获取到数据")
            return
        # 绘制收盘价
        if 'Date' in df.columns and 'Close' in df.columns:
            fig = px.line(df, x='Date', y='Close', title=f"{symbol.upper()} 收盘价")
            st.plotly_chart(fig, use_container_width=True)
        else:
            st.dataframe(df.head())

    # KPI 卡片
    st.markdown("#### 关键指标")
    kpis = _kpis_from_df(df)
    k1, k2, k3 = st.columns(3)
    k1.metric("最新价", kpis["最新价"]) 
    k2.metric("近30日涨幅", kpis["近30日涨幅"]) 
    k3.metric("最大回撤(近90日)", kpis["最大回撤(近90日)"]) 

    # 未来：基本面、新闻、情绪等组件占位
    with st.expander("🚧 更多组件（即将推出）"):
        st.write("基本面卡片、新闻与情绪、宏观指标、策略筛选等将逐步接入。")