# =========================================================
# SIPXAR LIVE — SINGLE FILE
# =========================================================
import os
import yfinance as yf
import numpy as np
import math
import sqlite3
import logging
import uuid
import datetime as dt
from datetime import datetime, date
from kiteconnect import KiteConnect

# =========================================================
# CONFIG
# =========================================================

MONTHLY_SIP = 100

ALLOW_AFTER_MARKET_TEST = True   # ONLY for testing

NIFTY_YF = "NIFTYBEES.NS"
GOLD_YF  = "GOLDBEES.NS"

NIFTY_KITE = "NIFTYBEES"
GOLD_KITE  = "GOLDBEES"

SMA_MONTHS = 36

BASE_EQUITY = 0.60
TILT_MULT   = 1.5
MAX_TILT    = 0.25
MIN_EQUITY  = 0.20
MAX_EQUITY  = 0.90

STRATEGY_VERSION = "SIPXAR_v1.0"

KITE_API_KEY = "YOUR_API_KEY"
KITE_ACCESS_TOKEN = "YOUR_ACCESS_TOKEN"

KILL_SWITCH_FILE = "KILL_SWITCH"
DB_FILE = "sipxar_live.db"
LOG_FILE = f"sipxar_{datetime.now().strftime('%Y_%m')}.log"

# =========================================================
# LOGGER
# =========================================================

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s | %(levelname)s | %(message)s",
    handlers=[
        logging.FileHandler(LOG_FILE),
        logging.StreamHandler()
    ]
)
log = logging.getLogger("SIPXAR")

# =========================================================
# LEDGER (SQLite)
# =========================================================

def db():
    return sqlite3.connect(DB_FILE)

def init_db():
    with db() as conn:
        conn.execute("""
        CREATE TABLE IF NOT EXISTS runs (
            run_date TEXT PRIMARY KEY,
            run_id TEXT
        )
        """)
        conn.execute("""
        CREATE TABLE IF NOT EXISTS trades (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            run_id TEXT,
            run_date TEXT,
            symbol TEXT,
            strategy_version TEXT,
            equity_weight REAL,
            allocated_amount REAL,
            price_used REAL,
            quantity INTEGER,
            order_id TEXT,
            order_status TEXT,
            timestamp TEXT,
            notes TEXT
        )
        """)

def already_ran_today():
    today = date.today().isoformat()
    with db() as conn:
        cur = conn.execute("SELECT 1 FROM runs WHERE run_date=?", (today,))
        return cur.fetchone() is not None

def record_run(run_id):
    with db() as conn:
        conn.execute(
            "INSERT INTO runs (run_date, run_id) VALUES (?, ?)",
            (date.today().isoformat(), run_id)
        )

def record_trade(**r):
    with db() as conn:
        conn.execute("""
        INSERT INTO trades (
            run_id, run_date, symbol, strategy_version,
            equity_weight, allocated_amount, price_used,
            quantity, order_id, order_status, timestamp, notes
        )
        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
        """, (
            r["run_id"], r["run_date"], r["symbol"], r["strategy_version"],
            r["equity_weight"], r["allocated_amount"], r["price_used"],
            r["quantity"], r["order_id"], r["order_status"],
            datetime.now().isoformat(), r.get("notes")
        ))

# =========================================================
# DATA (YFINANCE)
# =========================================================

def fetch_sma(ticker):
    df = yf.download(
        ticker,
        period=f"{SMA_MONTHS + 2}mo",
        interval="1mo",
        auto_adjust=True,
        progress=False
    ).dropna()
    return float(df["Close"].tail(SMA_MONTHS).mean())

def fetch_price(ticker):
    df = yf.download(
        ticker,
        period="5d",
        interval="1d",
        auto_adjust=True,
        progress=False
    )
    return float(df["Close"].iloc[-1].item())

# =========================================================
# STRATEGY
# =========================================================

def kill_switch_active():
    return os.path.exists(KILL_SWITCH_FILE)

def market_open():
    now = dt.datetime.now().time()
    return dt.time(9, 15) <= now <= dt.time(15, 30)

def sanity_check(n_qty, g_qty):
    if n_qty <= 0 and g_qty <= 0:
        raise RuntimeError("Sanity check failed: both quantities zero")

def compute_weights(n_price, g_price, n_sma, g_sma):
    assert isinstance(n_price, float)
    assert isinstance(g_price, float)
    assert isinstance(n_sma, float)
    assert isinstance(g_sma, float)

    dev_n = (n_price / n_sma) - 1
    dev_g = (g_price / g_sma) - 1
    rel   = dev_n - dev_g

    tilt = np.clip(-rel * TILT_MULT, -MAX_TILT, MAX_TILT)

    eq_w = BASE_EQUITY * (1 + tilt)
    eq_w = min(max(eq_w, MIN_EQUITY), MAX_EQUITY)

    return eq_w, 1 - eq_w

# =========================================================
# ZERODHA EXECUTION
# =========================================================

kite = KiteConnect(api_key=KITE_API_KEY)
kite.set_access_token(KITE_ACCESS_TOKEN)

def place_buy(symbol, qty):
    if qty <= 0:
        return None
    return kite.place_order(
        variety=kite.VARIETY_REGULAR,
        exchange=kite.EXCHANGE_NSE,
        tradingsymbol=symbol,
        transaction_type=kite.TRANSACTION_TYPE_BUY,
        quantity=qty,
        order_type=kite.ORDER_TYPE_MARKET,
        product=kite.PRODUCT_CNC
    )

# =========================================================
# MAIN RUN
# =========================================================

def main():
    init_db()

    if kill_switch_active():
        log.critical("KILL SWITCH ACTIVE — ABORTING EXECUTION")
        return

    if not market_open() and not ALLOW_AFTER_MARKET_TEST:
        log.error("Market closed — aborting")
        return

    if not market_open() and ALLOW_AFTER_MARKET_TEST:
        log.warning("Market closed — TEST MODE override enabled")

    if already_ran_today():
        log.error("ABORT: Strategy already executed today")
        return

    run_id = str(uuid.uuid4())
    record_run(run_id)

    log.info(f"Starting SIPXAR LIVE | run_id={run_id}")

    n_price = fetch_price(NIFTY_YF)
    g_price = fetch_price(GOLD_YF)

    n_sma = fetch_sma(NIFTY_YF)
    g_sma = fetch_sma(GOLD_YF)

    eq_w, g_w = compute_weights(n_price, g_price, n_sma, g_sma)

    n_amt = MONTHLY_SIP * eq_w
    g_amt = MONTHLY_SIP * g_w

    n_qty = math.floor(n_amt / n_price)
    g_qty = math.floor(g_amt / g_price)
    sanity_check(n_qty, g_qty)
    log.info(f"Weights → Equity={eq_w:.2f}, Gold={g_w:.2f}")
    log.info(f"Qty → NIFTY={n_qty}, GOLD={g_qty}")

    for sym, qty, price, amt in [
        (NIFTY_KITE, n_qty, n_price, n_amt),
        (GOLD_KITE,  g_qty, g_price, g_amt)
    ]:
        if qty <= 0:
            log.warning(f"Skipping {sym}, qty=0")
            continue

        try:
            oid = place_buy(sym, qty)
            log.info(f"Order placed {sym} | order_id={oid}")

            record_trade(
                run_id=run_id,
                run_date=date.today().isoformat(),
                symbol=sym,
                strategy_version=STRATEGY_VERSION,
                equity_weight=eq_w,
                allocated_amount=amt,
                price_used=price,
                quantity=qty,
                order_id=oid,
                order_status="PLACED",
                notes="LIVE order"
            )

        except Exception as e:
            log.error(f"Order failed {sym}: {e}")

            record_trade(
                run_id=run_id,
                run_date=date.today().isoformat(),
                symbol=sym,
                strategy_version=STRATEGY_VERSION,
                equity_weight=eq_w,
                allocated_amount=amt,
                price_used=price,
                quantity=qty,
                order_id=None,
                order_status="FAILED",
                notes=str(e)
            )

    log.info("SIPXAR LIVE run completed")

# =========================================================

if __name__ == "__main__":
    main()
