import os
import sys
import shutil
import requests
import pytz
from datetime import datetime
from thefirstock import thefirstock
from kiteconnect import KiteConnect

# ------------------- SYMBOL CONFIGURATION -------------------

class SymbolConfig:
    DEFAULT_PROPERTIES = {
        "NIFTY":  {"quantity": 75, "expiry": "02SEP25", "exchange": "NFO", "enabled": True, "multiplier": 1, "profit": 100},
        "SENSEX": {"quantity": 20, "expiry": "25819",   "exchange": "BFO", "enabled": True, "multiplier": 1, "profit": 220},
    }

    CLIENT_OVERRIDES = {
        'TT': {
            "NIFTY":  {"enabled": True, "multiplier": 5},
            "SENSEX": {"enabled": False, "multiplier": 1}
        },
        'RA': {
            "NIFTY":  {"enabled": False, "multiplier": 1},
            "SENSEX": {"enabled": True, "multiplier": 1}
        },
        'PR': {
            "NIFTY":  {"enabled": True, "multiplier": 1, "expiry": "25724"},
            "SENSEX": {"enabled": True, "multiplier": 1}
        }
    }

    @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')
        self.symbol_properties = SymbolConfig.get_properties(client_code)

        self.base_folder = "/var/www/html/ACTIONS_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

        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 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 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 = thefirstock.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(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 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 process_files(self):
        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

        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'

        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 = thefirstock.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):
        print("[TODO] Zerodha profit booking logic not implemented.")

    def placeProfitOrderFirstock(self):
        try:
            positions = thefirstock.firstock_PositionBook(userId=self.client_id)['data']
            orders = thefirstock.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 = thefirstock.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 = thefirstock.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("Usage: python script_name.py <client_code>")
        sys.exit(1)

    client_code = sys.argv[1]
    client_map = {
        'TT': ['TT1582', 'ABcd@12345', '15111982', 'TT1582_API', '833d82681166d2a73ba0145a42b8aa6a'],
        'RA': ['RA1383', 'ABcd@1234', '13061983', 'RA1383_API', '025a1f06bb55753e2d1b24689edddd1c'],
        'GR': ['GR1453', 'O9i8u7y6$$', '14031953', 'GR1453_API', '21cd2c7c7613d5ef713b803e63004303'],
        'PR': ['PR5711', '3jx3i4it8uknmhya']
    }

    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()
