"""
NEUROBRO SCALPING SYSTEM V1.0 — Indonesian Stock Screener
Data source: Yahoo Finance (yfinance)
Author: Neurobro
"""

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

warnings.filterwarnings('ignore')

# ============================================================
# KONFIGURASI
# ============================================================
MIN_PRICE = 0        # Rp minimum (0 = no filter)
MAX_PRICE = 200      # Rp maksimum untuk scalp
MIN_VOLUME_RATIO = 1.5   # volume hari ini / volume kemarin
VOLUME_SPIKE_THRESHOLD = 0.4   # 40% spike from average
MIN_SCORE = 70       # minimum total score (dari 100)
RSI_MIN = 45
RSI_MAX = 65
LOOKBACK_DAYS = 60   # data historis untuk kalkulasi indikator

# Daftar saham IDX (bisa ditambah sendiri)
IDX_TICKERS = [
    'BBYB.JK', 'BUVA.JK', 'HOPE.JK', 'KBLV.JK', 'KJEN.JK',
    'MLPL.JK', 'SQMI.JK', 'COCO.JK', 'KIJA.JK', 'TOOL.JK',
    'AHAP.JK', 'KOKA.JK', 'BIPI.JK', 'KOTA.JK', 'PADI.JK',
    'KPIG.JK', 'BUMI.JK', 'FUTR.JK', 'LAND.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',
]


def fetch_data(ticker, period=f'{LOOKBACK_DAYS}d'):
    """Ambil data OHLCV dari Yahoo Finance."""
    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):
    """Cek apakah harga membentuk higher low dalam lookback terakhir."""
    if len(df) < lookback + 5:
        return False
    recent = df.tail(lookback)
    low = recent['low'].values
    # Bandingkan 2 bagian: 5 bar pertama vs 5 bar terakhir
    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):
    """
    Layer 1: Liquidity & Eligibility
    Layer 2: Technical Structure (scoring)
    """
    df = fetch_data(ticker)
    if df is None:
        return None

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

    # --- Harga terbaru ---
    last_close = close.iloc[-1]
    last_volume = volume.iloc[-1]

    # --- LAYER 1: LIQUIDITY & ELIGIBILITY ---
    
    # Filter harga
    if last_close < MIN_PRICE or last_close > MAX_PRICE:
        return None

    # Volume ratio (hari ini vs kemarin)
    if len(volume) < 2:
        return None
    vol_ratio = last_volume / volume.iloc[-2]
    if vol_ratio < MIN_VOLUME_RATIO:
        return None

    # Volume vs rata-rata 20 hari
    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 < 2:  # minimal 2x rata-rata untuk scalp
        return None

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

    # 2. EMA9/21 Golden Cross (20 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 += 20
            details.append('EMA9/21: GOLDEN CROSS (+20)')
        else:
            details.append('EMA9/21: DEATH CROSS (+0)')
    else:
        details.append('EMA9/21: N/A')

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

    # 4. RSI Momentum (20 pts)
    rsi = calc_rsi(close)
    rsi_val = rsi.iloc[-1] if not rsi.isna().all() else 50
    if RSI_MIN <= rsi_val <= RSI_MAX:
        score += 20
        details.append(f'RSI: {rsi_val:.1f} — IDEAL ZONE (+20)')
    elif 30 <= rsi_val < RSI_MIN:
        score += 10
        details.append(f'RSI: {rsi_val:.1f} — OVERSOLD BOUNCE (+10)')
    elif RSI_MAX < rsi_val <= 70:
        score += 10
        details.append(f'RSI: {rsi_val:.1f} — MOMENTUM (+10)')
    else:
        details.append(f'RSI: {rsi_val:.1f} — EXTREME (+0)')

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

    # --- Support & Resistance ---
    recent_high = high.tail(20).max()
    recent_low = low.tail(20).min()
    support = recent_low
    resistance = recent_high

    # Previous day high/low
    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', ''),
        '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),
        'score': score,
        'details': details,
        'support': round(support, 0),
        'resistance': round(resistance, 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):
    """Jalankan screener untuk semua ticker."""
    if tickers is None:
        tickers = IDX_TICKERS
    
    print(f'{"="*60}')
    print(f'  NEUROBRO SCALPING SYSTEM V1.0')
    print(f'  {datetime.now().strftime("%Y-%m-%d %H:%M")} WIB')
    print(f'{"="*60}')
    print(f'  Filter: Harga <= Rp{MAX_PRICE} | Vol ratio >= {MIN_VOLUME_RATIO}x')
    print(f'  Min Score: {MIN_SCORE}/100 | RSI: {RSI_MIN}-{RSI_MAX}')
    print(f'{"="*60}\n')

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

    # Urutkan berdasarkan score descending
    results.sort(key=lambda x: x['score'], reverse=True)
    
    # Filter minimal score
    qualified = [r for r in results if r['score'] >= MIN_SCORE]
    
    # --- OUTPUT ---
    print(f'\n{"="*60}')
    print(f'  HASIL SCREENING — {len(qualified)} saham lolos dari {total}')
    print(f'{"="*60}\n')
    
    if not qualified:
        print('  Tidak ada saham yang memenuhi kriteria hari ini.')
        print('  Coba turunkan threshold atau tambah daftar ticker.')
        return qualified
    
    # Tabel
    print(f'  {"TICKER":<8s} {"PRICE":>7s} {"VOL":>10s} {"VOLxMA":>7s} {"RSI":>6s} {"SCORE":>6s}')
    print(f'  {"-"*8} {"-"*7} {"-"*10} {"-"*7} {"-"*6} {"-"*6}')
    for r in qualified:
        vol_str = f'{r["volume"]:,}'
        print(f'  ${r["ticker"]:<6s} {r["price"]:>7.0f} {vol_str:>10s} '
              f'{r["vol_vs_avg"]:>6.1f}x {r["rsi"]:>5.1f} {r["score"]:>5d}/100')
    
    print(f'\n{"="*60}')
    print(f'  DETAIL TEKNIKAL (Top {min(5, len(qualified))})')
    print(f'{"="*60}\n')
    
    for r in qualified[:5]:
        print(f'  ${r["ticker"]} — Rp{r["price"]:,.0f} | Score: {r["score"]}/100')
        print(f'  Support: Rp{r["support"]:,.0f} | Resistance: Rp{r["resistance"]:,.0f}')
        print(f'  Prev H/L: Rp{r["prev_high"]:,.0f} / Rp{r["prev_low"]:,.0f}')
        for d in r['details']:
            print(f'    • {d}')
        print()
    
    print(f'{"="*60}')
    print(f'  LENGKAP: {len(qualified)} saham lolos')
    print(f'{"="*60}')
    
    return qualified


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