"""
NEUROBRO SCALPING SYSTEM V4.1 — OPSI B ONLY
Order-flow style entry: masuk di support area + konfirmasi manual
Tidak ada entry di atas harga close terakhir
"""

import yfinance as yf
import pandas as pd
import numpy as np
import warnings
import hashlib
from datetime import datetime

warnings.filterwarnings('ignore')

# ============================================================
# KONFIGURASI
# ============================================================
MIN_PRICE = 50
MAX_PRICE = 500
MIN_VOLUME_AVG_RATIO = 1.2
RSI_MIN = 40
RSI_MAX = 70
MIN_SCORE = 85
LOOKBACK_DAYS = 365
TICKER_FILE = "tickers_idx.txt"
MIN_AVG_VOLUME = 1_000_000          # minimal average volume 1M shares/hari - filter likuiditas jelek
MIN_AVG_VALUE_TRADED = 1_000_000_000  # minimal average nilai transaksi 1M/hari - 1 Miliar Rupiah
FREE_FLOAT_FILE = "free_float_idx.csv"
MIN_FREE_FLOAT_PCT = 15            # minimal free float percentage
MAX_FREE_FLOAT_PCT = 50            # maksimal free float percentage (avoid too liquid stocks)
MIN_MARKET_CAP = 100_000_000_000   # minimal market cap 500B Rupiah
MIN_VAL_TODAY = 500_000_000  # Rp2 Miliar
MIN_AVG_VAL = 100_000_000    # Rp1 Miliar (mencegah saham yang cuma rame 1 hari / one-day spike)


def load_tickers(ticker_file: str = None) -> list:
    """
    Load ticker list dari file.

    Args:
        ticker_file: Path ke file ticker (default TICKER_FILE)

    Returns:
        List of ticker codes dengan .JK suffix
    """
    if ticker_file is None:
        ticker_file = TICKER_FILE

    with open(ticker_file) as f:
        codes = [line.strip() for line in f if line.strip()]
    return [f"{code}.JK" for code in codes]


def load_free_float(free_float_file: str = None) -> dict:
    """
    Baca cache free_float_idx.csv (dari update_free_float.py).
    Return dict {kode_saham: persen_free_float}. Kalau file belum ada,
    return dict kosong (semua saham diperlakukan 'data gak tersedia').

    Args:
        free_float_file: Path ke file free float CSV (default FREE_FLOAT_FILE)

    Returns:
        Dict mapping ticker code ke free float percentage
    """
    import os

    if free_float_file is None:
        free_float_file = FREE_FLOAT_FILE

    if not os.path.exists(free_float_file):
        print(f"[WARN] {free_float_file} belum ada - free float filter tidak aktif")
        return {}

    free_float_map = {}
    with open(free_float_file) as f:
        next(f)  # skip header
        for line in f:
            parts = line.strip().split(",")
            if len(parts) == 2 and parts[1]:
                try:
                    free_float_map[parts[0]] = float(parts[1])
                except ValueError:
                    continue
    return free_float_map


def fetch_data(ticker, period='1y', trading_style='daytrade'):
    """
    Fetch data dengan 1 tahun data untuk semua style.
    - daytrade: EMA 9/21, konfirmasi EMA 50
    - swing: SMA 20/50, konfirmasi SMA 100
    - long: SMA 50/200
    - custom: SMA 9/21, konfirmasi SMA 50
    """
    try:
        stock = yf.Ticker(ticker)
        df = stock.history(period=period)
        if df.empty or len(df) < 30:
            return None, None
        df.columns = [c.lower() for c in df.columns]
        return df, stock
    except:
        return None, None


def calc_sma(series, window):
    return series.rolling(window=window).mean()


def calc_ema(series, window):
    return series.ewm(span=window, adjust=False).mean()


def calc_rsi(series, window=14):
    """
    Hitung RSI menggunakan Wilder's Smoothing Method (seperti TradingView).
    RSI = 100 - (100 / (1 + RS))
    RS = Average Gain / Average Loss
    """
    delta = series.diff()
    gain = delta.where(delta > 0, 0.0)
    loss = (-delta.where(delta < 0, 0.0))

    # Wilder's Smoothing (EMA-like) - sama dengan TradingView default
    avg_gain = gain.ewm(alpha=1/window, adjust=False).mean()
    avg_loss = loss.ewm(alpha=1/window, adjust=False).mean()

    rs = avg_gain / avg_loss
    return 100 - (100 / (1 + rs))


def check_higher_lows(df, lookback=10):
    if len(df) < lookback + 5:
        return False
    recent = df.tail(lookback)
    low = recent['low'].values
    first_half_min = np.min(low[:lookback // 2])
    second_half_min = np.min(low[lookback // 2:])
    return second_half_min > first_half_min


def get_support_resistance(df):
    recent = df.tail(20)
    resistance = recent['high'].max()
    support = recent['low'].min()

    close = df['close']
    ema50 = calc_ema(close, 50)

    if not ema50.isna().all() and not pd.isna(ema50.iloc[-1]):
        ema50_val = ema50.iloc[-1]
        if support <= ema50_val <= resistance:
            support = max(support, ema50_val)

    return float(support), float(resistance)


def check_ara_lock(df, price):
    """
    Detect ARA (Auto Rejection Atas) lock.
    ARA terjadi kalo:
    1. close == high (harga tutup di tertinggi hari itu)
    2. % change >= 20% (modified threshold untuk lebih sensitive)

    Return tuple: (is_ara, ara_type, pct_change)
    ara_type: 'ARA_20', 'ARA_20', 'ARA_35', 'ARA_50', atau None
    """
    if len(df) < 2:
        return False, None, 0.0

    last_close = float(df['close'].iloc[-1])
    prev_close = float(df['close'].iloc[-2])

    if prev_close == 0:
        return False, None, 0.0

    pct_change = ((last_close - prev_close) / prev_close) * 100

    # Cek apakah close di high (ARA lock characteristic)
    last_high = float(df['high'].iloc[-1])
    is_at_high = abs(last_close - last_high) < 0.01  # Toleransi 0.01 Rupiah

    if is_at_high:
        if pct_change >= 50:
            return True, 'ARA_50', pct_change
        elif pct_change >= 35:
            return True, 'ARA_35', pct_change
        elif pct_change >= 20:
            return True, 'ARA_20', pct_change
        elif pct_change >= 15:
            return True, 'ARA_15', pct_change

    return False, None, pct_change


def get_entry_sl_tp_for_ara(price, ara_pct, atr):
    """
    Entry/SL/TP khusus untuk ARA-locked stocks.
    Strategy: Wait for pullback ke ARA area (NOT di support)
    """
    # ENTRY: Pullback ke ARA area (10-15% di bawah harga sekarang)
    entry_low = round(price * 0.88, 0)  # 12% di bawah
    entry_high = round(price * 0.92, 0)  # 8% di bawah

    # SL: Wider karena volatility tinggi (15-18% di bawah entry)
    sl = round(entry_low - (price * 0.15), 0)

    # TP: Measured move (pasangkan dengan range harian)
    daily_range = atr * 1.5  # Estimasi range harian
    tp1 = round(price + daily_range, 0)
    tp2 = round(price + (daily_range * 1.5), 0)

    return entry_low, entry_high, sl, tp1, tp2


def calc_atr(df, window=14):
    high, low, close = df['high'], df['low'], df['close']
    tr1 = high - low
    tr2 = (high - close.shift()).abs()
    tr3 = (low - close.shift()).abs()
    tr = pd.concat([tr1, tr2, tr3], axis=1).max(axis=1)
    return float(tr.tail(window).mean())


def get_entry_sl_tp_official_v41(price, support, resistance, atr):
    """
    V5 (ORDER-FLOW STYLE) WAJIB:
    Entry di support area + konfirmasi manual
    Nggak pernah entry di atas harga close terakhir

    1. Entry low = max(support, price * 0.98) - nggak lebih dari 2% di bawah close
    2. Entry high = min(price, support + atr) - nggak lebih dari harga saat ini
    3. SL = price * 0.92 (capped -8%) atau support * 0.98 (ambil yang tertinggi)
    4. TP1 = resistance (atau price + atr*1.5 kalo resistance terlalu jauh)
    5. TP2 = resistance + atr (kalo ada ruang)
    """
    # VALIDASI AWAL: Support harus <= price untuk order-flow style
    # Kalau support > price, harga di bawah support → tidak layak
    adjusted_support = min(support, price)

    # ENTRY — OPSI B
    entry_low = round(max(adjusted_support, price * 0.98), 0)
    entry_high = round(min(price, adjusted_support + atr), 0)

    # Kalo entry_low > entry_high, paksa manual
    if entry_low > entry_high:
        # Pastikan entry_low < entry_high dengan SWAP
        temp_low = round(adjusted_support, 0)
        temp_high = round(min(price, adjusted_support + atr * 0.5), 0)

        # SWAP - pastikan entry_low < entry_high
        if temp_low > temp_high:
            entry_low, entry_high = temp_high, temp_low
        else:
            entry_low, entry_high = temp_low, temp_high
    elif entry_low < entry_high:
        # Values are already correct, preserve them
        pass

    # FINAL VALIDASI: Entry tidak boleh lebih tinggi dari harga sekarang
    if entry_high > price:
        # Paksa entry_high <= price (wajib order-flow style!)
        entry_high = round(price, 0)
        # Kalau entry_low juga > price, fix juga
        if entry_low > price:
            entry_low = round(price * 0.98, 0)

    # STOP LOSS - pakai yang paling masuk akal untuk order-flow entry
    sl_support_based = adjusted_support * 0.98  # SL sedikit di bawah support
    sl_atr_based = adjusted_support - (atr * 0.5)  # SL berdasarkan ATR
    sl_capped = price * 0.92  # SL capped -8% dari harga sekarang

    # Pilih SL yang paling reasonable untuk entry zone
    # Kalo entry di support area, SL harus di bawah support
    sl = round(min(sl_support_based, sl_atr_based, sl_capped), 0)

    # Pastikan SL di bawah entry_low
    if sl >= entry_low:
        sl = round(entry_low - (atr * 0.3), 0)  # SL 30% ATR di bawah entry

    # TAKE PROFIT
    tp1 = round(min(resistance, price + atr * 1.5), 0)
    tp2 = round(resistance + atr, 0)

    # TP adjustment: kalo harga > resistance, pake ATR-based
    if price > resistance:
        tp1 = round(price + atr * 0.8, 0)
        tp2 = round(price + atr * 1.5, 0)

    # VALIDASI PROFIT POTENTIAL
    # Pastikan TP1 > entry_high (minimal ada profit)
    min_profit_pct = 0.02  # Minimal 2% profit
    min_tp1 = entry_high * (1 + min_profit_pct)

    if tp1 <= min_tp1:
        # Adjust TP1 kalau terlalu dekat dengan entry
        tp1 = round(min_tp1, 0)

    # Pastikan TP2 > TP1 (ada tiered profit)
    if tp2 <= tp1:
        # Adjust TP2 kalau tidak lebih tinggi dari TP1
        tp2 = round(tp1 * 1.05, 0)  # Minimal 5% di atas TP1

    return entry_low, entry_high, sl, tp1, tp2


def analyze_stock(ticker, free_float_map=None, is_single_mode=False, config=None):
    # Use provided config or defaults to constants
    if config is None:
        config = {
            'min_price': MIN_PRICE,
            'max_price': MAX_PRICE,
            'min_free_float_pct': MIN_FREE_FLOAT_PCT,
            'max_free_float_pct': MAX_FREE_FLOAT_PCT,
            'trading_style': 'daytrade',
            'min_market_cap': MIN_MARKET_CAP,
            'min_val_today': MIN_VAL_TODAY,
            'min_avg_val': MIN_AVG_VAL,
            'min_avg_volume': MIN_AVG_VOLUME
        }

    # Get trading style config - harus sebelum fetch_data
    trading_style = config.get('trading_style', 'daytrade')

    trading_styles = {
        'daytrade': {'ma_fast': 9, 'ma_slow': 21, 'ma_confirm': 50, 'ma_type': 'EMA', 'period': '1y'},
        'swing': {'ma_fast': 20, 'ma_slow': 50, 'ma_confirm': 100, 'ma_type': 'SMA', 'period': '1y'},
        'long': {'ma_fast': 50, 'ma_slow': 200, 'ma_confirm': None, 'ma_type': 'SMA', 'period': '1y'},
        'custom': {'ma_fast': 9, 'ma_slow': 21, 'ma_confirm': 50, 'ma_type': 'SMA', 'period': '1y'}
    }

    style_config = trading_styles.get(trading_style, trading_styles['daytrade'])
    ma_fast = style_config['ma_fast']
    ma_slow = style_config['ma_slow']
    ma_confirm = style_config['ma_confirm']
    ma_type = style_config['ma_type']
    data_period = style_config['period']

    # Fetch data dengan period berdasarkan trading style
    df, stock = fetch_data(ticker, period=data_period, trading_style=trading_style)
    if df is None:
        return None

    # Extract ticker symbol without .JK suffix for free_float lookup
    ticker_symbol = ticker.replace('.JK', '')

    close = df['close']
    volume = df['volume']
    high = df['high']
    low = df['low']

    last_close = float(close.iloc[-1])
    last_volume = int(volume.iloc[-1])

    # ARA DETECTION - lakukan lebih awal untuk bypass filter
    is_ara, ara_type, ara_pct = check_ara_lock(df, last_close)

    # Filter harga - use config values
    min_price = config.get('min_price', MIN_PRICE)
    max_price = config.get('max_price', MAX_PRICE)

    if last_close < min_price or last_close > max_price:
        return None

    # Volume analysis — 5-day average (bukan filter, tapi indikator scoring)
    avg_vol_5 = float(volume.tail(5).mean())
    vol_vs_avg = last_volume / avg_vol_5 if avg_vol_5 > 0 else 0
    failed_filter = None

    # --- HARD FILTER: likuiditas (rata-rata volume harian) ---
    # BYPASS untuk ARA stocks
    min_avg_volume = config.get('min_avg_volume', MIN_AVG_VOLUME)
    avg_vol_daily = float(volume.tail(20).mean())
    if avg_vol_daily < min_avg_volume and not is_ara:
        failed_filter = f'Avg vol {avg_vol_daily/1_000_000:.1f}M < {min_avg_volume/1_000_000:.1f}M'
        if not is_single_mode:
            return None  # Skip - likuiditas terlalu rendah, rawan nyangkuk

    # --- HARD FILTER: nilai transaksi (value traded harian) ---
    # Hitung value traded = volume × close untuk setiap hari
    df['value_traded'] = df['close'] * df['volume']
    # 1. Gunakan Typical Price (High + Low + Close / 3) agar perkalian nilainya mendekati Nilai Transaksi Asli (Val)
    # df['typical_price'] = (df['high'] + df['low'] + df['close']) / 3
    # df['value_traded'] = df['typical_price'] * df['volume']
    val_today = float(df['value_traded'].iloc[-1])
    avg_value_traded = float(df['value_traded'].tail(20).mean())

    # Use config values
    min_val_today = config.get('min_val_today', MIN_VAL_TODAY)
    min_avg_val = config.get('min_avg_val', MIN_AVG_VAL)

    # BYPASS untuk ARA stocks
    if (val_today < min_val_today or avg_value_traded < min_avg_val) and not is_ara:
        val_today_b = val_today / 1_000_000_000
        avg_20d_b = avg_value_traded / 1_000_000_000

        failed_filter = f'Likuiditas Rendah: Today Rp{val_today_b:.2f}B, Avg20D Rp{avg_20d_b:.2f}B'

        if not is_single_mode:
            return None  # Skip saham tidak likuid
    # avg_value_traded = float(df['value_traded'].tail(20).mean())
    # if avg_value_traded < MIN_AVG_VALUE_TRADED:
    #     failed_filter = f'Avg value Rp{avg_value_traded/1_000_000_000:.1f}B < Rp{MIN_AVG_VALUE_TRADED/1_000_000_000:.1f}B {df['close']} {df['volume']}'
    #     if not is_single_mode:
    #         return None  # Skip - nilai transaksi terlalu kecil, tidak likuid

    # --- HARD FILTER: free float ---
    min_free_float_pct = config.get('min_free_float_pct', MIN_FREE_FLOAT_PCT)
    max_free_float_pct = config.get('max_free_float_pct', MAX_FREE_FLOAT_PCT)

    free_float_pct = None
    if free_float_map is not None and ticker_symbol in free_float_map:
        free_float_pct = free_float_map[ticker_symbol]
        if free_float_pct < min_free_float_pct:
            failed_filter = f'Free float {free_float_pct}% < {min_free_float_pct}%'
            if not is_single_mode:
                return None  # Skip - free float terlalu rendah
        elif free_float_pct > max_free_float_pct:
            failed_filter = f'Free float {free_float_pct}% > {max_free_float_pct}%'
            if not is_single_mode:
                return None  # Skip - free float terlalu tinggi (too liquid)

    # --- HARD FILTER: market cap ---
    min_market_cap = config.get('min_market_cap', MIN_MARKET_CAP)
    market_cap = None
    try:
        if stock and 'marketCap' in stock.info:
            market_cap = stock.info['marketCap']
            if market_cap and market_cap < min_market_cap:
                failed_filter = f'Market cap Rp{market_cap/1_000_000_000:.0f}B < Rp{min_market_cap/1_000_000_000:.0f}B'
                if not is_single_mode:
                    return None  # Skip - market cap terlalu kecil
    except:
        pass  # Market cap data tidak tersedia, lanjut saja

    # ATR
    atr = calc_atr(df)

    # --- HARD FILTER: Price Range (Volatility Check) ---
    min_price_range = config.get('min_price_range', 5)  # Default min 5 points range
    avg_price_range = None
    try:
        # Calculate average (high - low) from recent 20 days
        recent_df = df.tail(20)
        price_ranges = recent_df['high'] - recent_df['low']
        avg_price_range = price_ranges.mean()

        if avg_price_range < min_price_range:
            failed_filter = f'Price range {avg_price_range:.1f} < {min_price_range} (kurang volatil)'
            if not is_single_mode:
                return None  # Skip - price range terlalu kecil
    except:
        pass  # Price range calculation gagal, lanjut saja

    # Calculate MAs based on trading style
    if ma_type == 'EMA':
        ma_fast_line = calc_ema(close, ma_fast)
        ma_slow_line = calc_ema(close, ma_slow)
    else:  # SMA
        ma_fast_line = calc_sma(close, ma_fast)
        ma_slow_line = calc_sma(close, ma_slow)

    # MA cross detection
    ma_cross = None
    golden_cross_days_ago = None  # How many days ago golden cross occurred

    if not ma_fast_line.isna().all() and not ma_slow_line.isna().all():
        if not pd.isna(ma_fast_line.iloc[-1]) and not pd.isna(ma_slow_line.iloc[-1]):
            ma_cross = 'golden' if ma_fast_line.iloc[-1] > ma_slow_line.iloc[-1] else 'death'

            # Calculate how many days ago golden cross occurred
            if ma_cross == 'golden':
                # Look backwards to find when the cross happened
                for i in range(len(ma_fast_line) - 2, max(0, len(ma_fast_line) - 61), -1):  # Check last 60 days
                    if not pd.isna(ma_fast_line.iloc[i]) and not pd.isna(ma_slow_line.iloc[i]):
                        if ma_fast_line.iloc[i] <= ma_slow_line.iloc[i]:
                            # Found the cross point - it happened between i and i+1
                            golden_cross_days_ago = len(ma_fast_line) - i - 1
                            break

    # Confirmation MA
    ma_confirm_val = None
    ma_confirm_trend = None
    if ma_confirm:
        if ma_type == 'EMA':
            ma_confirm_line = calc_ema(close, ma_confirm)
        else:  # SMA
            ma_confirm_line = calc_sma(close, ma_confirm)

        if not ma_confirm_line.isna().all() and not pd.isna(ma_confirm_line.iloc[-1]):
            ma_confirm_val = float(ma_confirm_line.iloc[-1])
            ma_confirm_trend = 'bullish' if last_close > ma_confirm_val else 'bearish'

    # Keep legacy SMA50 for compatibility
    sma50 = calc_sma(close, 50)
    sma50_val = None
    sma50_trend = None
    if not sma50.isna().all() and not pd.isna(sma50.iloc[-1]):
        sma50_val = float(sma50.iloc[-1])
        sma50_trend = 'bullish' if last_close > sma50_val else 'bearish'

    # Legacy EMA calculations for compatibility
    ema9 = calc_ema(close, 9)
    ema21 = calc_ema(close, 21)
    ema_cross_legacy = None
    if not ema9.isna().all() and not ema21.isna().all():
        if not pd.isna(ema9.iloc[-1]) and not pd.isna(ema21.iloc[-1]):
            ema_cross_legacy = 'golden' if ema9.iloc[-1] > ema21.iloc[-1] else 'death'

    rsi = calc_rsi(close)
    rsi_val = 50.0
    if not rsi.isna().all() and not pd.isna(rsi.iloc[-1]):
        rsi_val = float(rsi.iloc[-1])

    support, resistance = get_support_resistance(df)
    spread_pct = (resistance - support) / last_close if last_close > 0 else 99
    hl_bool = check_higher_lows(df)

    # ARA DETECTION sudah dilakukan di awal function (sebelum filters)

    # --- SCORING (MAX 100) ---
    score = 0
    details = []

    # 1. MA Confirmation (20 pts) - based on trading style
    ma_name_confirm = f'{ma_type}{ma_confirm}' if ma_confirm else 'N/A'
    if ma_confirm_trend == 'bullish':
        score += 20
        details.append(f'{ma_name_confirm}: BULLISH (+20)')
    elif ma_confirm_trend == 'bearish':
        details.append(f'{ma_name_confirm}: BEARISH (+0)')
    else:
        details.append(f'{ma_name_confirm}: N/A (+0)')

    # 2. MA Fast/Slow Cross (15 pts) - based on trading style
    ma_name_cross = f'{ma_type}{ma_fast}/{ma_slow}'
    if ma_cross == 'golden':
        score += 15
        details.append(f'{ma_name_cross}: GOLDEN CROSS (+15)')
    elif ma_cross == 'death':
        details.append(f'{ma_name_cross}: DEATH CROSS (+0)')
    else:
        details.append(f'{ma_name_cross}: N/A (+0)')

    # 3. Volume (20 pts)
    if vol_vs_avg >= 5:
        score += 20
        details.append(f'VOLUME: {vol_vs_avg:.1f}x — MASSIVE (+20)')
    elif vol_vs_avg >= 3:
        score += 15
        details.append(f'VOLUME: {vol_vs_avg:.1f}x — STRONG (+15)')
    elif vol_vs_avg >= 1.5:
        score += 10
        details.append(f'VOLUME: {vol_vs_avg:.1f}x — MODERATE (+10)')
    else:
        details.append(f'VOLUME: {vol_vs_avg:.1f}x — THIN (+0)')

    # 4. RSI (25 pts) + PENALTI
    if RSI_MIN <= rsi_val <= RSI_MAX:
        score += 25
        details.append(f'RSI: {rsi_val:.1f} — SWEET SPOT (+25)')
    elif 30 <= rsi_val < RSI_MIN:
        score += 10
        details.append(f'RSI: {rsi_val:.1f} — UNDERBOUGHT (+10)')
    elif RSI_MAX < rsi_val <= 75:
        score += 5
        details.append(f'RSI: {rsi_val:.1f} — WARM (+5)')
    elif rsi_val > 75:
        score -= 15
        details.append(f'RSI: {rsi_val:.1f} — OVERBOUGHT! (-15)')
    elif rsi_val < 30:
        score -= 10
        details.append(f'RSI: {rsi_val:.1f} — OVERSOLD EXTREME (-10)')
    else:
        details.append(f'RSI: {rsi_val:.1f} (+0)')

    # 5. Price action (10 pts)
    if hl_bool:
        score += 10
        details.append(f'HIGHER LOW (+10)')
    else:
        details.append(f'NO HIGHER LOW (+0)')

    # 6. S/R spread (10 pts)
    if spread_pct <= 0.08:
        score += 10
        details.append(f'SPREAD: {spread_pct:.1%} — TIGHT (+10)')
    elif spread_pct <= 0.15:
        score += 5
        details.append(f'SPREAD: {spread_pct:.1%} — MODERATE (+5)')
    else:
        details.append(f'SPREAD: {spread_pct:.1%} — WIDE (+0)')

    # --- ENTRY, SL, TP — OPSI B (ORDER-FLOW STYLE) atau ARA MODIFIED ---
    if is_ara:
        # Gunakan ARA-specific entry calculation
        entry_low, entry_high, sl, tp1, tp2 = get_entry_sl_tp_for_ara(
            last_close, ara_pct, atr
        )
    else:
        # Gunakan regular order-flow style entry
        entry_low, entry_high, sl, tp1, tp2 = get_entry_sl_tp_official_v41(
            last_close, support, resistance, atr
        )

    # Risk-Reward (pakai entry_low sebagai acuan entry)
    risk = entry_low - sl if entry_low > sl else 1
    reward = tp1 - entry_low
    rr = round(reward / risk, 2) if risk > 0 else 0

    return {
        'ticker': ticker.replace('.JK', ''),
        'price': round(last_close, 0),
        'volume': last_volume,
        'vol_vs_avg': round(vol_vs_avg, 1),
        'rsi': round(rsi_val, 1),
        'atr': round(atr, 1),
        'score': score,
        'details': details,
        'support': round(support, 0),
        'resistance': round(resistance, 0),
        'sma50_val': round(sma50_val, 0) if sma50_val else None,  # SMA50 value untuk cross-check
        'entry_low': entry_low,
        'entry_high': entry_high,
        'sl': sl,
        'tp1': tp1,
        'tp2': tp2,
        'rr': rr,
        'free_float_pct': free_float_pct,
        'market_cap': market_cap,
        'avg_value_traded': avg_value_traded,
        'avg_price_range': round(avg_price_range, 1) if avg_price_range else None,  # Average (high-low) range
        'golden_cross_days_ago': golden_cross_days_ago,  # Days since golden cross occurred
        'failed_filter': failed_filter,  # Menandakan filter yang gagal (untuk single mode)
        'is_ara': is_ara,  # ARA lock flag
        'ara_type': ara_type,  # 'ARA_35', 'ARA_50', atau None
        'ara_pct': round(ara_pct, 1),  # Persentase change
    }


def save_results_to_markdown(results, qualified, is_single_mode=False):
    """
    Simpan hasil screening ke file markdown dengan format screening-{tanggal}.md
    """
    from pathlib import Path

    # Generate filename dengan tanggal hari ini
    tanggal = datetime.now().strftime("%Y-%m-%d")
    filename = f"screening-{tanggal}.md"

    # Hitung total saham yang dianalisis
    total_analyzed = len(results)
    total_qualified = len(qualified)

    # Buat content markdown
    content = []
    content.append(f"# 📊 NEUROBRO SCALPING SYSTEM V5.1 — HASIL SCREENING")
    content.append(f"\n**🕐 Waktu:** {datetime.now().strftime('%Y-%m-%d %H:%M')} WIB")
    content.append(f"**📈 Total Dianalisis:** {total_analyzed} saham")
    content.append(f"**✅ Total Lolos:** {total_qualified} saham")
    content.append(f"**❌ Filtered:** {total_analyzed - total_qualified} saham")
    content.append(f"**🎯 Mode:** {'Single Stock' if is_single_mode else 'Batch Screening'}")
    content.append("")
    content.append("---")
    content.append("")

    # KONFIGURASI YANG DIGUNAKAN
    content.append("## ⚙️ KONFIGURASI")
    content.append(f"- **Harga Range:** Rp{MIN_PRICE} - Rp{MAX_PRICE}")
    content.append(f"- **Volume Ratio:** ≥ {MIN_VOLUME_AVG_RATIO}x avg")
    content.append(f"- **RSI Range:** {RSI_MIN} - {RSI_MAX}")
    content.append(f"- **Min Avg Volume:** {MIN_AVG_VOLUME/1_000_000:.1f}M shares/hari")
    content.append(f"- **Min Avg Value:** Rp{MIN_AVG_VAL/1_000_000_000:.1f}B/hari")
    content.append(f"- **Min Free Float:** {MIN_FREE_FLOAT_PCT}% — **Max Free Float:** {MAX_FREE_FLOAT_PCT}%")
    content.append(f"- **Min Market Cap:** Rp{MIN_MARKET_CAP/1_000_000_000:.0f}B")
    content.append(f"- **Min Score:** {MIN_SCORE}/100")
    content.append("")

    if not qualified:
        content.append("## ❌ TIDAK ADA SAHAM YANG LOLOS")
        content.append("")
        content.append("### 📊 Top 5 Terdekat:")
        content.append("")
        content.append("| TICKER | PRICE |     ENTRY    |   SL  |  TP1  |  R:R | SCORE |  RSI  |")
        content.append("|--------|-------|--------------|-------|-------|------|-------|-------|")

        for r in results[:5]:
            entry = f'{r["entry_low"]:,.0f}-{r["entry_high"]:,.0f}'
            content.append(f"| ${r['ticker']:<5s} | {r['price']:>6.0f} | {entry:>13s} | {r['sl']:>6.0f} | {r['tp1']:>6.0f} | {r['rr']:>5.2f} | {r['score']:>6d} | {r['rsi']:>6.1f} |")
    else:
        # SUMMARY TABLE - QUALIFIED
        content.append("## ✅ SAHAM YANG LOLOS SCREENING")
        content.append("")
        content.append("| TICKER | PRICE |     ENTRY    |   SL  |  TP1  |  TP2  |  R:R | SCORE | VOL/MA |  VALUE  |  RSI  |")
        content.append("|--------|-------|--------------|-------|-------|-------|------|-------|--------|---------|-------|")

        for r in qualified:
            vol_str = f'{r["vol_vs_avg"]:>5.1f}x'
            val_str = f'Rp{r["avg_value_traded"]/1_000_000_000:.1f}B'
            entry = f'{r["entry_low"]:,.0f}-{r["entry_high"]:,.0f}'
            content.append(f"| ${r['ticker']:<5s} | {r['price']:>6.0f} | {entry:>13s} | {r['sl']:>6.0f} | {r['tp1']:>6.0f} | {r['tp2']:>6.0f} | {r['rr']:>5.2f} | {r['score']:>6d} | {vol_str:>7s} | {val_str:>8s} | {r['rsi']:>6.1f} |")

        content.append("")

        # DETAIL ANALYSIS
        content.append("## 📋 DETAIL ANALYSIS")
        content.append("")

        display_list = results if is_single_mode else qualified[:3]
        for r in display_list:
            entry_zone = f'Rp{r["entry_low"]:,.0f} - Rp{r["entry_high"]:,.0f}'
            val_info = f'Rp{r["avg_value_traded"]/1_000_000_000:.1f}B/hari'

            content.append(f"### ${r['ticker']} — Rp{r['price']:,.0f} | Score: {r['score']}/100")
            content.append(f"**Liquidity:** {val_info} | **Volume vs Avg:** {r['vol_vs_avg']:.1f}x")
            content.append(f"**Setup:** Entry `{entry_zone}` | SL: `Rp{r['sl']:,.0f}` | TP1: `Rp{r['tp1']:,.0f}` | TP2: `Rp{r['tp2']:,.0f}`")
            content.append(f"**Risk/Reward:** {r['rr']}:1")
            content.append("")
            content.append("**Score Breakdown:**")
            for det in r['details']:
                content.append(f"- {det}")
            content.append("")

        # GOLDEN CROSS ONLY - Saham dengan momentum awal tapi belum lolos full filter
        if not is_single_mode:
            golden_only = [r for r in results if r not in qualified and any('GOLDEN CROSS' in det for det in r.get('details', []))]

            if golden_only:
                content.append("## 🟡 GOLDEN CROSS ONLY — Momentum Awal")
                content.append("")
                content.append("> Saham dengan EMA9/21 Golden Cross tapi belum lolos full filter.")
                content.append("> Potensi momentum awal, tapi perlu konfirmasi tambahan.")
                content.append("")
                content.append("| TICKER | PRICE |     ENTRY    |   SL  |  TP1  |  R:R | SCORE | VOL/MA |  RSI  |")
                content.append("|--------|-------|--------------|-------|-------|------|-------|--------|-------|")

                for r in golden_only[:10]:  # Top 10 saja
                    entry = f'{r["entry_low"]:,.0f}-{r["entry_high"]:,.0f}'
                    vol_str = f'{r["vol_vs_avg"]:>5.1f}x'
                    content.append(f"| ${r['ticker']:<5s} | {r['price']:>6.0f} | {entry:>13s} | {r['sl']:>6.0f} | {r['tp1']:>6.0f} | {r['rr']:>5.2f} | {r['score']:>6d} | {vol_str:>7s} | {r['rsi']:>6.1f} |")

                content.append("")
                content.append("**Catatan:** Saham di atas sudah menunjukkan golden cross (EMA9 > EMA21) tapi belum memenuhi semua kriteria筛选. Perlu konfirmasi tambahan seperti volume, RSI, atau breakout dari resistance.")
                content.append("")

        # ARA LOCKED - Saham yang sedang lock ARA (termasuk yang tidak qualified)
        ara_stocks = [r for r in results if r.get('is_ara', False)]

        if ara_stocks:
            content.append("## 🔥 ARA LOCKED — Momentum Play")
            content.append("")
            content.append("> Saham yang sedang lock ARA (Auto Rejection Atas).")
            content.append("> Entry saat pullback ke ARA area, BUKAN di support. Wider SL karena volatility tinggi.")
            content.append("")
            content.append("| TICKER | PRICE |     ENTRY (Pullback)   |   SL  |  TP1  |  TP2  |  R:R | SCORE | VOL/MA | ARA % |  RSI  |")
            content.append("|--------|-------|-------------------------|-------|-------|-------|------|-------|--------|-------|-------|")

            for r in ara_stocks[:10]:  # Top 10 ARA stocks
                entry = f'{r["entry_low"]:,.0f}-{r["entry_high"]:,.0f}'
                vol_str = f'{r["vol_vs_avg"]:>5.1f}x'
                ara_pct_str = f'+{r["ara_pct"]:.1f}%'
                content.append(f"| ${r['ticker']:<5s} | {r['price']:>6.0f} | {entry:>24s} | {r['sl']:>6.0f} | {r['tp1']:>6.0f} | {r['tp2']:>6.0f} | {r['rr']:>5.2f} | {r['score']:>6d} | {vol_str:>7s} | {ara_pct_str:>6s} | {r['rsi']:>6.1f} |")

            content.append("")
            content.append("**Catatan:** Entry untuk ARA stocks adalah saat pullback ke area tersebut (10-15% di bawah harga sekarang). SL lebih lebar (15-18%) karena volatility tinggi. TP berdasarkan measured move dari daily range ARA.")
            content.append("")

    # FOOTER
    content.append("---")
    content.append("")
    content.append(f"*Generated by NEUROBRO V5.1 — {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} WIB*")
    content.append("")

    # Write ke file
    filepath = Path(filename)
    with open(filepath, 'w', encoding='utf-8') as f:
        f.write('\n'.join(content))

    print(f"\n[INFO] Hasil disimpan ke: {filename}")
    return str(filepath)


def run_screener(tickers=None):
    if tickers is None:
        tickers = load_tickers()

    free_float_map = load_free_float()

    # Check if single ticker mode
    is_single_mode = len(tickers) == 1

    print(f'{"="*70}')
    print(f'  Analyze SYSTEM V5.1 — ORDER-FLOW STYLE')
    print(f'  {datetime.now().strftime("%Y-%m-%d %H:%M")} WIB')
    print(f'{"="*70}')

    if is_single_mode:
        print(f'  SINGLE STOCK MODE: {tickers[0]}')
    else:
        print(f'  BATCH MODE: {len(tickers)} saham')

    print(f'  Rp{MIN_PRICE}-{MAX_PRICE} | Vol >= {MIN_VOLUME_AVG_RATIO}x avg | RSI {RSI_MIN}-{RSI_MAX}')
    print(f'  Min Avg Volume: {MIN_AVG_VOLUME/1_000_000:.1f}M shares/hari | Min score: {MIN_SCORE}')
    print(f'  Min Avg Value: {MIN_AVG_VALUE_TRADED/1_000_000_000:.1f}B Rupiah/hari')
    print(f'  Min Free Float: {MIN_FREE_FLOAT_PCT}% — Max Free Float: {MAX_FREE_FLOAT_PCT}% | Min Market Cap: {MIN_MARKET_CAP/1_000_000_000:.0f}B')
    print(f'  Entry: Support area + konfirmasi manual (Opsi B)')
    print(f'  SL: Capped -8% | TP: Resistance / ATR-based')
    print(f'{"="*70}\n')

    results = []

    for i, ticker in enumerate(tickers, 1):
        print(f'  [{i:2d}/{len(tickers)}] {ticker:12s} ', end='', flush=True)
        result = analyze_stock(ticker, free_float_map, is_single_mode)
        if result:
            results.append(result)
            bar_len = 20
            filled = int(result['score'] / 100 * bar_len)
            bar = '█' * filled + '░' * (bar_len - filled)

            # Show failed filter in single mode
            if result.get('failed_filter'):
                print(f'GAGAL — {result["failed_filter"]} ❌')
            elif result['score'] >= MIN_SCORE:
                print(f'PASS — SCORE {result["score"]:2d}/100 {bar}')
            else:
                print(f'PASS — SCORE {result["score"]:2d}/100 {bar}')
        else:
            print(f'GAGAL ❌')

    results.sort(key=lambda x: x['score'], reverse=True)
    qualified = [r for r in results if r['score'] >= MIN_SCORE]

    print(f'\n{"="*70}')
    print(f'  HASIL SCREENING — {len(qualified)}/{len(tickers)} lolos')
    print(f'{"="*70}\n')

    if not qualified and is_single_mode and results:
        # Single ticker mode: tampilkan detail breakdown walaupun tidak lolos
        r = results[0]  # Ambil satu-satunya result
        entry_zone = f'Rp{r["entry_low"]:,.0f} - Rp{r["entry_high"]:,.0f}'
        val_info = f'Avg Value: Rp{r["avg_value_traded"]/1_000_000_000:.1f}B/hari'

        print(f'  ${r["ticker"]} — Rp{r["price"]:,.0f} | Score: {r["score"]}/100')

        # Tampilkan ARA flag jika ada
        if r.get('is_ara', False):
            ara_pct = r.get('ara_pct', 0)
            print(f'  🔥 ARA LOCKED +{ara_pct:.1f}%')

        print(f'  {val_info}')
        # Tampilkan SMA50 value untuk cross-check dengan TradingView
        if r.get('sma50_val'):
            print(f'  📊 SMA50: Rp{r["sma50_val"]:,.0f} (TradingView check)')
        # Tampilkan failed filter jika ada
        if r.get('failed_filter'):
            print(f'  ❌ GAGAL: {r["failed_filter"]}')
        print(f'  Entry: {entry_zone} | SL: Rp{r["sl"]:,.0f} | TP1: Rp{r["tp1"]:,.0f} | TP2: Rp{r["tp2"]:,.0f}')
        print(f'  R:R: {r["rr"]}:1')
        print(f'  Breakdown:')
        for det in r['details']:
            print(f'    - {det}')
        print()

        # Show ARA section in single mode
        ara_stocks = [r for r in results if r.get('is_ara', False)]
        if ara_stocks:
            print(f'{"="*70}')
            print(f'  🔥 ARA LOCKED — {len(ara_stocks)} saham (Momentum Play)')
            print(f'{"="*70}\n')
            print(f'  {"TICKER":<7s} {"PRICE":>6s} {"ARA":>6s} {"VOL":>10s} {"VOL/MA":>7s} {"RSI":>6s} {"ENTRY (Pullback)":>18s} {"SL":>6s} {"TP1":>6s} {"R:R":>5s} {"SCORE":>6s}')
            print(f'  {"-"*7} {"-"*6} {"-"*6} {"-"*10} {"-"*7} {"-"*6} {"-"*18} {"-"*6} {"-"*6} {"-"*5} {"-"*6}')

            for r in ara_stocks[:10]:
                vol_str = f'{r["volume"]:,}'
                vol_vs_str = f'{r["vol_vs_avg"]:>5.1f}x'
                ara_pct_str = f'+{r["ara_pct"]:.1f}%'
                entry = f'{r["entry_low"]:,.0f}-{r["entry_high"]:,.0f}'
                print(f'  ${r["ticker"]:<5s} {r["price"]:>5.0f} {ara_pct_str:>6s} {vol_str:>10s} {vol_vs_str:>7s} {r["rsi"]:>5.1f} {entry:>18s} {r["sl"]:>5.0f} {r["tp1"]:>5.0f} {r["rr"]:>4.2f} {r["score"]:>4d}')

            print(f'\n  📝 Entry untuk ARA: Tunggu pullback ke area tersebut (10-15% di bawah harga)')
            print(f'  📝 SL lebih lebar (15-18%) karena volatility tinggi')
            print(f'{"="*70}\n')

        return qualified

    if not qualified:
        print('  Top 5 paling dekat:')
        print(f'  {"TICKER":<7s} {"PRICE":>6s} {"RSI":>6s} {"SCORE":>6s} {"ENTRY":>12s} {"SL":>6s} {"TP1":>6s} {"R:R":>5s}')
        print(f'  {"-"*7} {"-"*6} {"-"*6} {"-"*6} {"-"*12} {"-"*6} {"-"*6} {"-"*5}')
        for r in results[:5]:
            entry = f'{r["entry_low"]:,.0f}-{r["entry_high"]:,.0f}'
            print(f'  ${r["ticker"]:<5s} {r["price"]:>5.0f} {r["rsi"]:>5.1f} {r["score"]:>4d} {entry:>12s} {r["sl"]:>5.0f} {r["tp1"]:>5.0f} {r["rr"]:>4.2f}')

        # Show ARA section even in single mode
        ara_stocks = [r for r in results if r.get('is_ara', False)]
        if ara_stocks:
            print(f'\n{"="*70}')
            print(f'  🔥 ARA LOCKED — {len(ara_stocks)} saham (Momentum Play)')
            print(f'{"="*70}\n')
            print(f'  {"TICKER":<7s} {"PRICE":>6s} {"ARA":>6s} {"VOL":>10s} {"VOL/MA":>7s} {"RSI":>6s} {"ENTRY (Pullback)":>18s} {"SL":>6s} {"TP1":>6s} {"R:R":>5s} {"SCORE":>6s}')
            print(f'  {"-"*7} {"-"*6} {"-"*6} {"-"*10} {"-"*7} {"-"*6} {"-"*18} {"-"*6} {"-"*6} {"-"*5} {"-"*6}')

            for r in ara_stocks[:10]:
                vol_str = f'{r["volume"]:,}'
                vol_vs_str = f'{r["vol_vs_avg"]:>5.1f}x'
                ara_pct_str = f'+{r["ara_pct"]:.1f}%'
                entry = f'{r["entry_low"]:,.0f}-{r["entry_high"]:,.0f}'
                print(f'  ${r["ticker"]:<5s} {r["price"]:>5.0f} {ara_pct_str:>6s} {vol_str:>10s} {vol_vs_str:>7s} {r["rsi"]:>5.1f} {entry:>18s} {r["sl"]:>5.0f} {r["tp1"]:>5.0f} {r["rr"]:>4.2f} {r["score"]:>4d}')

            print(f'\n  📝 Entry untuk ARA: Tunggu pullback ke area tersebut (10-15% di bawah harga)')
            print(f'  📝 SL lebih lebar (15-18%) karena volatility tinggi')
            print(f'{"="*70}')

        return qualified

    # HEADER TABLE — qualified
    print(f'  {"TICKER":<7s} {"PRICE":>6s} {"VOL":>10s} {"VOL/MA":>7s} {"VAL":>10s} {"RSI":>6s} {"ATR":>6s}')
    print(f'  {"ENTRY":>12s} {"SL":>6s} {"TP1":>6s} {"TP2":>6s} {"R:R":>5s} {"SCORE":>6s}')
    print(f'  {"-"*7} {"-"*6} {"-"*10} {"-"*7} {"-"*10} {"-"*6} {"-"*6} {"-"*12} {"-"*6} {"-"*6} {"-"*6} {"-"*5} {"-"*6}')

    for r in qualified:
        vol_str = f'{r["volume"]:,}'
        val_str = f'Rp{r["avg_value_traded"]/1_000_000_000:.1f}B'
        entry = f'{r["entry_low"]:,.0f}-{r["entry_high"]:,.0f}'
        print(f'  ${r["ticker"]:<5s} {r["price"]:>5.0f} {vol_str:>10s} {r["vol_vs_avg"]:>5.1f}x {val_str:>10s} {r["rsi"]:>5.1f} {r["atr"]:>5.0f} {entry:>12s} {r["sl"]:>5.0f} {r["tp1"]:>5.0f} {r["tp2"]:>5.0f} {r["rr"]:>4.2f} {r["score"]:>4d}')

    print(f'\n{"="*70}')
    if is_single_mode:
        print(f'  DETAIL ANALISA — {len(results)} saham')
    else:
        print(f'  DETAIL TOP {min(3, len(qualified))}')
    print(f'{"="*70}\n')

    # Untuk single ticker mode: tampilkan semua results
    # Untuk batch mode: tampilkan top 3 qualified
    display_list = results if is_single_mode else qualified[:3]

    for r in display_list:
        entry_zone = f'Rp{r["entry_low"]:,.0f} - Rp{r["entry_high"]:,.0f}'
        val_info = f'Avg Value: Rp{r["avg_value_traded"]/1_000_000_000:.1f}B/hari'
        print(f'  ${r["ticker"]} — Rp{r["price"]:,.0f} | Score: {r["score"]}/100')
        print(f'  {val_info}')
        # Tampilkan SMA50 value untuk cross-check dengan TradingView
        if r.get('sma50_val'):
            print(f'  📊 SMA50: Rp{r["sma50_val"]:,.0f} (TradingView check)')
        print(f'  Entry: {entry_zone} | SL: Rp{r["sl"]:,.0f} | TP1: Rp{r["tp1"]:,.0f} | TP2: Rp{r["tp2"]:,.0f}')
        print(f'  R:R: {r["rr"]}:1')
        print(f'  Breakdown:')
        for det in r['details']:
            print(f'    - {det}')
        print()

    print(f'{"="*70}')
    print(f'  SELESAI — {len(qualified)} scalp kandidat')
    print(f'{"="*70}')

    # Tampilkan ARA LOCKED stocks jika ada
    ara_stocks = [r for r in results if r.get('is_ara', False)]
    if ara_stocks:
        print(f'\n{"="*70}')
        print(f'  🔥 ARA LOCKED — {len(ara_stocks)} saham (Momentum Play)')
        print(f'{"="*70}\n')
        print(f'  {"TICKER":<7s} {"PRICE":>6s} {"ARA":>6s} {"VOL":>10s} {"VOL/MA":>7s} {"RSI":>6s} {"ENTRY (Pullback)":>18s} {"SL":>6s} {"TP1":>6s} {"R:R":>5s} {"SCORE":>6s}')
        print(f'  {"-"*7} {"-"*6} {"-"*6} {"-"*10} {"-"*7} {"-"*6} {"-"*18} {"-"*6} {"-"*6} {"-"*5} {"-"*6}')

        for r in ara_stocks[:10]:
            vol_str = f'{r["volume"]:,}'
            vol_vs_str = f'{r["vol_vs_avg"]:>5.1f}x'
            ara_pct_str = f'+{r["ara_pct"]:.1f}%'
            entry = f'{r["entry_low"]:,.0f}-{r["entry_high"]:,.0f}'
            print(f'  ${r["ticker"]:<5s} {r["price"]:>5.0f} {ara_pct_str:>6s} {vol_str:>10s} {vol_vs_str:>7s} {r["rsi"]:>5.1f} {entry:>18s} {r["sl"]:>5.0f} {r["tp1"]:>5.0f} {r["rr"]:>4.2f} {r["score"]:>4d}')

        print(f'\n  📝 Entry untuk ARA: Tunggu pullback ke area tersebut (10-15% di bawah harga)')
        print(f'  📝 SL lebih lebar (15-18%) karena volatility tinggi')
        print(f'{"="*70}')

    # Simpan hasil ke file markdown
    save_results_to_markdown(results, qualified, is_single_mode)

    return qualified


if __name__ == '__main__':
    import sys

    # Parse command line arguments
    single_ticker = None
    if len(sys.argv) > 1:
        ticker_arg = sys.argv[1].strip()
        # Remove $ prefix if present
        if ticker_arg.startswith('$'):
            ticker_arg = ticker_arg[1:]
        # Add .JK suffix if not present
        if not ticker_arg.endswith('.JK'):
            ticker_arg = f"{ticker_arg}.JK"
        single_ticker = [ticker_arg]
        print(f"[INFO] Single ticker mode: {ticker_arg}\n")

    run_screener(single_ticker)
