from flask import Flask, request, jsonify
from pyngrok import ngrok
from alpaca_trade_api.rest import REST, TimeFrame
import logging

# Flask app setup
app = Flask(__name__)

# Setup logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

# Alpaca API credentials
ALPACA_API_KEY = "PKVIYT9NC2P2GHCB43CJ"  # Replace with your actual API Key
ALPACA_API_SECRET = "pcnilPA8Zj2gO4pPs2Xth2io063gSymfX35moeac"  # Replace with your actual Secret Key
ALPACA_BASE_URL = "https://paper-api.alpaca.markets"

# Initialize Alpaca API
alpaca = REST(ALPACA_API_KEY, ALPACA_API_SECRET, base_url=ALPACA_BASE_URL)

# Secret key for the webhook
WEBHOOK_SECRET_KEY = "Yuvi23780557"

def reformat_ticker(symbol):
    """Reformat the ticker symbol to remove any exchange prefix."""
    return symbol.split(':')[-1]

def get_current_price(symbol):
    """Retrieve the current price of the given symbol."""
    try:
        barset = alpaca.get_bars(symbol, TimeFrame.Minute, limit=1).df
        if not barset.empty:
            return barset['close'].iloc[-1]  # Return the close price of the last bar
        else:
            logging.warning(f"No recent trading data for {symbol}")
            return None  # or some default price
    except Exception as e:
        logging.error(f"Error retrieving price for {symbol}: {e}")
        return None  # or some default price

def get_user_position(symbol):
    """Retrieve the user's current position for the given symbol."""
    try:
        position = alpaca.get_position(symbol)
        return position.qty, position.avg_entry_price
    except Exception as e:
        logging.warning(f"Could not retrieve position for {symbol}: {e}")
        return 0, 0  # Defaults if no position is found

@app.route('/webhook', methods=['POST'])
def webhook():
    """Handle incoming webhook requests."""
    data = request.json

    # Validate data
    if not all(k in data for k in ["secret_key", "ticker", "quantity", "action"]):
        logging.warning("Missing data in request")
        return jsonify({"error": "Missing data"}), 400

    # Check if the secret key matches
    if data.get('secret_key') != WEBHOOK_SECRET_KEY:
        logging.warning("Invalid secret key received")
        return jsonify({"error": "Invalid secret key"}), 403

    try:
        # Reformat the ticker symbol
        symbol = reformat_ticker(data['ticker'])
        logging.info(f"Received quantity as a string: {data['quantity']}")
        quantity = float(data['quantity'])
        logging.info(f"Parsed quantity as a float: {quantity}")
        action = data['action'].lower()

        # Retrieve current price and user's position
        current_price = get_current_price(symbol)
        user_qty, avg_entry_price = get_user_position(symbol)

        # Log order attempt and additional info
        logging.info(f"Order Attempt - Symbol: {symbol}, Quantity: {quantity}, Action: {action}")
        logging.info(f"Current Price: {current_price}, User Position: {user_qty} at avg price {avg_entry_price}")

        # Process the order
        if action in ['buy', 'sell']:
            formatted_quantity = "{:.8f}".format(quantity)
            logging.info(f"Formatted quantity: {formatted_quantity}")
            # Ensure the quantity is greater than 0
            if quantity <= 0:
                raise ValueError("Quantity must be greater than 0")
            order = alpaca.submit_order(symbol=symbol, qty=formatted_quantity, side=action, type='market', time_in_force='gtc')
            logging.info(f"Order submitted successfully - Order ID: {order.id}")
            return jsonify({"message": "Order submitted", "order_id": order.id}), 200
        else:
            logging.warning("Invalid action received")
            return jsonify({"error": "Invalid action"}), 400
    except ValueError as e:
        # Handle the specific error when conversion fails or quantity is invalid
        logging.error(f"Error in processing order: {e}")
        return jsonify({"error": "Invalid quantity format"}), 400
    except Exception as e:
        # Handle any other exceptions
        logging.error(f"Error in processing order: {e}")
        return jsonify({"error": str(e)}), 500

if __name__ == '__main__':
    # Start ngrok when app is run
    ngrok_tunnel = ngrok.connect(5000, bind_tls=True, hostname='ysps.ngrok.io')
    logging.info(f'ngrok tunnel "webhook" -> {ngrok_tunnel.public_url}')

    # Run the Flask app
    app.run(port=5000)