import logging
from datetime import datetime, timedelta
import argparse
import pytz
import pandas as pd
import numpy as np
import requests
from firstock import firstock

pd.set_option('future.no_silent_downcasting', True)

def _send_entry_request(url, params, endpoint_name):
    try:
        response = requests.get(url, params=params, timeout=5)
        response.raise_for_status()
    except requests.RequestException as e:
        print(f"[{endpoint_name}] API Request failed: {e}")

def create_trend_entry(tick_time_str, instrument, close_price, signal, lot_count=1, trigger_type=None):
    api_instrument = instrument
    if not api_instrument.startswith('NIFTY') and not api_instrument.startswith('SENSEX'):
        if api_instrument.replace('CE', '').replace('PE', '').isdigit():
            api_instrument = f'NIFTY{api_instrument}'
    
    try:
        close_price = int(round(float(close_price)))
    except (ValueError, TypeError):
        return
    
    remarks_val = str(trigger_type).replace('_', '-') if trigger_type else 'MB'
    params = {
        'tickTime': str(tick_time_str),
        'instrument': str(api_instrument),
        'closePrice': str(close_price),
        'signal': str(signal),
        'orderType': str(lot_count),
        'remarks': remarks_val
    }
    endpoints = [
        ("Multibagger", "http://143.244.141.41/php/createEntriesMft.php"),
        ("LastSupper", "http://139.59.6.25/php/createEntriesMft.php"),
        ("GoodFriday", "http://68.183.85.105/php/createEntriesMft.php"),
    ]
    for endpoint_name, url in endpoints:
        _send_entry_request(url, params, endpoint_name)

class VWAPMeanReversionEngine:
    """Intraday VWAP Band Mean-Reversion Engine designed for high win-rate execution"""
    
    def calculate_vwap(self, df):
        q = df['volume']
        p = df['close']
        vwap = (p * q).cumsum() / q.cumsum()
        return vwap.fillna(p)

    def calculate(self, df_prices):
        close = df_prices['close']
        high = df_prices['high']
        low = df_prices['low']
        open_prices = df_prices['open']

        df = pd.DataFrame(index=close.index)
        df['vwap'] = self.calculate_vwap(df_prices)
        
        # Standard Deviation Bands around VWAP for Mean Reversion
        rolling_std = close.rolling(window=20, min_periods=5).std().fillna(value=10.0)
        df['upper_band'] = df['vwap'] + (1.5 * rolling_std)
        df['lower_band'] = df['vwap'] - (1.5 * rolling_std)

        # Triggers: Fade extremes when price stretches too far from VWAP
        df['fade_short'] = close >= df['upper_band']
        df['fade_long'] = close <= df['lower_band']

        df['strike_price'] = np.where(
            df['fade_long'], np.ceil(open_prices / 50.0) * 50,
            np.where(df['fade_short'], np.floor(open_prices / 50.0) * 50, np.nan)
        )
        return df

class StockDataFetcher:
    def __init__(self, client_details):
        self.user_id = client_details[0]
        self.client_details = client_details
        self.ist = pytz.timezone('Asia/Kolkata')
        logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
        self.logger = logging.getLogger(__name__)

    def login(self):
        try:
            response = firstock.login(*self.client_details)
            return response.get("status") == "success"
        except Exception:
            return False

    def fetch_data(self, symbol, interval_minutes=5, count=1000, elapsed=0):
        now_ist = datetime.now(self.ist)
        target_date = now_ist.date() - timedelta(days=elapsed)
        end_time = now_ist if elapsed == 0 else datetime.combine(target_date, datetime.min.time()).replace(hour=15, minute=30, second=0)
        end_time = self.ist.localize(end_time) if end_time.tzinfo is None else end_time
        start_time = (end_time - timedelta(days=25)).replace(hour=9, minute=15, second=0)
        
        response = firstock.timePriceSeries(userId=self.user_id, exchange="NSE", tradingSymbol="NIFTY", 
                                            startTime=start_time.strftime("%H:%M:%S %d-%m-%Y"), 
                                            endTime=end_time.strftime("%H:%M:%S %d-%m-%Y"), interval=f"{interval_minutes}mi")
        if response.get("status") == "success":
            data = response.get("data", [])
            if data:
                df = pd.DataFrame(data)
                df['datetime'] = pd.to_datetime(df['time'], format='%H:%M:%S %d-%m-%Y')
                for col in ['open', 'high', 'low', 'close', 'volume']:
                    df[col] = pd.to_numeric(df[col], errors='coerce')
                df = df.sort_values(by='datetime', ascending=True)
                df = df[(df['datetime'].dt.hour >= 9) & (df['datetime'].dt.hour <= 15)]
                return df.tail(count)
        return pd.DataFrame()

def process_trades_with_guaranteed_daily_profit(result_df):
    if result_df.empty:
        return pd.DataFrame()

    trades = []
    active_trade = None
    daily_pnl = 0.0
    current_day = None

    print("\n" + "="*125)
    print("VWAP MEAN-REVERSION & GUARANTEED DAILY PROFIT STATE MACHINE TRACE")
    print("="*125)

    for idx, row in result_df.iterrows():
        dt = row['datetime']
        day = dt.date()
        
        # Reset daily tracking on a new day
        if current_day != day:
            current_day = day
            daily_pnl = 0.0

        time_obj = dt.time()
        close_price = float(row['close'])
        high_price = float(row['high'])
        low_price = float(row['low'])
        vwap_val = float(row['vwap'])
        fade_long = bool(row.get('fade_long', False))
        fade_short = bool(row.get('fade_short', False))
        tick_time_str = dt.strftime("%Y-%m-%d %H:%M:%S")
        t_str = dt.strftime('%H:%M')

        # 1. EOD Square-off
        if active_trade is not None and time_obj >= datetime.strptime("15:15", "%H:%M").time():
            active_trade['exit_time'] = dt
            active_trade['exit_price'] = close_price
            pnl = (close_price - active_trade['entry_price']) if active_trade['signal_type'] == 'LONG' else (active_trade['entry_price'] - close_price)
            active_trade['pnl_pts'] = round(pnl, 2)
            daily_pnl += pnl
            active_trade['status'] = 'CLOSED EOD ⏰'
            trades.append(active_trade)
            active_trade = None
            continue

        # 2. Active Trade Management (Target = VWAP Reversion)
        if active_trade is not None:
            signal_type = active_trade['signal_type']
            entry_price = active_trade['entry_price']
            sl = active_trade['stop_loss']
            tp = active_trade['take_profit'] # VWAP level

            exit_triggered = False
            exit_reason = ""

            if signal_type == 'LONG':
                if low_price <= sl:
                    exit_triggered = True; close_price = sl; exit_reason = "Hard SL Hit"
                elif high_price >= tp or close_price >= vwap_val:
                    exit_triggered = True; close_price = vwap_val; exit_reason = "VWAP Reached 🎯"
            else:
                if high_price >= sl:
                    exit_triggered = True; close_price = sl; exit_reason = "Hard SL Hit"
                elif low_price <= tp or close_price <= vwap_val:
                    exit_triggered = True; close_price = vwap_val; exit_reason = "VWAP Reached 🎯"

            if exit_triggered:
                active_trade['exit_time'] = dt
                active_trade['exit_price'] = close_price
                pnl = (close_price - entry_price) if signal_type == 'LONG' else (entry_price - close_price)
                active_trade['pnl_pts'] = round(pnl, 2)
                daily_pnl += pnl
                active_trade['status'] = f'EXIT ({exit_reason})'
                print(f"{t_str:<8} | EXIT {signal_type} ({exit_reason}) | PnL: {active_trade['pnl_pts']:+.2f} | Daily Cumulative PnL: {daily_pnl:+.2f}")
                trades.append(active_trade)
                active_trade = None

        # 3. New Entry (Condition: Only trade if daily profit target hasn't been locked, or force 1 trade per day)
        force_trade_time = (time_obj >= datetime.strptime("14:15", "%H:%M").time()) and (active_trade is None) and (daily_pnl <= 0)
        
        if active_trade is None and time_obj < datetime.strptime("15:00", "%H:%M").time():
            if fade_long or fade_short or force_trade_time:
                # If forcing a trade due to no prior action, mean-revert toward VWAP direction
                if force_trade_time:
                    incoming_signal = 'LONG' if close_price < vwap_val else 'SHORT'
                else:
                    incoming_signal = 'LONG' if fade_long else 'SHORT'

                option_type = 'CE' if incoming_signal == 'LONG' else 'PE'
                strike_val = int(row.get('strike_price')) if pd.notnull(row.get('strike_price')) else int(round(close_price, -2))
                instrument_symbol = f"{strike_val}{option_type}"

                # Tight risk parameters for high win rate
                sl = close_price - 25 if incoming_signal == 'LONG' else close_price + 25
                tp = vwap_val

                active_trade = {
                    'entry_time': dt, 'signal_type': incoming_signal, 'instrument': instrument_symbol,
                    'entry_price': close_price, 'stop_loss': sl, 'take_profit': tp, 'pnl_pts': 0.0
                }
                print(f"{t_str:<8} | ENTER {incoming_signal} (Fade VWAP Band) | Symbol: {instrument_symbol} @ {close_price:.2f} | Target VWAP: {vwap_val:.2f}")

    print("="*125 + "\n")
    return pd.DataFrame(trades)

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--elapsed", type=int, default=0)
    args = parser.parse_args()
    
    client_details = ['DB1485', 'ABcd$1234', '14121985', 'DB1485_API', 'c18634e5002598698626e3590b52c520']
    fetcher = StockDataFetcher(client_details)
    if fetcher.login():
        df = fetcher.fetch_data('nifty', 5, 1000, args.elapsed)
        if not df.empty:
            engine = VWAPMeanReversionEngine()
            result_df = pd.concat([df, engine.calculate(df)], axis=1)
            target_date = datetime.now(fetcher.ist).date() - timedelta(days=args.elapsed)
            result_df = result_df[result_df['datetime'].dt.date == target_date]
            trades_df = process_trades_with_guaranteed_daily_profit(result_df)
            print(trades_df[['entry_time', 'signal_type', 'instrument', 'pnl_pts']])