from fastapi import FastAPI, Header, HTTPException, Request
import yaml
import os
import json
from pydantic import BaseModel

from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

# Enable CORS for the dashboard
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"], # In production, restrict this to your dashboard's domain/IP
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

CONFIG_PATH = "config.yaml"
DATA_PATH = "data"

# Shared token for auth (should be in .env in production)
SHARED_TOKEN = os.getenv("SHARED_AUTH_TOKEN", "default_secret_token")

def verify_token(authorization: str = Header(None)):
    if authorization != f"Bearer {SHARED_TOKEN}":
        raise HTTPException(status_code=401, detail="Invalid token")

class ConfigUpdate(BaseModel):
    bot_settings: dict
    strategy_params: dict
    risk_management: dict

@app.get("/api/status")
async def get_status(request: Request, authorization: str = Header(None)):
    verify_token(authorization)
    bot = getattr(request.app.state, 'bot', None)
    if bot:
        status_str = "RUNNING" if bot.is_running else "STOPPED"
        mode = "TESTNET" if bot.config['bot_settings']['testnet'] else "LIVE"
        return {"status": status_str, "mode": mode}
    return {"status": "UNKNOWN", "mode": "UNKNOWN"}

@app.get("/api/balance")
async def get_balance(request: Request, authorization: str = Header(None)):
    verify_token(authorization)
    bot = getattr(request.app.state, 'bot', None)
    if not bot:
        return {"total_usdt": 0.0, "free_usdt": 0.0, "assets": {}}
    
    # Needs to fetch real balances from OrderExecutor
    balances = bot.order_executor.get_portfolio_balances()
    
    free_usdt = 0.0
    if 'USDT' in balances:
        free_usdt = balances['USDT']['free']
    
    # Calculate rough total value (in a real scenario we'd use current prices)
    # We can get current prices from latest_states
    total_usdt = free_usdt
    assets_data = {}
    for asset, bal in balances.items():
        if asset == 'USDT':
            assets_data[asset] = {"amount": bal['total'], "value": bal['total']}
            continue
            
        # Try to find price for AssetUSDT
        symbol = f"{asset}USDT"
        price = 0.0
        if symbol in bot.signal_generator.latest_states:
            price = bot.signal_generator.latest_states[symbol]['price']
        
        value = bal['total'] * price
        total_usdt += value
        if value > 1.0: # Only show significant assets
            assets_data[asset] = {"amount": bal['total'], "value": value}

    return {"total_usdt": total_usdt, "free_usdt": free_usdt, "assets": assets_data}

@app.get("/api/state")
async def get_state(request: Request, authorization: str = Header(None)):
    verify_token(authorization)
    bot = getattr(request.app.state, 'bot', None)
    if not bot:
        return {}
    
    return bot.signal_generator.latest_states

@app.get("/api/trades")
async def get_trades(authorization: str = Header(None)):
    verify_token(authorization)
    path = os.path.join(DATA_PATH, "trades_log.json")
    if os.path.exists(path):
        with open(path, 'r') as f:
            return json.load(f)
    return []

@app.post("/api/config")
async def update_config(config: ConfigUpdate, authorization: str = Header(None)):
    verify_token(authorization)
    
    # Save to file
    with open(CONFIG_PATH, 'r') as f:
        current_config = yaml.safe_load(f)
    
    # Update nested dicts
    if config.bot_settings:
        current_config['bot_settings'].update(config.bot_settings)
    if config.strategy_params:
        current_config['strategy_params'].update(config.strategy_params)
    if config.risk_management:
        current_config['risk_management'].update(config.risk_management)
        
    with open(CONFIG_PATH, 'w') as f:
        yaml.dump(current_config, f)
        
    # Update live bot instance if possible
    bot = getattr(app.state, 'bot', None)
    if bot:
        bot.config = current_config
        # Note: changing testnet on the fly might need client re-init
        # but trading_enabled can be toggled easily
        bot.is_running = current_config['bot_settings'].get('trading_enabled', bot.is_running)

    return {"message": "Config updated"}

@app.post("/api/start")
async def start_bot(authorization: str = Header(None)):
    verify_token(authorization)
    bot = getattr(app.state, 'bot', None)
    if bot and not bot.is_running:
        import asyncio
        bot.is_running = True
        asyncio.create_task(bot.trading_loop())
        return {"message": "Bot started"}
    return {"message": "Bot already running or not found"}

@app.post("/api/stop")
async def stop_bot(authorization: str = Header(None)):
    verify_token(authorization)
    bot = getattr(app.state, 'bot', None)
    if bot:
        bot.is_running = False
        return {"message": "Bot stop signal sent"}
    return {"message": "Bot not found"}
