from __future__ import annotations

from typing import Any

import streamlit as st
from streamlit_autorefresh import st_autorefresh

from web_core.constants import INTERVAL_OPTIONS, PERIOD_OPTIONS
from web_core.ui.help_content import help_dialog
from web_core.market.presets import MARKET_PRESET_OPTIONS, apply_market_preset
from web_core.market.symbols import lookup_symbol_candidates
from web_core.auth.profile_store import (
    first_query_param_value,
    format_epoch,
    get_profile_audit,
    list_web_profiles,
    load_web_settings,
    normalize_web_settings,
)

def _parse_watchlist(raw: str) -> list[str]:
    seen: set[str] = set()
    symbols: list[str] = []
    normalized_raw = str(raw or "").replace("\\n", "\n").replace("\\N", "\n").replace(",", "\n")
    for token in normalized_raw.splitlines():
        normalized = token.strip().upper()
        if not normalized or normalized in seen:
            continue
        seen.add(normalized)
        symbols.append(normalized)
    return symbols[:40]


def render_sidebar(active_profile: str, query_params: Any) -> dict[str, Any]:
    with st.sidebar:
        st.header("Profile")
        st.success(f"Logged in as: {active_profile}")
        if st.button("Switch profile", use_container_width=True):
            st.session_state.pop("active_profile", None)
            if "profile" in query_params:
                del query_params["profile"]
            st.rerun()
        st.divider()

        available_profiles = list_web_profiles()
        profile_audit = get_profile_audit(active_profile)
        st.caption(
            "Profiles found: "
            + ", ".join(available_profiles + ([active_profile] if active_profile not in available_profiles else []))
        )
        st.caption(
            "Audit: created "
            f"{format_epoch(profile_audit.get('created_at'))}, "
            f"last login {format_epoch(profile_audit.get('last_login_at'))}, "
            f"updated {format_epoch(profile_audit.get('updated_at'))}, "
            f"last symbol {str(profile_audit.get('last_symbol') or 'n/a')}"
        )
        st.divider()

        st.header("Data Settings")
        if st.button("Help / Quick Start", use_container_width=True):
            help_dialog()
        st.divider()

        persisted_settings = load_web_settings(profile_id=active_profile)
        query_overrides: dict[str, Any] = {}
        for key in persisted_settings:
            candidate = first_query_param_value(query_params, key)
            if candidate is not None:
                query_overrides[key] = candidate
        effective_defaults = normalize_web_settings({**persisted_settings, **query_overrides})

        st.subheader("Market Preset")
        preset_default = str(effective_defaults["market_preset"])
        preset_index = MARKET_PRESET_OPTIONS.index(preset_default) if preset_default in MARKET_PRESET_OPTIONS else 0
        market_preset = st.selectbox(
            "Preset",
            MARKET_PRESET_OPTIONS,
            index=preset_index,
            help="Select a template, then click Apply Preset to populate defaults.",
        )
        if market_preset != "Custom" and st.button("Apply Preset", use_container_width=True):
            effective_defaults = apply_market_preset(effective_defaults, market_preset)
            st.caption(f"Preset applied: {market_preset}")

        st.subheader("Watchlist")
        watchlist_raw = st.text_input(
            "Watchlist symbols (comma separated)",
            value=", ".join(effective_defaults["watchlist"]),
            help="Saved per profile. Example: AAPL, MSFT, NVDA, BTC-USD",
        )
        watchlist = _parse_watchlist(watchlist_raw)
        selected_from_watchlist: str | None = None
        if watchlist:
            st.caption("Quick select:")
            for idx, watch_symbol in enumerate(watchlist[:12]):
                if st.button(watch_symbol, use_container_width=True, key=f"watchlist-{watch_symbol}-{idx}"):
                    selected_from_watchlist = watch_symbol
                    st.session_state["watchlist_symbol_choice"] = watch_symbol
            selected_from_watchlist = st.session_state.get("watchlist_symbol_choice", selected_from_watchlist)

        st.subheader("Find Symbol")
        symbol_search_query = st.text_input(
            "Search by company or ticker",
            value="",
            placeholder="Apple, Tesla, Bitcoin...",
            help="Type a name (e.g. Apple) and select a result to prefill Symbol.",
        )
        symbol_from_search: str | None = None
        if symbol_search_query.strip():
            candidates = lookup_symbol_candidates(symbol_search_query)
            if candidates:
                result_placeholder = "Select a result..."
                labels = [result_placeholder] + [
                    " | ".join(
                        [
                            candidate["symbol"],
                            candidate["name"] or "No name",
                            candidate["exchange"] or "Unknown exchange",
                        ]
                    )
                    for candidate in candidates
                ]
                selected_label = st.selectbox("Matches", labels, index=0)
                if selected_label != result_placeholder:
                    selected_index = labels.index(selected_label) - 1
                    symbol_from_search = candidates[selected_index]["symbol"]
            else:
                st.caption("No matches found. Try another company name.")

        symbol = st.text_input(
            "Symbol",
            value=symbol_from_search or selected_from_watchlist or str(effective_defaults["symbol"]),
            help="Ticker or pair to analyze, e.g. AAPL, MSFT, BTC-USD.",
        ).strip().upper()
        interval = st.selectbox(
            "Timeframe",
            INTERVAL_OPTIONS,
            index=INTERVAL_OPTIONS.index(str(effective_defaults["interval"])),
            help="Bar size for each candle. Shorter intervals are noisier; 1d is a good default.",
        )
        period = st.selectbox(
            "Period",
            PERIOD_OPTIONS,
            index=PERIOD_OPTIONS.index(str(effective_defaults["period"])),
            help="How much history to load for trend analysis.",
        )
        max_bars = st.number_input(
            "Max bars",
            min_value=20,
            max_value=5000,
            value=int(effective_defaults["max_bars"]),
            step=10,
            help="Limits loaded candles to keep charting responsive. 500 is a solid starting point.",
        )
        drop_live = st.checkbox("Ignore potentially live last bar", value=bool(effective_defaults["drop_live"]))

        with st.expander("Classification Filters", expanded=False):
            use_body_range = st.checkbox(
                "Use previous body range (ignore wicks)",
                value=bool(effective_defaults["use_body_range"]),
            )
            volume_filter_enabled = st.checkbox(
                "Enable volume filter",
                value=bool(effective_defaults["volume_filter_enabled"]),
            )
            volume_sma_window = st.slider("Volume SMA window", 2, 100, int(effective_defaults["volume_sma_window"]))
            volume_multiplier = st.slider(
                "Min volume / SMA multiplier",
                0.1,
                3.0,
                float(effective_defaults["volume_multiplier"]),
                0.1,
            )
            gray_fake = st.checkbox("Gray out fake bars", value=bool(effective_defaults["gray_fake"]))
            hide_market_closed_gaps = st.checkbox(
                "Hide market-closed gaps (stocks)",
                value=bool(effective_defaults["hide_market_closed_gaps"]),
            )

        with st.expander("Training & Guidance", expanded=False):
            show_live_guide = st.checkbox(
                "Show live decision guide",
                value=bool(effective_defaults["show_live_guide"]),
                help="Shows a plain-English interpretation of current trend state and confirmation status.",
            )
            show_past_behavior = st.checkbox(
                "Show past behavior examples",
                value=bool(effective_defaults["show_past_behavior"]),
                help="Displays historical example trades based on trend confirmation and reversal signals.",
            )
            show_trade_markers = st.checkbox(
                "Overlay example entries/exits on chart",
                value=bool(effective_defaults["show_trade_markers"]),
                help="Adds entry/exit markers for the training examples onto the main chart.",
            )
            focus_chart_on_selected_example = st.checkbox(
                "Focus chart on selected example",
                value=bool(effective_defaults["focus_chart_on_selected_example"]),
                help="When a training row is selected, zooms chart around that trade window.",
            )
            max_training_examples = st.slider(
                "Max training examples",
                5,
                100,
                int(effective_defaults["max_training_examples"]),
                5,
            )
            replay_mode_enabled = st.checkbox(
                "Replay mode (hide future bars)",
                value=bool(effective_defaults["replay_mode_enabled"]),
            )
            replay_bars = st.slider(
                "Replay bars shown",
                20,
                1000,
                int(effective_defaults["replay_bars"]),
                5,
            )

        with st.expander("Monitoring", expanded=False):
            enable_auto_refresh = st.checkbox("Auto-refresh", value=bool(effective_defaults["enable_auto_refresh"]))
            refresh_sec = st.slider("Refresh interval (seconds)", 10, 600, int(effective_defaults["refresh_sec"]), 10)
            if enable_auto_refresh:
                st_autorefresh(interval=refresh_sec * 1000, key="data_refresh")

        with st.expander("Advanced Signals", expanded=False):
            advanced_auto_run = st.checkbox(
                "Auto-run advanced panels (slower)",
                value=bool(effective_defaults["advanced_auto_run"]),
            )
            run_advanced_now = st.button("Run advanced panels now", use_container_width=True)
            enable_multi_tf_confirmation = st.checkbox(
                "Show multi-timeframe confirmation (1h/4h/1d)",
                value=bool(effective_defaults["enable_multi_tf_confirmation"]),
            )
            enable_regime_filter = st.checkbox(
                "Regime filter (stand aside in choppy periods)",
                value=bool(effective_defaults["enable_regime_filter"]),
            )

        with st.expander("Compare Symbols", expanded=False):
            enable_compare_symbols = st.checkbox(
                "Enable compare symbols panel",
                value=bool(effective_defaults["enable_compare_symbols"]),
            )
            compare_symbols_raw = st.text_input(
                "Compare symbols (comma separated)",
                value=", ".join(effective_defaults["compare_symbols"]),
                help="Example: AAPL, MSFT, NVDA",
            )
            compare_symbols = _parse_watchlist(compare_symbols_raw)[:12]

        with st.expander("Alerts", expanded=False):
            enable_alert_rules = st.checkbox(
                "Enable alert rules",
                value=bool(effective_defaults["enable_alert_rules"]),
            )
            alert_on_bull = st.checkbox("Alert on bullish confirmations", value=bool(effective_defaults["alert_on_bull"]))
            alert_on_bear = st.checkbox("Alert on bearish confirmations", value=bool(effective_defaults["alert_on_bear"]))
            alert_webhook_url = st.text_input(
                "Webhook URL (optional)",
                value=str(effective_defaults["alert_webhook_url"]),
                help="Use Zapier/Make/Telegram bot webhook for push delivery.",
            )

        with st.expander("Backtest Controls", expanded=False):
            backtest_slippage_bps = st.slider(
                "Slippage (bps per side)",
                0.0,
                100.0,
                float(effective_defaults["backtest_slippage_bps"]),
                0.5,
            )
            backtest_fee_bps = st.slider(
                "Fee (bps per side)",
                0.0,
                100.0,
                float(effective_defaults["backtest_fee_bps"]),
                0.5,
            )
            backtest_stop_loss_pct = st.slider(
                "Stop loss (%)",
                0.0,
                25.0,
                float(effective_defaults["backtest_stop_loss_pct"]),
                0.25,
            )
            backtest_take_profit_pct = st.slider(
                "Take profit (%)",
                0.0,
                25.0,
                float(effective_defaults["backtest_take_profit_pct"]),
                0.25,
            )
            backtest_min_hold_bars = st.slider(
                "Min hold bars",
                1,
                20,
                int(effective_defaults["backtest_min_hold_bars"]),
                1,
            )
            backtest_max_hold_bars = st.slider(
                "Max hold bars",
                backtest_min_hold_bars,
                40,
                int(max(effective_defaults["backtest_max_hold_bars"], backtest_min_hold_bars)),
                1,
            )

    return {
        "symbol": symbol,
        "interval": interval,
        "period": period,
        "max_bars": int(max_bars),
        "drop_live": bool(drop_live),
        "use_body_range": bool(use_body_range),
        "volume_filter_enabled": bool(volume_filter_enabled),
        "volume_sma_window": int(volume_sma_window),
        "volume_multiplier": float(volume_multiplier),
        "gray_fake": bool(gray_fake),
        "hide_market_closed_gaps": bool(hide_market_closed_gaps),
        "show_live_guide": bool(show_live_guide),
        "show_past_behavior": bool(show_past_behavior),
        "show_trade_markers": bool(show_trade_markers),
        "focus_chart_on_selected_example": bool(focus_chart_on_selected_example),
        "max_training_examples": int(max_training_examples),
        "enable_auto_refresh": bool(enable_auto_refresh),
        "refresh_sec": int(refresh_sec),
        "watchlist": watchlist,
        "market_preset": market_preset,
        "compare_symbols": compare_symbols,
        "enable_compare_symbols": bool(enable_compare_symbols),
        "enable_multi_tf_confirmation": bool(enable_multi_tf_confirmation),
        "advanced_auto_run": bool(advanced_auto_run),
        "run_advanced_now": bool(run_advanced_now),
        "enable_regime_filter": bool(enable_regime_filter),
        "replay_mode_enabled": bool(replay_mode_enabled),
        "replay_bars": int(replay_bars),
        "enable_alert_rules": bool(enable_alert_rules),
        "alert_on_bull": bool(alert_on_bull),
        "alert_on_bear": bool(alert_on_bear),
        "alert_webhook_url": str(alert_webhook_url).strip(),
        "backtest_slippage_bps": float(backtest_slippage_bps),
        "backtest_fee_bps": float(backtest_fee_bps),
        "backtest_stop_loss_pct": float(backtest_stop_loss_pct),
        "backtest_take_profit_pct": float(backtest_take_profit_pct),
        "backtest_min_hold_bars": int(backtest_min_hold_bars),
        "backtest_max_hold_bars": int(backtest_max_hold_bars),
    }
