import os
import sys
import shutil
import requests
import pytz
from datetime import datetime
#from thefirstock import thefirstock
from firstock import firstock
from kiteconnect import KiteConnect
import pprint

# ------------------- SYMBOL CONFIGURATION -------------------

class SymbolConfig:

    DEFAULT_PROPERTIES = {
                'NIFTY':{
                        'enabled': True,
                        'exchange': 'NFO',
                        'expiry': '16DEC25',
                        'multiplier': 1,
                        'profit': 9.65,
                        'quantity': 75,
                        'max_profit_target': 2000,   # Stop trading when cumulative profit reaches this amount; use 0 to diable
                        'max_loss_limit': 2000       # Stop trading when cumulative loss reaches this amount; use 0 to diable
                     }
            }

    CLIENT_OVERRIDES = {

        'TT': {
            'NIFTY':{
                        'enabled': True,
                        'multiplier': 1,
                        'profit': 99,
                        'max_profit_target': 0,
                        'max_loss_limit': 0
                     },
        },

        'RA': {
            'NIFTY':{
                        'enabled': True,
                        'multiplier': 1,
                        'profit': 4098,
                        'max_profit_target': 0,
                        'max_loss_limit': 0
                     },
        },

        'PR': {
            'NIFTY':{
                        'enabled': True,
                        'multiplier': 1,
                        'profit': 97,
                        'max_profit_target': 2000,
                        'max_loss_limit': 1500,
                        "expiry": "25D16"
                     },
        },

        'HO': {
            'NIFTY':{
                        'enabled': True,
                        'multiplier': 1,
                        'profit': 96,
                        'max_profit_target': 0,
                        'max_loss_limit': 0,
                        "expiry": "25D16"
                     },
        }
    }

    @classmethod
    def get_properties(cls, client_code):
        if client_code not in cls.CLIENT_OVERRIDES:
            raise ValueError(f"Unknown client code: {client_code}")
        props = cls.DEFAULT_PROPERTIES.copy()
        for symbol, overrides in cls.CLIENT_OVERRIDES[client_code].items():
            props[symbol].update(overrides)
        return props


# ------------------- TOKEN MANAGER -------------------

class TokenManager:
    @staticmethod
    def download_token(url, save_path):
        try:
            os.makedirs(os.path.dirname(save_path), exist_ok=True)
            response = requests.get(url)
            response.raise_for_status()
            with open(save_path, 'wb') as file:
                file.write(response.content)
            with open(save_path, 'r') as file:
                return file.read()
        except Exception as e:
            print(f"[ERROR] Token download/read error: {e}")
            return None


# ------------------- MAIN PROCESSOR -------------------

class ClientFileProcessor:

    def __init__(self, client_details, client_code):
        self.client_details = client_details
        self.client_code = client_code
        self.client_id = client_details[0]
        self.userId = self.client_id
        self.zerodhaClient = (client_code == 'PR' or client_code == 'HO' )
        self.symbol_properties = SymbolConfig.get_properties(client_code)

        self.base_folder = "/var/www/html/ACTIONS_MTF_MH/"
        
        if self.client_code == 'HO' or self.client_code == 'TT' or self.client_code == 'RA':
           self.base_folder = "/var/www/html/ACTIONS_MTF_MH/" 

        self.client_folder = os.path.join(self.base_folder, self.client_id)
       

        os.makedirs(self.client_folder, exist_ok=True)
        os.chdir(self.client_folder)
        self.loginDone = False
        self.defaultProfitMargin = 20

        self.max_allowed_quantity_single_order = 900  # Max quantity per order for Firstock

        print(f"[INFO] Initialized processor for client: {self.client_id}")
        print(f"[INFO] Symbol Properties: {self.symbol_properties}")

    def get_multiplier(self, symbol):
        multiplier = self.symbol_properties.get(symbol, {}).get("multiplier", 1)
        #print(f"[INFO] Multiplier for {symbol}: {multiplier}")
        return multiplier
    
    def get_target_levels(self,symbol):
        max_profit_target = self.symbol_properties.get(symbol, {}).get("max_profit_target", 1)
        max_loss_limit = self.symbol_properties.get(symbol, {}).get("max_loss_limit", 1)

        return int(max_profit_target), int(max_loss_limit)

    def login(self):
        print(f"[INFO] Logging in client: {self.client_id}")
        if self.zerodhaClient:
            self.api_key = self.client_details[1]
            '''
            token = TokenManager.download_token(
                "http://139.59.6.25/temp/token.txt",
                   f"/var/www/html/clientData/{self.client_id}_token.txt"
        
            )
            '''

            if self.client_code == 'HO':
                token = 'FNBE5DBOtdASNT52bMOSARKMfGWFx3A7'
            elif self.client_code == 'PR':
                token = 'jc9wREUbwBFjjaJVYh315euaQXyEhmrO'

            if not token:
                sys.exit("Token download failed.")
            try:
                self.kite = KiteConnect(api_key=self.api_key)
                self.kite.set_access_token(token)
                _ = self.kite.profile()
                print("[SUCCESS] Zerodha login successful.")
            except Exception as e:
                print(f"[ERROR] Zerodha login failed: {e}")
                sys.exit()
        else:
            login_response = firstock.login(
                userId=self.client_id,
                password=self.client_details[1],
                TOTP=self.client_details[2],
                vendorCode=self.client_details[3],
                apiKey=self.client_details[4]
            )
            if login_response.get('status') == 'success':
                print("[SUCCESS] Firstock login successful.")
            else:
                print(f"[ERROR] Firstock login failed: {login_response}")

    def parse_file_info(self, file_path):
        parts = file_path.split('__')
        info = {
            'time': parts[0].split('/')[-1].replace(':', '-'),
            'symbol': parts[1],
            'action': parts[3],
            'noLots': int(parts[4])
        }
        #print(f"[INFO] Parsed file info: {info}")
        return info

    def replicate_file_old(self, file_name):
        shutil.copy2(os.path.join(self.base_folder, file_name), os.path.join(self.client_folder, file_name))
        #print(f"[INFO] Replicated file to client folder: {file_name}")

    def replicate_file(self, file_name):
        src = os.path.join(self.base_folder, file_name)
        dst_dir = self.client_folder
        dst = os.path.join(dst_dir, file_name)

        # Ensure destination directory exists with 777
        # umask can override mode on creation; set umask=0 temporarily
        old_umask = os.umask(0)
        try:
            os.makedirs(dst_dir, mode=0o777, exist_ok=True)
        finally:
            os.umask(old_umask)

        # Copy file (metadata preserved)
        shutil.copy2(src, dst)

        # Force file permission to 777
        os.chmod(dst, 0o777)

        # Optional: also ensure directory permission remains 777
        os.chmod(dst_dir, 0o777)


    def is_valid_file(self, file_name):
        source_path = os.path.join(self.base_folder, file_name)
        target_path = os.path.join(self.client_folder, file_name)
        valid = not os.path.isdir(source_path) and not os.path.exists(target_path)
        '''
        if not valid:
            print(f"[SKIP] File {file_name} already processed or is a directory.")
        '''
        return valid
    
    def checkStopAllActionsZerodha(self):

        max_profit_target, max_loss_limit = self.get_target_levels('NIFTY')
       
        if max_profit_target == 0 and max_loss_limit == 0:
            return

        if not self.loginDone:
            self.login()
            self.loginDone = True

        try:
            # Get positions and orders from Zerodha
            positions_data = self.kite.positions()
            net_positions_data = positions_data['net']
            
            # Check if all quantities are zero
            if all(item['quantity'] == 0 for item in net_positions_data):
                total_pnl = int(sum(item['pnl'] for item in net_positions_data))
                if total_pnl > max_profit_target:
                    print(f"[INFO] total_pnl: {total_pnl}  max_profit_target: {max_profit_target}")
                    print(f"[INFO] MAX PROFIT Target Limit reached. stopping Execution")
                    sys.exit()
                elif total_pnl < -max_loss_limit:
                    print(f"[INFO] total_pnl: {total_pnl}  max_loss_limit: {max_loss_limit}")
                    print(f"[INFO] MAX LOSS Target Limit reached. stopping Execution")
                    sys.exit()
                else:
                    print(f"[INFO] Current realized pnl : {total_pnl}")

            else:
                total_pnl = int(sum(item['pnl'] for item in net_positions_data))
                print(f"[INFO] Some positions are still open. so continuing execution.. current pnl :{total_pnl}")

        except Exception as e:
            print(f"[ERROR] Zerodha stop all action check failed: {e}")

    def checkStopAllActionsFirstock(self):
        max_profit_target, max_loss_limit = self.get_target_levels('NIFTY')

        if max_profit_target == 0 and max_loss_limit == 0:
            return

        if not self.loginDone:
            self.login()
            self.loginDone = True

        try:
            # Fetch positions from Firstock
            positions = firstock.positionBook(userId=self.client_id)['data']

            # Check if all net quantities are zero
            if all(int(pos['netQuantity']) == 0 for pos in positions):
                # Calculate total realized PnL
                total_pnl = sum(float(pos['RealizedPNL']) for pos in positions)

                if total_pnl > max_profit_target:
                    print(f"[INFO] total_pnl: {total_pnl}  max_profit_target: {max_profit_target}")
                    print("[INFO] MAX PROFIT Target Limit reached. stopping Execution")
                    sys.exit()
                elif total_pnl < -max_loss_limit:
                    print(f"[INFO] total_pnl: {total_pnl}  max_loss_limit: {max_loss_limit}")
                    print("[INFO] MAX LOSS Target Limit reached. stopping Execution")
                    sys.exit()
                else:
                    print(f"[INFO] Current realized pnl : {total_pnl}")
            else:
                total_pnl = sum(float(pos['totalPNL']) for pos in positions)
                print("[INFO] Some positions are still open. so continuing execution.. current pnl :{total_pnl}")

        except Exception as e:
            print(f"[ERROR] Firstock stop all action check failed: {e}")

    def process_files(self):

        if self.zerodhaClient:
            self.checkStopAllActionsZerodha()
        else:
            self.checkStopAllActionsFirstock()
    

        for file_name in sorted(os.listdir(self.base_folder)):
            if not self.is_valid_file(file_name):
                continue
            if not self.loginDone:
                self.login()
                self.loginDone = True

            full_path = os.path.join(self.base_folder, file_name)
            if self.process_file(full_path):
                self.replicate_file(file_name)
            else:
                print(f"[ERROR] Failed to process: {file_name}")

        if self.zerodhaClient:
            self.placeProfitOrderZerodha()
        else:
            self.placeProfitOrderFirstock()

    def process_file(self, file_path):
        info = self.parse_file_info(file_path)
        base_symbol = next((sym for sym in self.symbol_properties if info['symbol'].startswith(sym)), None)
        if not base_symbol:
            print(f"[ERROR] Unrecognized symbol: {info['symbol']}")
            return False

        props = self.symbol_properties[base_symbol]
        if not props["enabled"]:
            print(f"[SKIP] Trading disabled for symbol: {base_symbol}")
            return False

        quantity = props["quantity"] * info['noLots'] * self.get_multiplier(base_symbol)
        remarks = f"test__{info['time']}__{info['noLots']}"

        print(f"[ACTION] Placing order for {info['symbol']} - Qty: {quantity}, Action: {info['action']}, Exchange: {props['exchange']}, Expiry: {props['expiry']}")

        if self.zerodhaClient:
            return self.place_zerodha_order(info, base_symbol, props, quantity, remarks)
        else:
            return self.place_firstock_order(info, base_symbol, props, quantity, remarks)

    def place_zerodha_order(self, info, base_symbol, props, quantity, remarks):
        instrument_post = info['symbol'].replace(base_symbol, "")
        trading_symbol = f"{base_symbol}{props['expiry']}{instrument_post}"
        transaction_type = KiteConnect.TRANSACTION_TYPE_BUY if info['action'].upper() == 'BUY' else KiteConnect.TRANSACTION_TYPE_SELL
        exchange = KiteConnect.EXCHANGE_NFO if base_symbol == 'NIFTY' else KiteConnect.EXCHANGE_BFO

        # Convert quantity to integer for calculations
        quantity = int(quantity)

        # Check if quantity exceeds max_allowed_quantity_single_order
        if quantity > self.max_allowed_quantity_single_order:
            num_full_orders = quantity // self.max_allowed_quantity_single_order
            remaining_quantity = quantity % self.max_allowed_quantity_single_order
            success = True

            # Place full orders
            for i in range(num_full_orders):
                order_quantity = self.max_allowed_quantity_single_order
                print(f"[INFO] Placing split order {i+1}/{num_full_orders} with params: variety={KiteConnect.VARIETY_REGULAR}, exchange={exchange}, "
                    f"tradingsymbol={trading_symbol}, transaction_type={transaction_type}, quantity={order_quantity}, "
                    f"product={KiteConnect.PRODUCT_NRML}, order_type={KiteConnect.ORDER_TYPE_MARKET}, price={0.0}, "
                    f"validity={KiteConnect.VALIDITY_DAY}, tag={remarks}")
                try:
                    order_id = self.kite.place_order(
                        variety=KiteConnect.VARIETY_REGULAR,
                        exchange=exchange,
                        tradingsymbol=trading_symbol,
                        transaction_type=transaction_type,
                        quantity=order_quantity,
                        product=KiteConnect.PRODUCT_NRML,
                        order_type=KiteConnect.ORDER_TYPE_MARKET,
                        price="0.0",
                        validity=KiteConnect.VALIDITY_DAY,
                        tag=remarks[-20:]
                    )
                    print(f"[SUCCESS] Zerodha Split Order {i+1} Placed: ID={order_id}")
                except Exception as e:
                    print(f"[ERROR] Zerodha Split Order {i+1} Failed: {e}")
                    success = False

            # Place order for remaining quantity if any
            if remaining_quantity > 0:
                print(f"[INFO] Placing split order for remaining quantity with params: variety={KiteConnect.VARIETY_REGULAR}, exchange={exchange}, "
                    f"tradingsymbol={trading_symbol}, transaction_type={transaction_type}, quantity={remaining_quantity}, "
                    f"product={KiteConnect.PRODUCT_NRML}, order_type={KiteConnect.ORDER_TYPE_MARKET}, price={0.0}, "
                    f"validity={KiteConnect.VALIDITY_DAY}, tag={remarks}")
                try:
                    order_id = self.kite.place_order(
                        variety=KiteConnect.VARIETY_REGULAR,
                        exchange=exchange,
                        tradingsymbol=trading_symbol,
                        transaction_type=transaction_type,
                        quantity=remaining_quantity,
                        product=KiteConnect.PRODUCT_NRML,
                        order_type=KiteConnect.ORDER_TYPE_MARKET,
                        price="0.0",
                        validity=KiteConnect.VALIDITY_DAY,
                        tag=remarks[-20:]
                    )
                    print(f"[SUCCESS] Zerodha Split Order for remaining quantity Placed: ID={order_id}")
                except Exception as e:
                    print(f"[ERROR] Zerodha Split Order for remaining quantity Failed: {e}")
                    success = False

            return success
        else:
            # Original logic for single order
            print(f"[INFO] Placing order with params: variety={KiteConnect.VARIETY_REGULAR}, exchange={exchange}, tradingsymbol={trading_symbol}, "
                f"transaction_type={transaction_type}, quantity={quantity}, product={KiteConnect.PRODUCT_NRML}, order_type={KiteConnect.ORDER_TYPE_MARKET}, "
                f"price={0.0}, validity={KiteConnect.VALIDITY_DAY}, tag={remarks}")
            try:
                order_id = self.kite.place_order(
                    variety=KiteConnect.VARIETY_REGULAR,
                    exchange=exchange,
                    tradingsymbol=trading_symbol,
                    transaction_type=transaction_type,
                    quantity=quantity,
                    product=KiteConnect.PRODUCT_NRML,
                    order_type=KiteConnect.ORDER_TYPE_MARKET,
                    price="0.0",
                    validity=KiteConnect.VALIDITY_DAY,
                    tag=remarks[-20:]
                )
                print(f"[SUCCESS] Zerodha Order Placed: ID={order_id}")
                return True
            except Exception as e:
                print(f"[ERROR] Zerodha Order Failed: {e}")
                return False
            
    def place_firstock_order(self, info, base_symbol, props, quantity, remarks):
        if base_symbol == 'SENSEX':
            instrument_post = info['symbol'].replace(base_symbol, "")
            trading_symbol = f"{base_symbol}{props['expiry']}{instrument_post}"
        else:
            pre = info['symbol'].replace(base_symbol, "")
            instrument = pre.replace("PE", "").replace("CE", "")
            option_type = "C" if "CE" in pre else "P"
            trading_symbol = f"{base_symbol}{props['expiry']}{option_type}{instrument}"
        
        transaction_type = 'B' if info['action'].upper() == 'BUY' else 'S'
        
        # Convert quantity to integer for calculations
        quantity = int(quantity)
        
        # Check if quantity exceeds max_allowed_quantity_single_order
        if quantity > self.max_allowed_quantity_single_order:
            num_full_orders = quantity // self.max_allowed_quantity_single_order
            remaining_quantity = quantity % self.max_allowed_quantity_single_order
            success = True
            
            # Place full orders
            for i in range(num_full_orders):
                order_quantity = self.max_allowed_quantity_single_order
                print(f"[INFO] Placing split order {i+1}/{num_full_orders} with params: userId={self.userId}, exchange={props['exchange']}, "
                    f"tradingSymbol={trading_symbol}, quantity={order_quantity}, price=0.0, "
                    f"product=M, transactionType={transaction_type}, priceType=MKT, retention=DAY, triggerPrice=0, remarks={remarks}")
                try:
                    response = firstock.placeOrder(
                        userId=self.client_id,
                        exchange=props['exchange'],
                        tradingSymbol=trading_symbol,
                        quantity=str(order_quantity),
                        price="0.0",
                        product="M",
                        transactionType=transaction_type,
                        priceType="MKT",
                        retention="DAY",
                        triggerPrice="0",
                        remarks=remarks
                    )
                    print(f"[SUCCESS] Firstock Split Order {i+1} Placed: {response}")
                except Exception as e:
                    print(f"[ERROR] Firstock Split Order {i+1} Failed: {e}")
                    success = False
            
            # Place order for remaining quantity if any
            if remaining_quantity > 0:
                print(f"[INFO] Placing split order for remaining quantity with params: userId={self.userId}, exchange={props['exchange']}, "
                    f"tradingSymbol={trading_symbol}, quantity={remaining_quantity}, price=0.0, "
                    f"product=M, transactionType={transaction_type}, priceType=MKT, retention=DAY, triggerPrice=0, remarks={remarks}")
                try:
                    response = firstock.placeOrder(
                        userId=self.client_id,
                        exchange=props['exchange'],
                        tradingSymbol=trading_symbol,
                        quantity=str(remaining_quantity),
                        price="0.0",
                        product="M",
                        transactionType=transaction_type,
                        priceType="MKT",
                        retention="DAY",
                        triggerPrice="0",
                        remarks=remarks
                    )
                    print(f"[SUCCESS] Firstock Split Order for remaining quantity Placed: {response}")
                except Exception as e:
                    print(f"[ERROR] Firstock Split Order for remaining quantity Failed: {e}")
                    success = False
            
            return success
        else:
            # Original logic for single order
            print(f"[INFO] Placing order with params: userId={self.userId}, exchange={props['exchange']}, "
                f"tradingSymbol={trading_symbol}, quantity={quantity}, price=0.0, "
                f"product=M, transactionType={transaction_type}, priceType=MKT, retention=DAY, triggerPrice=0, remarks={remarks}")
            try:
                response = firstock.placeOrder(
                    userId=self.client_id,
                    exchange=props['exchange'],
                    tradingSymbol=trading_symbol,
                    quantity=str(quantity),
                    price="0.0",
                    product="M",
                    transactionType=transaction_type,
                    priceType="MKT",
                    retention="DAY",
                    triggerPrice="0",
                    remarks=remarks
                )
                print(f"[SUCCESS] Firstock Order Placed: {response}")
                return True
            except Exception as e:
                print(f"[ERROR] Firstock Order Failed: {e}")
                return False

    def getProfitMargin(self, trading_symbol):
        for base_symbol, props in self.symbol_properties.items():
            if trading_symbol.startswith(base_symbol):
                margin = props.get("profit", self.defaultProfitMargin)
                print(f"[INFO] Profit Margin for {trading_symbol} ({base_symbol}): {margin}")
                return margin
        print(f"[WARN] Using default profit margin for {trading_symbol}")
        return self.defaultProfitMargin

    def placeProfitOrderZerodha(self):
        if not self.loginDone:
            self.login()
            self.loginDone = True

        try:
            # Get positions and orders from Zerodha
            positions_data = self.kite.positions()
            orders_data = self.kite.orders()
            
            '''
            print("\n[INFO] Current Positions:")
            pprint.pprint(positions_data)
            print("\n[INFO] Order Book:")
            pprint.pprint(orders_data)
            '''

            # Process both day and net positions
            for position_type in ['day', 'net']:
                positions = positions_data.get(position_type, [])
                
                for position in positions:
                    quantity = position['quantity']
                    if quantity == 0:
                        continue


                    trading_symbol = position['tradingsymbol']
                    exchange = position['exchange']
                    average_price = position['average_price']
                    remaining_quantity = quantity
                    cost_price_total = 0.0

                    print(f"\n[INFO] Evaluating profit booking for: {trading_symbol} (Qty={quantity}, AvgPrice={average_price})")

                    # Filter and sort BUY orders by timestamp (latest first)
                    buy_orders = []
                    for order in orders_data:
                        if (order.get('tradingsymbol') == trading_symbol and 
                            order.get('status') == 'COMPLETE' and 
                            order.get('transaction_type') == 'BUY'):
                            buy_orders.append(order)
                    
                    # Sort by order timestamp in descending order (latest first)
                    buy_orders.sort(key=lambda x: x.get('order_timestamp', ''), reverse=True)
                    
                    print(f"[INFO] Found {len(buy_orders)} BUY orders for {trading_symbol}")

                    # Calculate cost from latest orders first
                    for order in buy_orders:
                        if remaining_quantity <= 0:
                            break

                        order_avg_price = float(order['average_price'])
                        order_qty = int(order['filled_quantity'])
                        
                        # Use as much quantity as needed from this order
                        qty_to_use = min(remaining_quantity, order_qty)
                        
                        cost_price_total += qty_to_use * order_avg_price
                        remaining_quantity -= qty_to_use
                        
                        print(f"[INFO] Using {qty_to_use} from BUY order - Price: {order_avg_price}, TotalQty: {order_qty}")

                    # If we still have unmatched quantity, use position average price
                    if remaining_quantity > 0 and average_price > 0:
                        cost_price_total += remaining_quantity * average_price
                        print(f"[INFO] Using position average price for remaining {remaining_quantity} quantity: {average_price}")
                        remaining_quantity = 0

                    if cost_price_total == 0:
                        print(f"[SKIP] Cannot determine cost price for {trading_symbol}")
                        continue

                    average_cost_price = cost_price_total / quantity

                    # Get latest market price
                    try:
                        # Get instrument token for quote
                        instrument_token = position['instrument_token']
                        quote_data = self.kite.quote([instrument_token])
                        ltp = float(quote_data[str(instrument_token)]['last_price'])
                    except Exception as e:
                        print(f"[ERROR] Failed to get LTP for {trading_symbol}: {e}")
                        continue

                    profit_margin = self.getProfitMargin(trading_symbol)
                    expected_profit_level = float(average_cost_price) + profit_margin

                    print(f"[CHECK] {trading_symbol} | Cost: {average_cost_price:.2f} | ProfitMargin: {profit_margin} | LTP: {ltp} | Target: {expected_profit_level:.2f}")

                    if ltp >= expected_profit_level:
                        transaction_type = self.kite.TRANSACTION_TYPE_SELL
                        remarks = 'Profit Target'
                        
                        print(f"[PLACE] Profit Order Params:: variety={self.kite.VARIETY_REGULAR}, exchange={exchange}, "
                            f"tradingsymbol={trading_symbol}, transaction_type={transaction_type}, quantity={quantity}, "
                            f"product={self.kite.PRODUCT_NRML}, order_type={self.kite.ORDER_TYPE_MARKET}, price=0.0, "
                            f"validity={self.kite.VALIDITY_DAY}, tag={remarks}")
                        
                        try:
                            # Handle large quantities by splitting orders
                            if quantity > self.max_allowed_quantity_single_order:
                                num_full_orders = quantity // self.max_allowed_quantity_single_order
                                remaining_qty = quantity % self.max_allowed_quantity_single_order
                                success = True
                                
                                # Place full orders
                                for i in range(num_full_orders):
                                    order_quantity = self.max_allowed_quantity_single_order
                                    order_id = self.kite.place_order(
                                        variety=self.kite.VARIETY_REGULAR,
                                        exchange=exchange,
                                        tradingsymbol=trading_symbol,
                                        transaction_type=transaction_type,
                                        quantity=order_quantity,
                                        product=self.kite.PRODUCT_NRML,
                                        order_type=self.kite.ORDER_TYPE_MARKET,
                                        price="0.0",
                                        validity=self.kite.VALIDITY_DAY,
                                        tag=remarks
                                    )
                                    print(f"[SUCCESS] Zerodha Profit Split Order {i+1} Placed: ID={order_id}")
                                
                                # Place remaining quantity
                                if remaining_qty > 0:
                                    order_id = self.kite.place_order(
                                        variety=self.kite.VARIETY_REGULAR,
                                        exchange=exchange,
                                        tradingsymbol=trading_symbol,
                                        transaction_type=transaction_type,
                                        quantity=remaining_qty,
                                        product=self.kite.PRODUCT_NRML,
                                        order_type=self.kite.ORDER_TYPE_MARKET,
                                        price="0.0",
                                        validity=self.kite.VALIDITY_DAY,
                                        tag=remarks
                                    )
                                    print(f"[SUCCESS] Zerodha Profit Split Order for remaining quantity Placed: ID={order_id}")
                            else:
                                # Single order for smaller quantities
                                order_id = self.kite.place_order(
                                    variety=self.kite.VARIETY_REGULAR,
                                    exchange=exchange,
                                    tradingsymbol=trading_symbol,
                                    transaction_type=transaction_type,
                                    quantity=quantity,
                                    product=self.kite.PRODUCT_NRML,
                                    order_type=self.kite.ORDER_TYPE_MARKET,
                                    price="0.0",
                                    validity=self.kite.VALIDITY_DAY,
                                    tag=remarks
                                )
                                print(f"[SUCCESS] Zerodha Profit Order Placed: ID={order_id}\n")
                                
                        except Exception as e:
                            print(f"[ERROR] Profit order placement failed for {trading_symbol}: {e}")
                    else:
                        gap = round(expected_profit_level - ltp, 2)
                        print(f"[WAIT] Target not reached for {trading_symbol}. Gap: {gap}")

        except Exception as e:
            print(f"[ERROR] Zerodha profit order processing failed: {e}")
            
    def placeProfitOrderFirstock(self):
        try:
            positions = firstock.positionBook(userId=self.client_id)['data']
            orders = firstock.orderBook(userId=self.client_id)['data']

            for position in positions:
                net_quantity = int(position['netQuantity'])
                if net_quantity == 0:
                    continue

                trading_symbol = position['tradingSymbol']
                exchange = position['exchange']
                remaining_quantity = net_quantity
                exit_set = False
                cost_price_total = 0.0

                print(f"\n[INFO] Evaluating profit booking for: {trading_symbol} (NetQty={net_quantity})")

                for order in orders:
                    if exit_set:
                        break

                    if (order['tradingSymbol'] == trading_symbol and 
                        order['status'] == 'COMPLETE' and 
                        order['transactionType'] == 'B'):

                        avg_price = float(order['averagePrice'])
                        qty = int(order['quantity'])

                        print(f"[INFO] Matched order - Qty={qty}, AvgPrice={avg_price}")

                        if remaining_quantity >= qty:
                            remaining_quantity -= qty
                            cost_price_total += qty * avg_price
                            if remaining_quantity <= 0:
                                exit_set = True
                        else:
                            # Partial quantity
                            cost_price_total += remaining_quantity * avg_price
                            exit_set = True

                if cost_price_total == 0:
                    print(f"[SKIP] Carry forward or unmatched buys for {trading_symbol}")
                    continue

                average_cost_price = int(cost_price_total / net_quantity) + 1

                # Get latest market price
                quote = firstock.getQuote(
                    userId=self.client_id,
                    exchange=exchange,
                    tradingSymbol=trading_symbol
                )
                ltp = float(quote['data']['lastTradedPrice'])

                profit_margin = self.getProfitMargin(trading_symbol)
                expected_profit_level = float(average_cost_price) + profit_margin

                print(f"[CHECK] {trading_symbol} | Cost: {average_cost_price} | ProfitMargin: {profit_margin} | LTP: {ltp} | Target: {expected_profit_level}")

                if ltp >= expected_profit_level:
                    transaction_type = 'S'
                    remarks = 'Profit Target'
                    print(f"[PLACE] Profit Order Params:: userId={self.userId}, exchange={exchange}, "
                                f"tradingSymbol={trading_symbol}, quantity={net_quantity}, price=0.0, "
                                f"product=M, transactionType={transaction_type}, priceType=MKT, retention=DAY, triggerPrice=0, remarks={remarks}"
                            )

                    response = firstock.placeOrder(
                        userId=self.userId,
                        exchange=exchange,
                        tradingSymbol=trading_symbol,
                        quantity=str(net_quantity),
                        price="0.0",
                        product="M",
                        transactionType=transaction_type,
                        priceType="MKT",
                        retention="DAY",
                        triggerPrice="0",
                        remarks=remarks
                    )
                    print(f"[SUCCESS] Profit Order Placed: {response}\n")
                else:
                    gap = round(expected_profit_level - ltp, 2)
                    print(f"[WAIT] Target not reached for {trading_symbol}. Gap: {gap}")

        except Exception as e:
            print(f"[ERROR] Profit order processing failed: {e}")



# ------------------- MAIN EXECUTION -------------------

if __name__ == "__main__":

    if len(sys.argv) != 2:
        print(f"Usage: python {sys.argv[0].split('/')[-1]} <client_code>")
        sys.exit(1)

    client_code = sys.argv[1]

    client_map = {
        'TT': ['TT1582', 'ABcd@12345', '15111982', 'TT1582_API', '833d82681166d2a73ba0145a42b8aa6a'],
        'RA': ['RA1383', 'ABcd@12345', '13061983', 'RA1383_API', '025a1f06bb55753e2d1b24689edddd1c'],
        'GR': ['GR1453', 'O9i8u7y6$$', '14031953', 'GR1453_API', '21cd2c7c7613d5ef713b803e63004303'],
        'PR': ['PR5711', '3jx3i4it8uknmhya'],
        'HO': ['HO2208', 'myeqxx43swaeiikn']
    }

    if client_code not in client_map:
        print(f"[ERROR] Unknown client code: {client_code}")
        sys.exit(1)

    ist = pytz.timezone('Asia/Kolkata')
    now = datetime.now(ist).strftime('%Y-%m-%d %H:%M:%S')
    print(f"\n[START] {now} - Processing for client: {client_code}")

    processor = ClientFileProcessor(client_map[client_code], client_code)
    processor.process_files()
