"""
Update Cache Free Float IDX
=============================
Bikin/refresh file free_float_idx.csv berisi persentase free float
tiap saham IDX, dibaca oleh rsi_screener.py sebagai filter likuiditas.

KENAPA INI SCRIPT TERPISAH (bukan langsung dicek tiap screening)?
- Data harga (OHLCV) bisa di-download BATCH (ratusan ticker sekaligus,
  cepat). Data free float HARUS diambil SATU-SATU per ticker
  (yf.Ticker(x).info), jauh lebih lambat dan lebih gampang kena
  rate limit Yahoo Finance.
- Free float juga jarang berubah (bukan data harian), jadi gak perlu
  di-refresh tiap screening. Cukup jalankan ini sesekali (misal 1x sebulan).

CATATAN JUJUR soal keterbatasan data:
- Yahoo Finance untuk saham IDX sering TIDAK PUNYA data floatShares,
  terutama saham kecil/menengah. Wajar kalau banyak yang hasilnya kosong.
- Untuk saham blue-chip/likuid biasanya datanya lebih lengkap.
- Kalau butuh data lebih lengkap & akurat, sumber yang lebih reliable:
  situs resmi IDX (laporan kepemilikan saham publik) atau data vendor
  berbayar (RTI Business, Stockbit Pro, dll) - tapi itu di luar cakupan
  yfinance gratis.

Cara pakai:
1. pip install yfinance --break-system-packages
2. Pastikan tickers_idx.txt ada di folder yang sama
3. Jalankan: python update_free_float.py
   (bisa makan waktu lama - ratusan ticker x jeda antar-request.
    Total ~958 ticker x 1.5 detik ≈ 24 menit. Jalankan pas idle/malam.)
4. Hasil tersimpan di free_float_idx.csv
"""

import csv
import time
import yfinance as yf

TICKER_FILE = "tickers_idx.txt"
OUTPUT_FILE = "free_float_idx.csv"
SLEEP_BETWEEN_REQUESTS = 1.5   # detik, jaga-jaga rate limit Yahoo Finance
SAVE_EVERY = 25                # simpan progress tiap N ticker (biar gak hilang semua kalau macet di tengah)


def get_free_float_pct(code: str) -> float | None:
    """Ambil % free float 1 saham. Return None kalau data gak tersedia."""
    ticker = f"{code}.JK"
    try:
        info = yf.Ticker(ticker).info
        float_shares = info.get("floatShares")
        shares_outstanding = info.get("sharesOutstanding")
        if float_shares and shares_outstanding and shares_outstanding > 0:
            return round(float_shares / shares_outstanding * 100, 2)
    except Exception as e:
        print(f"  [WARN] {code}: {e}")
    return None


def load_existing_cache() -> dict:
    """Kalau file lama masih ada, load dulu - biar bisa lanjut/skip yang udah ada."""
    cache = {}
    try:
        with open(OUTPUT_FILE) as f:
            next(f)
            for line in f:
                parts = line.strip().split(",")
                if len(parts) == 2:
                    cache[parts[0]] = parts[1]
    except FileNotFoundError:
        pass
    return cache


def save_cache(cache: dict):
    with open(OUTPUT_FILE, "w", newline="") as f:
        writer = csv.writer(f)
        writer.writerow(["ticker", "free_float_pct"])
        for code, pct in sorted(cache.items()):
            writer.writerow([code, pct])


def main():
    with open(TICKER_FILE) as f:
        codes = [line.strip() for line in f if line.strip()]

    cache = load_existing_cache()
    print(f"[INFO] {len(codes)} ticker total. {len(cache)} sudah ada di cache lama.")

    for i, code in enumerate(codes, 1):
        if code in cache:
            continue  # skip yang udah pernah kecek (hapus baris ini kalau mau paksa re-fetch semua)

        pct = get_free_float_pct(code)
        cache[code] = pct if pct is not None else ""
        status = f"{pct}%" if pct is not None else "N/A"
        print(f"[{i}/{len(codes)}] {code}: {status}")

        if i % SAVE_EVERY == 0:
            save_cache(cache)

        time.sleep(SLEEP_BETWEEN_REQUESTS)

    save_cache(cache)
    known = sum(1 for v in cache.values() if v not in (None, ""))
    print(f"\n[DONE] {known}/{len(cache)} saham punya data free float. Tersimpan di {OUTPUT_FILE}")


if __name__ == "__main__":
    main()