#!/usr/bin/env python3
"""
Test suite for CryptoClaw Paper Trading System.

Runs through all commands, verifies P&L math, edge cases, and leaderboard.
"""

import os
import sys
import tempfile

# Use a temp DB so we don't pollute production
TEST_DB = os.path.join(tempfile.gettempdir(), "test_paper_trading.db")

# Remove old test DB
if os.path.exists(TEST_DB):
    os.remove(TEST_DB)

from paper_trading import (
    PaperTradingEngine, CommandParser, PaperTradingDB,
    PriceFeed, TradeDirection, SessionMode, COMMISSION_PCT,
    handle_command,
)

# Override DB path for testing
engine = PaperTradingEngine(db_path=TEST_DB)
parser = CommandParser(engine)

# Test users
USER1 = "user_001"
USER1_NAME = "TestTrader1"
USER2 = "user_002"
USER2_NAME = "AlgoLearner"

passed = 0
failed = 0

def test(name, condition, detail=""):
    global passed, failed
    if condition:
        print(f"  ✅ {name}")
        passed += 1
    else:
        print(f"  ❌ {name} — {detail}")
        failed += 1


def run_cmd(user_id, username, text):
    """Shortcut: parse command and return response."""
    return parser.parse(user_id, username, text)


# =============================================================================
print("\n🔧 TEST 1: Price Feed")
# =============================================================================
price = PriceFeed.get_btc_price()
test("BTC price fetched", price > 0, f"price={price}")
test("BTC price reasonable", 10000 < price < 500000, f"price={price}")
print(f"   Current BTC: ${price:,.2f}")


# =============================================================================
print("\n🔧 TEST 2: Session Start (Free Mode)")
# =============================================================================
resp = run_cmd(USER1, USER1_NAME, "/paper start free")
test("Session started", "Paper Trading Started" in resp, resp[:80])
test("Free mode shown", "FREE MODE" in resp)
test("Balance shown", "$20,000" in resp)

# Try starting again — should warn
resp2 = run_cmd(USER1, USER1_NAME, "/paper start free")
test("Duplicate start blocked", "already have an active session" in resp2)


# =============================================================================
print("\n🔧 TEST 3: Balance Check")
# =============================================================================
resp = run_cmd(USER1, USER1_NAME, "/balance")
test("Balance command works", "Balance" in resp)
test("Shows $20,000", "20,000" in resp or "20000" in resp)


# =============================================================================
print("\n🔧 TEST 4: Open BUY Trade")
# =============================================================================
resp = run_cmd(USER1, USER1_NAME, "/buy 0.01")
test("Buy executed", "BUY" in resp and "0.01" in resp, resp[:100])
test("Trade ID assigned", "#" in resp)
test("Commission shown", "Commission" in resp)

# Check positions
resp = run_cmd(USER1, USER1_NAME, "/positions")
test("Position shows up", "#" in resp and "BUY" in resp, resp[:100])


# =============================================================================
print("\n🔧 TEST 5: Open SELL Trade")
# =============================================================================
resp = run_cmd(USER1, USER1_NAME, "/sell 0.02")
test("Sell executed", "SELL" in resp and "0.02" in resp, resp[:100])


# =============================================================================
print("\n🔧 TEST 6: Buy with TP/SL")
# =============================================================================
tp_price = price + 5000
sl_price = price - 3000
resp = run_cmd(USER1, USER1_NAME, f"/buy 0.01 tp={tp_price} sl={sl_price}")
test("Buy with TP/SL", "BUY" in resp and "TP" in resp, resp[:120])


# =============================================================================
print("\n🔧 TEST 7: Positions with Multiple Trades")
# =============================================================================
resp = run_cmd(USER1, USER1_NAME, "/positions")
test("Shows multiple positions", resp.count("#") >= 3, f"Found {resp.count('#')} trades")
test("Shows unrealized P&L", "Unrealized" in resp)
test("Shows equity", "Equity" in resp)


# =============================================================================
print("\n🔧 TEST 8: Close Single Trade")
# =============================================================================
resp = run_cmd(USER1, USER1_NAME, "/close 1")
test("Trade closed", "CLOSED" in resp, resp[:100])
test("P&L shown", "P&L" in resp)
test("Balance updated", "Balance" in resp)


# =============================================================================
print("\n🔧 TEST 9: Close All Trades")
# =============================================================================
resp = run_cmd(USER1, USER1_NAME, "/close all")
test("All trades closed", "Closed" in resp, resp[:100])
test("Net P&L shown", "Net P&L" in resp or "Balance" in resp)


# =============================================================================
print("\n🔧 TEST 10: History")
# =============================================================================
resp = run_cmd(USER1, USER1_NAME, "/history")
test("History shows trades", "#" in resp, resp[:100])
test("Shows P&L per trade", "$" in resp)


# =============================================================================
print("\n🔧 TEST 11: Stats")
# =============================================================================
resp = run_cmd(USER1, USER1_NAME, "/paper stats")
test("Stats returned", "Statistics" in resp, resp[:100])
test("Win rate shown", "Win Rate" in resp)
test("Profit factor shown", "Profit Factor" in resp)
test("Max drawdown shown", "Drawdown" in resp)


# =============================================================================
print("\n🔧 TEST 12: Price Command")
# =============================================================================
resp = run_cmd(USER1, USER1_NAME, "/price")
test("Price returned", "BTC/USD" in resp, resp[:80])


# =============================================================================
print("\n🔧 TEST 13: Second User + Leaderboard")
# =============================================================================
resp = run_cmd(USER2, USER2_NAME, "/paper start free")
test("User 2 started", "Paper Trading Started" in resp)

resp = run_cmd(USER2, USER2_NAME, "/buy 0.05")
test("User 2 bought", "BUY" in resp and "0.05" in resp)

resp = run_cmd(USER1, USER1_NAME, "/paper leaderboard")
test("Leaderboard shows both users", USER1_NAME in resp and USER2_NAME in resp, resp[:200])
test("Leaderboard has ranking", "🥇" in resp or "🥈" in resp)

# Also test shortcut
resp = run_cmd(USER1, USER1_NAME, "/leaderboard")
test("Shortcut /leaderboard works", "Leaderboard" in resp)


# =============================================================================
print("\n🔧 TEST 14: Edge Cases")
# =============================================================================
# Invalid lot size
resp = run_cmd(USER1, USER1_NAME, "/buy abc")
test("Invalid lots rejected", "Invalid" in resp or "❌" in resp)

# Zero lots
resp = run_cmd(USER1, USER1_NAME, "/buy 0")
test("Zero lots rejected", "Invalid" in resp or "❌" in resp)

# Negative lots
resp = run_cmd(USER1, USER1_NAME, "/buy -1")
test("Negative lots rejected", "Invalid" in resp or "❌" in resp)

# Huge lots (margin check)
resp = run_cmd(USER1, USER1_NAME, "/buy 100")
test("Excessive lots rejected", "Insufficient" in resp or "Max" in resp or "❌" in resp)

# Close non-existent trade
resp = run_cmd(USER1, USER1_NAME, "/close 9999")
test("Non-existent trade error", "not found" in resp or "❌" in resp)

# Missing target for close
resp = run_cmd(USER1, USER1_NAME, "/close")
test("Missing close target error", "Usage" in resp or "❌" in resp)

# Wrong TP direction
resp = run_cmd(USER1, USER1_NAME, f"/buy 0.01 tp={price - 1000}")
test("Wrong TP direction rejected", "must be above" in resp or "❌" in resp)

# Wrong SL direction
resp = run_cmd(USER1, USER1_NAME, f"/buy 0.01 sl={price + 1000}")
test("Wrong SL direction rejected", "must be below" in resp or "❌" in resp)


# =============================================================================
print("\n🔧 TEST 15: Reset")
# =============================================================================
resp = run_cmd(USER1, USER1_NAME, "/paper reset")
test("Reset confirmed", "reset" in resp.lower())

# Verify clean slate
resp = run_cmd(USER1, USER1_NAME, "/balance")
test("No session after reset", "No active session" in resp)

# Can start again
resp = run_cmd(USER1, USER1_NAME, "/paper start free")
test("Can restart after reset", "Paper Trading Started" in resp)


# =============================================================================
print("\n🔧 TEST 16: Guided Mode")
# =============================================================================
resp = run_cmd(USER1, USER1_NAME, "/paper reset")
resp = run_cmd(USER1, USER1_NAME, "/paper start guided")
test("Guided mode starts", "GUIDED MODE" in resp, resp[:80])
test("Shows algo config", "0.03" in resp or "Hedge-Mart" in resp)

# Manual trading blocked in guided mode
resp = run_cmd(USER1, USER1_NAME, "/buy 0.01")
test("Manual trading blocked", "GUIDED" in resp or "algo trades" in resp.lower(), resp[:80])


# =============================================================================
print("\n🔧 TEST 17: Help Command")
# =============================================================================
resp = run_cmd(USER1, USER1_NAME, "/paper help")
test("Help returned", "Getting Started" in resp or "Commands" in resp or "Paper Trading" in resp)

resp = run_cmd(USER1, USER1_NAME, "/paper")
test("Bare /paper shows help", "Paper Trading" in resp)


# =============================================================================
print("\n🔧 TEST 18: Commission Math Verification")
# =============================================================================
# Reset and do a precise test
engine2 = PaperTradingEngine(db_path=TEST_DB)
engine2.db.reset_session("math_test")
engine2.start("math_test", "MathTester", "free")

btc_price = PriceFeed.get_btc_price()
# Open a trade
engine2.buy("math_test", "0.01")
session = engine2.db.get_session("math_test")

# Entry commission should be: 0.01 * price * 0.04/100
expected_commission = 0.01 * btc_price * (COMMISSION_PCT / 100)
actual_commission = 20000.0 - session.balance
test(
    "Entry commission correct",
    abs(actual_commission - expected_commission) < 1.0,  # allow $1 tolerance (price moves)
    f"expected≈${expected_commission:.2f}, got=${actual_commission:.2f}"
)


# =============================================================================
print("\n🔧 TEST 19: handle_command() top-level function")
# =============================================================================
resp = handle_command("top_level_user", "TopUser", "/paper help")
test("handle_command works", resp is not None and "Paper Trading" in resp)

resp = handle_command("top_level_user", "TopUser", "just a normal message")
test("Non-command returns None", resp is None)


# =============================================================================
# Cleanup
# =============================================================================
if os.path.exists(TEST_DB):
    os.remove(TEST_DB)


# =============================================================================
# Summary
# =============================================================================
print(f"\n{'='*50}")
total = passed + failed
print(f"📊 Results: {passed}/{total} passed ({passed/total*100:.0f}%)")
if failed:
    print(f"⚠️  {failed} test(s) FAILED")
else:
    print("✅ ALL TESTS PASSED!")
print(f"{'='*50}\n")

sys.exit(0 if failed == 0 else 1)
