from datetime import datetime, date
from bson import ObjectId
import logging

import asyncio
from typing import List, Dict
from twisted.internet import reactor
from zerodha.zerodha_ticker import KiteTicker
from pymongo import MongoClient

def serialize_order_o(order):
    """Convert MongoDB document to a JSON-compatible format."""
    # Convert ObjectId to string
    order['_id'] = str(order['_id'])

    # Convert datetime objects to ISO format strings
    for key, value in order.items():
        if isinstance(value, ObjectId):
            order[key] = str(value)
        elif isinstance(value, datetime):
            order[key] = value.isoformat()  # Convert datetime to ISO format string

    return order

client = MongoClient("mongodb://jenya:DJenya$Mongo%40St0ckDB@172.105.59.175: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)

async def place_order(orders: List[Dict]):
    order_map = {order["instrument_token"]: order for order in orders}
    print("********************************")

    # Fetch credentials - now using async find_one
    creds = creds_collection.find_one({"date": str(today)}, {"_id": 0, "enctoken": 1, "websocket": 1})
    if not creds or "enctoken" not in creds or "websocket" not in creds:
        raise ValueError("Incomplete or missing credentials for today's date.")

    websocket = creds["websocket"]
    kws = KiteTicker(socket_url=websocket)
    print("********************************")

    logging.basicConfig(level=logging.INFO)
    logger = logging.getLogger(__name__)

    def on_connect(ws, response):
        logger.info("WebSocket connected")
        ws.subscribe(list(order_map.keys()))
        ws.set_mode(kws.MODE_LTP, list(order_map.keys()))

    async def process_tick(ws, ticks):
        for tick in ticks:
            instrument_token = tick["instrument_token"]
            ltp = tick.get("last_price")

            if instrument_token and ltp:
                order = order_map[instrument_token]
                symbol = order["symbol"]
                quantity = order["quantity"]

                invested_amount = float(quantity) * float(ltp)
                action = order["action"]
                exchange = order["exchange"]
                timestamp = datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S")

                new_summary_entry = {
                    "action": action,
                    "quantity": quantity,
                    "avg_price": ltp,
                    "invested_amount": invested_amount,
                    "executed_at": timestamp
                }

                # Using async find_one
                existing_entry = holdings_collection.find_one({"instrument_token": instrument_token})
                print(instrument_token, "FOUND!!!")

                if existing_entry:
                    updated_summary = existing_entry["holdings"]["summary"] + [new_summary_entry]
                    total_quantity = sum(int(item["quantity"]) for item in updated_summary)
                    total_avg_price = sum(float(item["avg_price"]) for item in updated_summary)
                    total_invested = sum(float(item["invested_amount"]) for item in updated_summary)

                    updated_holding_entry = {
                        "$set": {
                            "last_updated": timestamp,
                            "holdings.summary": updated_summary,
                            "holdings.total": {
                                "total_quantity": total_quantity,
                                "total_avg_price": total_avg_price,
                                "total_invested": round(total_invested, 2)
                            }
                        }
                    }
                    # Using async update_one
                    holdings_collection.update_one(
                        {"instrument_token": instrument_token},
                        updated_holding_entry
                    )
                    logger.info(f"Updated holding for {symbol} at price {ltp} with quantity {quantity}.")
                else:
                    new_holding_entry = {
                        "instrument_token": instrument_token,
                        "symbol": symbol,
                        "exchange": exchange,
                        "allocated_balance": 1000,
                        "created_at": timestamp,
                        "last_updated": timestamp,
                        "holdings": {
                            "summary": [new_summary_entry],
                            "total": {
                                "total_quantity": quantity,
                                "total_avg_price": round(ltp, 2),
                                "total_invested": round(invested_amount, 2)
                            }
                        }
                    }
                    # Using async insert_one
                    holdings_collection.insert_one(new_holding_entry)
                    logger.info(f"Created new entry for {symbol} at price {ltp} with quantity {quantity}.")
        print("Process Finished")

    def on_ticks(ws, ticks):
        logger.info("Ticks received: %s", ticks)
        asyncio.create_task(process_tick(ws, ticks))
        ws.close()

    def on_close(ws, code, reason):
        logger.info(f"WebSocket closed: Code={code}, Reason={reason}")
        if reactor.running:
            kws.stop()
        reactor.callLater(2, kws.connect)

    kws.on_connect = on_connect
    kws.on_ticks = on_ticks
    kws.on_close = on_close

    try:
        if not reactor.running:
            kws.connect()
    except Exception as e:
        logger.error(f"Error during WebSocket operation: {str(e)}")
    finally:
        if reactor.running:
            kws.stop()
