import logging
import time
import uuid
from zerodha.zerodha_ticker import KiteTicker
from pymongo import MongoClient
from datetime import date, datetime


# MongoDB connection and collections
# client = MongoClient("mongodb://192.168.31.176:27017/")
# database = client["zerodha_test"]

client = MongoClient("mongodb://jenya:DJenya$Mongo%40St0ckDB@127.0.0.1:27017/")
database = client["trade_iq"]
creds_collection = database["zerodha_credentials"]
orders_collection = database["orders"]
holdings_collection = database["holdings"]
today = date.today()

# Fetch credentials
enctoken_obj = creds_collection.find_one({"date": str(today)}, {"_id": 0, "enctoken": 1})
websocket_obj = creds_collection.find_one({"date": str(today)}, {"_id": 0, "websocket": 1})
websocket = websocket_obj["websocket"]
ticker = KiteTicker(socket_url=websocket)


def place_order(orders: list):
    global executed_orders, execution_count

    # Store tokens and a mapping for quick access
    order_map = {order["instrument_token"]: order for order in orders}

    # Track executed orders to ensure each one is executed only once
    executed_orders = set()
    execution_count = 0  # To keep track of executions for batch unsubscribe

    # Batch the tokens in chunks of 100
    tokens_batches = [list(order_map.keys())[i:i + 100] for i in range(0, len(order_map), 100)]

    # Callback for tick reception
    def on_ticks(ws, ticks):
        global executed_orders, execution_count

        for tick in ticks:
            instrument_token = tick["instrument_token"]
            ltp = tick.get("last_price")  # Skip if LTP is not found

            if instrument_token in order_map and instrument_token not in executed_orders and ltp is not None:
                order = order_map[instrument_token]
                symbol = order["symbol"]
                quantity = order["quantity"]
                invested_amount = quantity * ltp  # Calculate invested amount
                exchange = order["exchange"]
                timestamp = datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S")

                # Retrieve or create holding entry for this instrument token
                holding_entry = holdings_collection.find_one({"instrument_token": instrument_token})
                if not holding_entry:
                    holding_entry = {
                        "_id": str(uuid.uuid4()),
                        "instrument_token": instrument_token,
                        "total_current_value": 0,
                        "holdings": {
                            "summary": [],
                            "total": {
                                "total_quantity": 0,
                                "total_avg_price": 0,
                                "total_invested": 0
                            }
                        },
                        "created_at": datetime.utcnow(),
                        "last_updated": datetime.utcnow(),
                        "allocated_balance": 1000,
                        "symbol": symbol,
                        "exchange": exchange
                    }
                    holdings_collection.insert_one(holding_entry)

                holding_entry["holdings"]["summary"].append({
                    "quantity": quantity,
                    "avg_price": ltp,
                    "invested_amount": invested_amount,
                    "executed_at": datetime.utcnow()
                })

                # Update the total summary
                total_quantity = holding_entry["holdings"]["total"]["total_quantity"] + quantity
                total_invested = holding_entry["holdings"]["total"]["total_invested"] + invested_amount
                total_avg_price = total_invested / total_quantity if total_quantity > 0 else 0

                holding_entry["holdings"]["total"] = {
                    "total_quantity": total_quantity,
                    "total_avg_price": round(total_avg_price, 2),
                    "total_invested": round(total_invested, 2)
                }

                # Update the document's total invested amount and timestamp
                holding_entry["last_updated"] = datetime.utcnow()

                # Save the updated entry in the database
                holdings_collection.update_one(
                    {"instrument_token": instrument_token},
                    {"$set": holding_entry}
                )

                logging.info(f"Executed paper trade for {symbol} at price {ltp} with quantity {quantity}.")

                # Mark the order as executed for this batch
                executed_orders.add(instrument_token)
                execution_count += 1

                # Stop the ticker once all orders are executed
                if len(executed_orders) == len(order_map):
                    logging.info("All orders have been executed. Stopping the ticker.")
                    ws.unsubscribe(tokens_batches)
                    ws.close()
                    break

                # Unsubscribe every 200 executions and continue with the next batch
                if execution_count % 200 == 0:
                    logging.info("200 executions completed. Unsubscribing current batch.")
                    ws.unsubscribe(tokens_batches)
                    ws.close()
                    break  # Exit to proceed with the next batch

    # Callback for successful connection
    def on_connect(ws, response):
        logging.info("Connected successfully.")
        for batch in tokens_batches:
            ws.subscribe(batch)

    # Callback when connection is closed
    def on_close(ws, code, reason):
        logging.info(f"Connection closed: {code} - {reason}")

    # Callback when connection closed with error
    def on_error(ws, code, reason):
        logging.info(f"Connection error: {code} - {reason}")

    # Callback when reconnect is in progress
    def on_reconnect(ws, attempts_count):
        logging.info(f"Reconnecting: {attempts_count}")

    # Callback when all reconnect attempts failed
    def on_noreconnect(ws):
        logging.info("Reconnect failed.")

    # Assign the callbacks
    ticker.on_ticks = on_ticks
    ticker.on_close = on_close
    ticker.on_error = on_error
    ticker.on_connect = on_connect
    ticker.on_reconnect = on_reconnect
    ticker.on_noreconnect = on_noreconnect

    # Connect to WebSocket in threaded mode
    ticker.connect(threaded=True)

    # Main loop to manage batches and ensure LTP mode
    current_batch = 0
    while len(executed_orders) < len(order_map):
        if ticker.is_connected() and current_batch < len(tokens_batches):
            batch = tokens_batches[current_batch]
            logging.info(f"### Setting LTP mode for batch {current_batch + 1}")
            ticker.set_mode(ticker.MODE_LTP, batch)
            current_batch += 1
        time.sleep(1)
