"""
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
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=f'{LOOKBACK_DAYS}d'):
    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):
    df, stock = fetch_data(ticker)
    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
    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
    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 nyangkut

    # --- 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())

    # 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 ---
    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 ---
    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)

    # Indikator
    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'

    ema9 = calc_ema(close, 9)
    ema21 = calc_ema(close, 21)
    ema_cross = 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 = '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. SMA50 (20 pts)
    if sma50_trend == 'bullish':
        score += 20
        details.append(f'SMA50: BULLISH (+20)')
    elif sma50_trend == 'bearish':
        details.append(f'SMA50: BEARISH (+0)')
    else:
        details.append(f'SMA50: N/A')

    # 2. EMA9/21 (15 pts)
    if ema_cross == 'golden':
        score += 15
        details.append(f'EMA9/21: GOLDEN CROSS (+15)')
    elif ema_cross == 'death':
        details.append(f'EMA9/21: DEATH CROSS (+0)')
    else:
        details.append(f'EMA9/21: N/A')

    # 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,
        '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)
