"""
NEUROBRO SCALPING SYSTEM V2.0 — FIXED PARAMETERS
Data: Yahoo Finance (yfinance)
"""

import yfinance as yf
import pandas as pd
import numpy as np
import warnings
from datetime import datetime, timedelta

warnings.filterwarnings('ignore')

# ============================================================
# KONFIGURASI — DIOPTIMALKAN UNTUK SCALP
# ============================================================
MIN_PRICE = 0        # Rp minimum
MAX_PRICE = 200      # Rp maksimum buat scalp
MIN_VOLUME_RATIO = 2.0    # NAIKIN dari 1.5 → 2.0 biar lebih ketat
VOLUME_SPIKE_THRESHOLD = 0.4
MIN_SCORE = 70
RSI_MIN = 40         # Turunin dikit biar nangkep oversold bounce
RSI_MAX = 60         # Turunin dari 65 → 60 biar lebih konservatif
LOOKBACK_DAYS = 60

# LIST LENGKAP — SUPLEMEN DENGAN BIPI & AHAP
IDX_TICKERS = [
    'BIPI.JK', 'AHAP.JK', 'KOKA.JK', 'BUVA.JK', 'HOPE.JK',
    'KBLV.JK', 'KJEN.JK', 'MLPL.JK', 'SQMI.JK', 'COCO.JK',
    'KIJA.JK', 'TOOL.JK', 'BBYB.JK', 'BUMI.JK', 'FUTR.JK',
    'PADI.JK', 'KPIG.JK', 'KOTA.JK', 'ISEA.JK', 'IKAN.JK',
    'BKDP.JK', 'FIRE.JK', 'RGAS.JK', 'MEDC.JK', 'CUAN.JK',
    'MPOW.JK', 'PIPA.JK', 'DATA.JK', 'KIOS.JK', 'RODA.JK',
    'PGEO.JK', 'DSSA.JK', 'RAJA.JK', 'ARTO.JK', 'FWCT.JK',
    'AYAM.JK', 'BBRI.JK', 'TLKM.JK', 'ASII.JK', 'LAND.JK',
]


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) < 20:
            return None
        df.columns = [c.lower() for c in df.columns]
        return df
    except Exception:
        return 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):
    delta = series.diff()
    gain = delta.where(delta > 0, 0.0)
    loss = (-delta.where(delta < 0, 0.0))
    avg_gain = gain.rolling(window=window).mean()
    avg_loss = loss.rolling(window=window).mean()
    rs = avg_gain / avg_loss.replace(0, np.nan)
    rsi = 100 - (100 / (1 + rs))
    return rsi


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 analyze_stock(ticker):
    df = fetch_data(ticker)
    if df is None:
        return None

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

    # --- AMBIL DATA REAL UNTUK VERIFIKASI ---
    last_close = close.iloc[-1]
    last_volume = volume.iloc[-1]
    atr = (high - low).tail(14).mean()

    # --- LAYER 1: ELIGIBILITY ---
    if last_close < MIN_PRICE or last_close > MAX_PRICE:
        return None

    if len(volume) < 2:
        return None
    vol_ratio = last_volume / volume.iloc[-2]
    if vol_ratio < MIN_VOLUME_RATIO:
        return None

    avg_vol_20 = volume.tail(20).mean()
    vol_vs_avg = last_volume / avg_vol_20 if avg_vol_20 > 0 else 0
    if vol_vs_avg < 1.5:  # minimal 1.5x rata-rata
        return None

    # --- LAYER 2: SCORING (MAX 100) ---
    score = 0
    details = []
    
    # 1. SMA50 Trend Filter (20 pts)
    sma50 = calc_sma(close, 50)
    if not sma50.isna().all():
        if close.iloc[-1] > sma50.iloc[-1]:
            score += 20
            details.append(f'SMA50: BULLISH (+20)')
        else:
            details.append('SMA50: BEARISH (+0)')
    else:
        details.append('SMA50: N/A')

    # 2. EMA9/21 Golden Cross (15 pts)
    ema9 = calc_ema(close, 9)
    ema21 = calc_ema(close, 21)
    if not ema9.isna().all() and not ema21.isna().all():
        if ema9.iloc[-1] > ema21.iloc[-1]:
            score += 15
            details.append('EMA9/21: GOLDEN CROSS (+15)')
        else:
            details.append('EMA9/21: DEATH CROSS (+0)')
    else:
        details.append('EMA9/21: N/A')

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

    # 4. RSI Momentum (25 pts) — DIPERKETAT + PENALTI
    rsi = calc_rsi(close)
    rsi_val = rsi.iloc[-1] if not rsi.isna().all() else 50
    
    if RSI_MIN <= rsi_val <= RSI_MAX:
        score += 25
        details.append(f'RSI: {rsi_val:.1f} — SWEET SPOT (+25)')
    elif RSI_MIN - 10 <= rsi_val < RSI_MIN:
        score += 10
        details.append(f'RSI: {rsi_val:.1f} — OVERSOLD BOUNCE (+10)')
    elif 60 < rsi_val <= 70:
        score += 5
        details.append(f'RSI: {rsi_val:.1f} — MOMENTUM (+5)')
    elif rsi_val > 70:
        score -= 15   # PENALTI!
        details.append(f'RSI: {rsi_val:.1f} — OVERBOUGHT! (-15)')
    elif rsi_val < 30:
        score -= 10   # PENALTI oversold parah
        details.append(f'RSI: {rsi_val:.1f} — OVERSOLD EXTREME (-10)')
    else:
        details.append(f'RSI: {rsi_val:.1f} — EXTREME (+0)')

    # 5. Price Action — Higher Low (10 pts)
    if check_higher_lows(df):
        score += 10
        details.append('Price action: HIGHER LOW (+10)')
    else:
        details.append('Price action: NO HIGHER LOW (+0)')

    # 6. Support-Resistance Spread (10 pts) — BARU!
    recent_high = high.tail(20).max()
    recent_low = low.tail(20).min()
    support = recent_low
    resistance = recent_high
    spread_pct = (resistance - support) / last_close if last_close > 0 else 99
    
    if spread_pct <= 0.10:
        score += 10
        details.append(f'S/R spread: {spread_pct:.1%} — TIGHT (+10)')
    elif spread_pct <= 0.20:
        score += 5
        details.append(f'S/R spread: {spread_pct:.1%} — MODERATE (+5)')
    else:
        details.append(f'S/R spread: {spread_pct:.1%} — WIDE (+0)')

    # --- HITUNG LIMIT LEVEL UNTUK TRADING ---
    fib_50 = support + (resistance - support) * 0.5
    entry_zone_high = support + (resistance - support) * 0.382
    entry_zone_low = support + (resistance - support) * 0.236
    
    prev_high = high.iloc[-2] if len(high) >= 2 else None
    prev_low = low.iloc[-2] if len(low) >= 2 else None

    return {
        'ticker': ticker.replace('.JK', ''),
        'name': yf.Ticker(ticker).info.get('longName', 'N/A')[:30] if yf.Ticker(ticker).info else ticker,
        'price': round(last_close, 0),
        'volume': int(last_volume),
        'vol_ratio': round(vol_ratio, 2),
        '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),
        'entry_low': round(entry_zone_low, 0),
        'entry_high': round(entry_zone_high, 0),
        'prev_high': round(prev_high, 0) if prev_high else None,
        'prev_low': round(prev_low, 0) if prev_low else None,
    }


def run_screener(tickers=None):
    if tickers is None:
        tickers = IDX_TICKERS
    
    print(f'{"="*62}')
    print(f'  NEUROBRO SCALPING SYSTEM V2.0 — FIXED')
    print(f'  {datetime.now().strftime("%Y-%m-%d %H:%M")} WIB')
    print(f'{"="*62}')
    print(f'  Harga <= Rp{MAX_PRICE} | Vol ratio >= {MIN_VOLUME_RATIO}x')
    print(f'  RSI ideal: {RSI_MIN}-{RSI_MAX} | Penalty RSI > 70 & < 30')
    print(f'  Min Score: {MIN_SCORE}/100 | S/R spread filter aktif')
    print(f'{"="*62}\n')

    results = []
    total = len(tickers)
    
    for i, ticker in enumerate(tickers, 1):
        print(f'  [{i:2d}/{total}] {ticker:12s}', end=' ')
        result = analyze_stock(ticker)
        if result:
            results.append(result)
            if result['rsi'] > 65:
                print(f'SCORE {result["score"]:2d}/100 ⚠️ RSI {result["rsi"]}')
            elif result['score'] >= MIN_SCORE:
                print(f'SCORE {result["score"]:2d}/100 ✅✅')
            else:
                print(f'SCORE {result["score"]:2d}/100')
        else:
            print('GAGAL ❌')

    results.sort(key=lambda x: x['score'], reverse=True)
    qualified = [r for r in results if r['score'] >= MIN_SCORE]
    
    print(f'\n{"="*62}')
    print(f'  HASIL SCREENING — {len(qualified)} saham lolos dari {total}')
    print(f'{"="*62}\n')
    
    if not qualified:
        print('  Tidak ada yang lolos. Coba turunkan threshold.')
        return qualified
    
    print(f'  {"TICKER":<7s} {"PRICE":>6s} {"VOL":>10s} {"VOL/MA":>7s}')
    print(f'  {"RSI":>5s} {"ATR":>5s} {"SPREAD":>7s} {"SCORE":>6s}')
    print(f'  {"-"*7} {"-"*6} {"-"*10} {"-"*7} {"-"*5} {"-"*5} {"-"*7} {"-"*6}')
    
    for r in qualified:
        vol_str = f'{r["volume"]:,}'
        spread = f'{(r["resistance"] - r["support"]) / r["price"] * 100:.0f}%'
        print(f'  ${r["ticker"]:<5s} {r["price"]:>5.0f} {vol_str:>10s} {r["vol_vs_avg"]:>5.1f}x'
              f' {r["rsi"]:>4.1f} {r["atr"]:>4.0f} {spread:>7s} {r["score"]:>4d}')
    
    print(f'\n{"="*62}')
    print(f'  ENTRY ZONE — TOP {min(3, len(qualified))}')
    print(f'{"="*62}\n')
    
    for r in qualified[:3]:
        entry_zone = f'Rp{r["entry_low"]:,.0f} - Rp{r["entry_high"]:,.0f}'
        sl = f'Rp{max(r["entry_low"] - r["atr"] * 1.5, r["support"]) :,.0f}'
        tp1 = f'Rp{min(r["price"] + r["atr"] * 2, r["resistance"]) :,.0f}'
        tp2 = f'Rp{min(r["resistance"], r["price"] + r["atr"] * 3) :,.0f}'
        
        print(f'  ${r["ticker"]} — Rp{r["price"]:,.0f} | Score: {r["score"]}/100')
        print(f'  Entry: {entry_zone}')
        print(f'  SL: {sl} | TP1: {tp1} | TP2: {tp2}')
        print(f'  R:R estimasi: {(float(tp1.replace("Rp","").replace(",","")) - r["price"]) / (r["price"] - float(sl.replace("Rp","").replace(",",""))):.2f}:1')
        print()

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


# ============================================================
# JALANKAN
# ============================================================
if __name__ == '__main__':
    results = run_screener()