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
import fcntl
import time


# ------------------- SYMBOL CONFIGURATION -------------------

class SymbolConfig:

    DEFAULT_PROPERTIES = {
                'NIFTY':{
                        'enabled': True,
                        'exchange': 'NFO',
                        'expiry': '14JUL26',
                        'multiplier': 1,
                        'profit': 9.65,
                        'quantity': 65
                     },
                'SENSEX':{
                        'enabled': True,
                        'exchange': 'BFO',
                        'expiry': '26716',
                        'multiplier': 1,
                        'profit': 99,
                        'quantity': 20
                     }
            }

    # Global profit/loss limits that apply across all symbols for all clients
    DEFAULT_GLOBAL_LIMITS = {
        'max_profit_target': 50000,   # Stop trading when cumulative profit reaches this amount; use 0 to disable
        'max_loss_limit': 5000        # Stop trading when cumulative loss reaches this amount; use 0 to disable
    }

    # Client-specific global limits (overrides DEFAULT_GLOBAL_LIMITS)
    CLIENT_GLOBAL_LIMITS = {
        'TT': {
            'max_profit_target': 200000,
            'max_loss_limit': 300000
        },
        'RA': {
            'max_profit_target': 200000,
            'max_loss_limit': 300000
        },
        'DB': {
            'max_profit_target': 200000,
            'max_loss_limit': 300000
        },
        'PR': {
            'max_profit_target': 200000,
            'max_loss_limit': 300000
        },
        'HO': {
            'max_profit_target': 400000,
            'max_loss_limit': 600000
        },
        'XC': {
            'max_profit_target': 200000,
            'max_loss_limit': 300000
        },
    }

    CLIENT_OVERRIDES = {

        'TT': {
            'NIFTY':{
                        'enabled': True,
                        'multiplier': 1,
                        'profit': 292
                     },
            'SENSEX':{
                        'enabled': True,
                        'multiplier': 1,
                        'profit': 292
                     }
        },

        'RA': {
            'NIFTY':{
                        'enabled': True,
                        'multiplier': 1,
                        'profit': 292
                     },
            'SENSEX':{
                        'enabled': True,
                        'multiplier': 1,
                        'profit': 292
                     }
        },

        'DB': {
            'NIFTY':{
                        'enabled': True,
                        'multiplier': 1,
                        'profit': 292
                     },
            'SENSEX':{
                        'enabled': True,
                        'multiplier': 1,
                        'profit': 292
                     }
        },

        'PR': {
            'NIFTY':{
                        'enabled': True,
                        'multiplier': 1,
                        'profit': 292,
                        "expiry": "26714"
                     },
            'SENSEX':{
                        'enabled': True,
                        'multiplier': 1,
                        'profit': 292
                     }
        },

        'HO': {
            'NIFTY':{
                        'enabled': True,
                        'multiplier': 2,
                        'profit': 292,
                        "expiry": "26714"
                     },
            'SENSEX':{
                        'enabled': True,
                        'multiplier': 2,
                        'profit': 292
                     }
        },

        'XC': {
            'NIFTY':{
                        'enabled': True,
                        'multiplier': 1,
                        'profit': 292,
                        "expiry": "26714"
                     },
            'SENSEX':{
                        'enabled': True,
                        'multiplier': 1,
                        'profit': 292
                     }
        }
    }

    @classmethod
    def get_properties(cls, client_code):
        if client_code not in cls.CLIENT_OVERRIDES:
            raise ValueError(f"Unknown client code: {client_code}")
        
        # Start with default properties
        props = {}
        for symbol, default_props in cls.DEFAULT_PROPERTIES.items():
            props[symbol] = default_props.copy()
        
        # Apply client overrides for each symbol
        for symbol, overrides in cls.CLIENT_OVERRIDES[client_code].items():
            if symbol in props:
                props[symbol].update(overrides)
        
        return props

    @classmethod
    def get_global_limits(cls, client_code):
        """Get global profit/loss limits for a client"""
        # Return client-specific global limits if available, otherwise defaults
        if client_code in cls.CLIENT_GLOBAL_LIMITS:
            return cls.CLIENT_GLOBAL_LIMITS[client_code].copy()
        else:
            return cls.DEFAULT_GLOBAL_LIMITS.copy()


# ------------------- 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' or client_code == 'XC')
        self.symbol_properties = SymbolConfig.get_properties(client_code)
        self.global_limits = SymbolConfig.get_global_limits(client_code)

        self.dynamicProfitEnable_Zerodha = False
        self.dynamicProfitEnable_Firstock = False

        self.base_folder = "/var/www/html/ACTIONS_MH/"
        self.base_folder = "/var/www/html/ACTIONS_MTF_MH/"
        
        '''
        if self.client_code == 'HO' or self.client_code == 'TT':
           self.base_folder = "/var/www/html/ACTIONS_MTF_MH/" 
        '''
        

        print(f"[INFO] ClientId: {self.client_code} watching: {self.base_folder}")

        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

        # Symbol-specific quantity limits
        self.max_allowed_quantity_nifty = 1300   # Max quantity per order for NIFTY
        self.max_allowed_quantity_sensex = 800  # Max quantity per order for SENSEX

        print(f"[INFO] Initialized processor for client: {self.client_id}")
        print(f"[INFO] Symbol Properties: {self.symbol_properties}")
        print(f"[INFO] Global Limits - Max Profit Target: {self.global_limits['max_profit_target']}, Max Loss Limit: {self.global_limits['max_loss_limit']}")
        print(f"[INFO] Quantity Limits - NIFTY: {self.max_allowed_quantity_nifty}, SENSEX: {self.max_allowed_quantity_sensex}")

    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_max_allowed_quantity(self, base_symbol):
        """
        Get max allowed quantity per order based on symbol
        Args:
            base_symbol: 'NIFTY' or 'SENSEX'
        Returns:
            int: Maximum allowed quantity per order
        """
        if base_symbol == 'SENSEX':
            return self.max_allowed_quantity_sensex
        else:  # NIFTY or any other symbol
            return self.max_allowed_quantity_nifty
    
    def get_target_levels(self):
        """Get global profit/loss target levels"""
        max_profit_target = self.global_limits.get("max_profit_target", 0)
        max_loss_limit = self.global_limits.get("max_loss_limit", 0)
        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 = '5kLNLUpGpKxvQ4agLTZUGaf0cYILkoZe'
            elif self.client_code == 'PR':
                token = 'zA6Ns0jGsIS48l7soCQqznlOewkhilcF'
            elif self.client_code == 'XC':
                token = 'UlB83wXjnnvzu84HekkdM9ZcObM3eNuZ'

            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]),
            'notes': parts[5]
        }
        #print(f"[INFO] Parsed file info: {info}")
        return info

    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)

        
        # CRITICAL: Force sync to disk and small delay
        os.sync()  # Sync filesystem
        time.sleep(0.5)  # Give time for file to be fully written

    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()
       
        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()
            #pprint.pprint(positions_data)
            net_positions_data = positions_data['net']
            day_positions_data = positions_data['net']
            
            # Check if all quantities are zero (all positions closed)
            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}")
                    print(f'net_positions_data - total_pnl : {total_pnl}')
                    total_pnl = int(sum(item['pnl'] for item in day_positions_data))
                    print(f'day_positions_data - total_pnl : {total_pnl}')
                    return

            else:
                # Some positions still open
                total_pnl = int(sum(item['pnl'] for item in net_positions_data))
                print(f'net_positions_data - total_pnl : {total_pnl}')
                total_pnl = int(sum(item['pnl'] for item in day_positions_data))
                print(f'day_positions_data - total_pnl : {total_pnl}')

                if not self.dynamicProfitEnable_Zerodha:
                    print(f"[INFO] Dynamic Profit Disabled ...")
                    return
                
                print(f"[INFO] Dynamic Profit Enabled ... Checking ...")

                if total_pnl > max_profit_target:
                    marker = 'ProfitLimit'
                    print(f"[INFO] Some positions are still open but Profit met. current pnl :{total_pnl}  > max_profit_target ({max_profit_target})")
                elif total_pnl < -max_loss_limit:
                    marker = 'LossLimit'
                    print(f"[INFO] Some positions are still open but loss limit met. current pnl :{total_pnl}  < max_loss_limit ( -{max_loss_limit})")
                else:
                    print(f"[INFO] Some positions are still open but within limits. current pnl :{total_pnl} ")
                    return
                
                print('-------------------------')
                print(f'FORCE CLOSE ALL POSITIONS- {marker}')
                print('-------------------------')
                

                for item in net_positions_data:

                    quantity = item['quantity'] 

                    if item['quantity'] != 0:

                        exchange = item['exchange']
                        trading_symbol = item['tradingsymbol']
                        transaction_type = self.kite.TRANSACTION_TYPE_SELL
                        
                        # Determine symbol for quantity limit
                        base_symbol = 'NIFTY' if 'NIFTY' in trading_symbol else 'SENSEX'
                        max_qty = self.get_max_allowed_quantity(base_symbol)

                        # Check if quantity exceeds max allowed quantity for this symbol
                        if quantity > max_qty:
                            num_full_orders = quantity // max_qty
                            remaining_quantity = quantity % max_qty

                            # Place full orders
                            for i in range(num_full_orders):
                                order_quantity = max_qty
                                remarks = f"PO_{i}_{order_quantity}"
                                print(f"[PLACE] Profit Order Params:: variety={self.kite.VARIETY_REGULAR}, exchange={exchange}, "
                                    f"tradingsymbol={trading_symbol}, transaction_type={transaction_type}, quantity={order_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:
                                    # Max allowed order in iteration
                                    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,
                                        market_protection="10",
                                        tag=remarks
                                    )
                                    print(f"[SUCCESS] Zerodha Profit Order Placed: ID={order_id}\n")
                                    time.sleep(5)
                                except Exception as e:
                                    print(f"[ERROR] Zerodha Order Failed: {e}")
                            
                            # Place order for remaining quantity if any
                            if remaining_quantity > 0:
                                order_quantity = remaining_quantity
                                remarks = f"PO_Last_{order_quantity}"
                                print(f"[PLACE] Profit Order Params:: variety={self.kite.VARIETY_REGULAR}, exchange={exchange}, "
                                    f"tradingsymbol={trading_symbol}, transaction_type={transaction_type}, quantity={order_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:
                                    # Max allowed order in iteration
                                    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,
                                        market_protection="10",
                                        tag=remarks
                                    )
                                    print(f"[SUCCESS] Zerodha Profit Order Placed: ID={order_id}\n")
                                except Exception as e:
                                    print(f"[ERROR] Zerodha Order Failed: {e}")
                            
                        else:
                            remarks = 'SO-PT'
                            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:
                                # 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,
                                    market_protection="10",
                                    tag=remarks
                                )
                                print(f"[SUCCESS] Zerodha Profit Order Placed: ID={order_id}\n")
                                
                            except Exception as e:
                                print(f"[ERROR] Zerodha Order Failed: {e}")
                

        except Exception as e:
            print(f"[ERROR] Zerodha stop all action check failed: {e}")

        print('-------------------------  exiting')
        sys.exit()

    def checkStopAllActionsFirstock(self):
        max_profit_target, max_loss_limit = self.get_target_levels()

        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 (all positions closed)
            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}")
                    return
            else:
                # Some positions still open
                total_pnl = sum(float(pos['totalPNL']) for pos in positions)
                print(f"[INFO] Current Total pnl : {total_pnl}")
                if not self.dynamicProfitEnable_Firstock:
                    print(f"[INFO] Dynamic Profit Disabled ...")
                    return
                
                print(f"[INFO] Dynamic Profit Enabled ... Checking ...")

                if total_pnl > max_profit_target:
                    marker = 'ProfitLimit'
                    print(f"[INFO] Some positions are still open but Profit met. current pnl :{total_pnl}  > max_profit_target ({max_profit_target})")
                elif total_pnl < -max_loss_limit:
                    marker = 'LossLimit'
                    print(f"[INFO] Some positions are still open but loss limit met. current pnl :{total_pnl}  < max_loss_limit ( -{max_loss_limit})")
                else:
                    print(f"[INFO] Some positions are still open but within limits. current pnl :{total_pnl} ")
                    return
                
                print('-------------------------')
                print(f'FORCE CLOSE ALL POSITIONS - {marker}')
                print('-------------------------')

                for pos in positions:

                    quantity = int(pos['netQuantity'])
                    trading_symbol = pos['tradingSymbol']
                    transaction_type = 'S'
                    
                    if quantity != 0:
                        
                        # Determine symbol for quantity limit
                        base_symbol = 'NIFTY' if 'NIFTY' in trading_symbol else 'SENSEX'
                        max_qty = self.get_max_allowed_quantity(base_symbol)

                        # Check if quantity exceeds max allowed quantity for this symbol
                        if quantity > max_qty:
                            num_full_orders = quantity // max_qty
                            remaining_quantity = quantity % max_qty
                            success = True

                            # Place full orders
                            for i in range(num_full_orders):
                                order_quantity = max_qty
                                remarks = f"test__Sell{i}__{max_qty}"
                                print(f"[INFO] Placing split order {i+1}/{num_full_orders} with params: userId={self.userId}, exchange={pos['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=pos['exchange'],
                                        tradingSymbol=trading_symbol,
                                        quantity=str(order_quantity),
                                        price="0.0",
                                        product="M",
                                        transactionType=transaction_type,
                                        priceType="MKT",
                                        retention="DAY",
                                        triggerPrice="0",
                                        mkt_protection="10",
                                        remarks=remarks
                                    )
                                    print(f"[SUCCESS] Firstock Split Order {i+1} Placed: {response}")
                                    time.sleep(0.5)
                                except Exception as e:
                                    print(f"[ERROR] Firstock Split Order {i+1} Failed: {e}")
                                    success = False
                                    time.sleep(0.5)
                            
                            # Place order for remaining quantity if any
                            if remaining_quantity > 0:
                                remarks = f"test__SellLast__{remaining_quantity}"
                                print(f"[INFO] Placing split order for remaining quantity with params: userId={self.userId}, exchange={pos['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=pos['exchange'],
                                        tradingSymbol=trading_symbol,
                                        quantity=str(remaining_quantity),
                                        price="0.0",
                                        product="M",
                                        transactionType=transaction_type,
                                        priceType="MKT",
                                        retention="DAY",
                                        triggerPrice="0",
                                        mkt_protection="10",
                                        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
                            
                        else:
                            remarks = f"test__SellSingle__{quantity}"
                            # Original logic for single order
                            print(f"[INFO] Placing order with params: userId={self.userId}, exchange={pos['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=pos['exchange'],
                                    tradingSymbol=trading_symbol,
                                    quantity=str(quantity),
                                    price="0.0",
                                    product="M",
                                    transactionType=transaction_type,
                                    priceType="MKT",
                                    retention="DAY",
                                    triggerPrice="0",
                                    mkt_protection="10",
                                    remarks=remarks
                                )
                                print(f"[SUCCESS] Firstock Order Placed: {response}")
                            except Exception as e:
                                print(f"[ERROR] Firstock Order Failed: {e}")
                
                print('-------------------------')    
                sys.exit()
                

        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):
        print(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']}_{info['notes']}"

        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):
        # For SENSEX, use the actual trading symbol from the entry itself
        if base_symbol == 'SENSEX':
            '''
            end_part = info['symbol'].replace(base_symbol,'')
            expiry = props['expiry']
            trading_symbol = f'{base_symbol}{expiry}{end_part}'
            '''

            trading_symbol = info['symbol']
        else:
            # For NIFTY, construct the trading symbol with expiry
            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)
        
        # Get max allowed quantity for this symbol
        max_qty = self.get_max_allowed_quantity(base_symbol)

        # Check if quantity exceeds max allowed quantity for this symbol
        if quantity > max_qty:
            num_full_orders = quantity // max_qty
            remaining_quantity = quantity % max_qty
            success = True

            # Place full orders
            for i in range(num_full_orders):
                order_quantity = max_qty
                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,
                        market_protection="10",
                        tag=remarks[-20:]
                    )
                    print(f"[SUCCESS] Zerodha Split Order {i+1} Placed: ID={order_id}")
                    time.sleep(0.5)
                except Exception as e:
                    print(f"[ERROR] Zerodha Split Order {i+1} Failed: {e}")
                    success = False
                    time.sleep(0.5)

            # 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,
                        market_protection="10",
                        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,
                    market_protection="10",
                    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):
        # For SENSEX, use the actual trading symbol from the entry itself
        if base_symbol == 'SENSEX':

            '''
            end_part = info['symbol'].replace(base_symbol,'')
            expiry = props['expiry']
            trading_symbol = f'{base_symbol}{expiry}{end_part}'
            '''

            trading_symbol = info['symbol']

        else:
            # For NIFTY, construct the trading symbol with expiry
            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)
        
        # Get max allowed quantity for this symbol
        max_qty = self.get_max_allowed_quantity(base_symbol)
        
        # Check if quantity exceeds max allowed quantity for this symbol
        if quantity > max_qty:
            num_full_orders = quantity // max_qty
            remaining_quantity = quantity % max_qty
            success = True
            
            # Place full orders
            for i in range(num_full_orders):
                order_quantity = max_qty
                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",
                        mkt_protection="10",
                        remarks=remarks
                    )
                    print(f"[SUCCESS] Firstock Split Order {i+1} Placed: {response}")
                    time.sleep(0.5)
                except Exception as e:
                    print(f"[ERROR] Firstock Split Order {i+1} Failed: {e}")
                    success = False
                    time.sleep(0.5)
            
            # 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",
                        mkt_protection="10",
                        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",
                    mkt_protection="10",
                    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)
            '''

            already_processed_sysmbols = []

            # 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

                    if trading_symbol in already_processed_sysmbols:
                        #avoid processing the same symbol multiple times
                        continue
                    else:
                        already_processed_sysmbols.append(trading_symbol)

                    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:
                            # Determine symbol for quantity limit
                            base_symbol = 'NIFTY' if 'NIFTY' in trading_symbol else 'SENSEX'
                            max_qty = self.get_max_allowed_quantity(base_symbol)
                            
                            # Handle large quantities by splitting orders
                            if quantity > max_qty:
                                num_full_orders = quantity // max_qty
                                remaining_qty = quantity % max_qty
                                success = True
                                
                                # Place full orders
                                for i in range(num_full_orders):
                                    order_quantity = max_qty
                                    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,
                                        market_protection="10",
                                        tag=remarks
                                    )
                                    print(f"[SUCCESS] Zerodha Profit Split Order {i+1} Placed: ID={order_id}")
                                    time.sleep(0.5)
                                
                                # 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,
                                        market_protection="10",
                                        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,
                                    market_protection="10",
                                    tag=remarks
                                )
                                print(f"[SUCCESS] Zerodha Profit Order Placed: ID={order_id}\n")
                                
                        except Exception as e:
                            time.sleep(0.5)
                            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'
                    
                    # Determine symbol for quantity limit
                    base_symbol = 'NIFTY' if 'NIFTY' in trading_symbol else 'SENSEX'
                    max_qty = self.get_max_allowed_quantity(base_symbol)
                    
                    # Handle large quantities by splitting orders
                    if net_quantity > max_qty:
                        num_full_orders = net_quantity // max_qty
                        remaining_qty = net_quantity % max_qty
                        
                        # Place full orders
                        for i in range(num_full_orders):
                            order_quantity = max_qty
                            print(f"[PLACE] Profit Order Params:: userId={self.userId}, exchange={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}"
                            )
                            response = firstock.placeOrder(
                                userId=self.userId,
                                exchange=exchange,
                                tradingSymbol=trading_symbol,
                                quantity=str(order_quantity),
                                price="0.0",
                                product="M",
                                transactionType=transaction_type,
                                priceType="MKT",
                                retention="DAY",
                                triggerPrice="0",
                                mkt_protection="10",
                                remarks=remarks
                            )
                            print(f"[SUCCESS] Profit Split Order {i+1} Placed: {response}\n")
                            time.sleep(0.5)
                        
                        # Place remaining quantity
                        if remaining_qty > 0:
                            print(f"[PLACE] Profit Order Params:: userId={self.userId}, exchange={exchange}, "
                                f"tradingSymbol={trading_symbol}, quantity={remaining_qty}, 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(remaining_qty),
                                price="0.0",
                                product="M",
                                transactionType=transaction_type,
                                priceType="MKT",
                                retention="DAY",
                                triggerPrice="0",
                                mkt_protection="10",
                                remarks=remarks
                            )
                            print(f"[SUCCESS] Profit Split Order for remaining quantity Placed: {response}\n")
                            time.sleep(0.5)
                    else:
                        # Single order for smaller quantities
                        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",
                            mkt_protection="10",
                            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#1234', '15111982', 'TT1582_API', '833d82681166d2a73ba0145a42b8aa6a'],
        'RA': ['RA1383', 'ABcd#1234', '13061983', 'RA1383_API', '025a1f06bb55753e2d1b24689edddd1c'],
        'DB': ['DB1485', 'ABcd#1234', '14121985', 'DB1485_API', 'd9ce8d1261a834458847929c11ea1047'],
        'PR': ['PR5711', '3jx3i4it8uknmhya'],
        'HO': ['HO2208', 'myeqxx43swaeiikn'],
        'XC': ['XC8939', 'mbiqu30s1y4z3v0x']
    }

    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()

'''

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]
    
    # ===== ADD LOCKING HERE =====
    lock_file = f"/tmp/{client_code}.lock"
    
    try:
        lock_fd = open(lock_file, 'w')
        fcntl.flock(lock_fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
    except (IOError, OSError):
        print(f"[SKIP] Another instance of {client_code} is already running")
        sys.exit(0)
    # ===== END LOCKING =====

    client_map = {
        'TT': ['TT1582', 'ABcd#1234', '15111982', 'TT1582_API', '833d82681166d2a73ba0145a42b8aa6a'],
        'RA': ['RA1383', 'ABcd#1234', '13061983', 'RA1383_API', '025a1f06bb55753e2d1b24689edddd1c'],
        'DB': ['DB1485', 'ABcd#1234', '14121985', 'DB1485_API', 'd9ce8d1261a834458847929c11ea1047'],
        'PR': ['PR5711', '3jx3i4it8uknmhya'],
        'HO': ['HO2208', 'myeqxx43swaeiikn'],
        'XC': ['XC8939', 'mbiqu30s1y4z3v0x']
    }

    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}")

    try:
        processor = ClientFileProcessor(client_map[client_code], client_code)
        processor.process_files()
    finally:
        # Release the lock when done
        fcntl.flock(lock_fd, fcntl.LOCK_UN)
        lock_fd.close()