#!/usr/bin/env python3
"""
Activity Simulation Script
Simulates random user activity including:
- New user registrations
- Creating spread bets
- Taking/matching bets
- Adding comments to events and matches
- Cancelling bets

Usage:
    python simulate_activity.py [--iterations N] [--delay SECONDS] [--continuous]
"""

import asyncio
import argparse
import random
from decimal import Decimal
from datetime import datetime
from sqlalchemy import select, and_
from sqlalchemy.orm import selectinload

from app.database import async_session, init_db
from app.models import (
    User, Wallet, SportEvent, SpreadBet, EventComment, MatchComment,
    EventStatus, SpreadBetStatus, TeamSide, Transaction, TransactionType, TransactionStatus
)
from app.utils.security import get_password_hash

# Sample data for generating random users
FIRST_NAMES = [
    "James", "Emma", "Liam", "Olivia", "Noah", "Ava", "Oliver", "Sophia",
    "Elijah", "Isabella", "Lucas", "Mia", "Mason", "Charlotte", "Ethan",
    "Amelia", "Alexander", "Harper", "Henry", "Evelyn", "Sebastian", "Luna",
    "Jack", "Camila", "Aiden", "Gianna", "Owen", "Abigail", "Samuel", "Ella",
    "Ryan", "Scarlett", "Nathan", "Emily", "Caleb", "Elizabeth", "Hunter",
    "Sofia", "Christian", "Avery", "Landon", "Chloe", "Jonathan", "Victoria"
]

LAST_NAMES = [
    "Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller",
    "Davis", "Rodriguez", "Martinez", "Hernandez", "Lopez", "Gonzalez",
    "Wilson", "Anderson", "Thomas", "Taylor", "Moore", "Jackson", "Martin",
    "Lee", "Perez", "Thompson", "White", "Harris", "Sanchez", "Clark",
    "Ramirez", "Lewis", "Robinson", "Walker", "Young", "Allen", "King"
]

# Sample comments for events
EVENT_COMMENTS = [
    "This is going to be a great game!",
    "Home team looking strong this season",
    "I'm betting on the underdog here",
    "What do you all think about the spread?",
    "Last time these teams played it was close",
    "Weather might be a factor today",
    "Key player is out, that changes everything",
    "The odds seem off to me",
    "Anyone else feeling bullish on the away team?",
    "This matchup is always entertaining",
    "Line moved a lot overnight",
    "Sharp money coming in on the home side",
    "Public is heavy on the favorite",
    "Value play on the underdog here",
    "Injury report looking concerning",
    "Home field advantage is huge here",
    "Expecting a low-scoring affair",
    "Over/under seems too high",
    "Rivalry game, throw out the records!",
    "Coach has a great record against this opponent"
]

# Sample comments for matches (between two bettors)
MATCH_COMMENTS = [
    "Good luck!",
    "May the best bettor win",
    "This should be interesting",
    "I'm feeling confident about this one",
    "Let's see how this plays out",
    "Nice bet, looking forward to the game",
    "GL HF",
    "First time betting against you",
    "Rematch from last week!",
    "I've been waiting for this matchup",
    "Your team doesn't stand a chance!",
    "We'll see about that...",
    "Close game incoming",
    "I'll be watching every play",
    "Don't count your winnings yet!"
]


async def create_random_user(db) -> User | None:
    """Create a new random user with a wallet."""
    first = random.choice(FIRST_NAMES)
    last = random.choice(LAST_NAMES)

    # Generate unique username
    suffix = random.randint(100, 9999)
    username = f"{first.lower()}{last.lower()}{suffix}"
    email = f"{username}@example.com"

    # Check if user already exists
    existing = await db.execute(
        select(User).where(User.username == username)
    )
    if existing.scalar_one_or_none():
        return None

    user = User(
        email=email,
        username=username,
        password_hash=get_password_hash("password123"),
        display_name=f"{first} {last}"
    )
    db.add(user)
    await db.flush()

    # Create wallet with random starting balance
    starting_balance = Decimal(str(random.randint(500, 5000)))
    wallet = Wallet(
        user_id=user.id,
        balance=starting_balance,
        escrow=Decimal("0.00")
    )
    db.add(wallet)
    await db.commit()

    print(f"  [USER] Created user: {user.username} with ${starting_balance} balance")
    return user


async def create_random_bet(db, users: list[User], events: list[SportEvent]) -> SpreadBet | None:
    """Create a random spread bet on an event."""
    if not users or not events:
        return None

    # Filter for users with sufficient balance
    users_with_balance = []
    for user in users:
        wallet_result = await db.execute(
            select(Wallet).where(Wallet.user_id == user.id)
        )
        wallet = wallet_result.scalar_one_or_none()
        if wallet and wallet.balance >= Decimal("10"):
            users_with_balance.append((user, wallet))

    if not users_with_balance:
        return None

    user, wallet = random.choice(users_with_balance)
    event = random.choice(events)

    # Random spread within event range
    spread = round(random.uniform(event.min_spread, event.max_spread) * 2) / 2  # Round to 0.5

    # Random stake (10-50% of balance, max $500)
    max_stake = min(float(wallet.balance) * 0.5, 500)
    stake = Decimal(str(round(random.uniform(10, max(10, max_stake)), 2)))

    # Random team selection
    team = random.choice([TeamSide.HOME, TeamSide.AWAY])

    bet = SpreadBet(
        event_id=event.id,
        spread=spread,
        team=team,
        creator_id=user.id,
        stake_amount=stake,
        house_commission_percent=Decimal("10.00"),
        status=SpreadBetStatus.OPEN
    )
    db.add(bet)
    await db.commit()

    team_name = event.home_team if team == TeamSide.HOME else event.away_team
    print(f"  [BET] {user.username} created ${stake} bet on {team_name} {'+' if spread > 0 else ''}{spread}")
    return bet


async def take_random_bet(db, users: list[User]) -> SpreadBet | None:
    """Have a random user take an open bet."""
    # Get open bets
    result = await db.execute(
        select(SpreadBet)
        .options(selectinload(SpreadBet.event), selectinload(SpreadBet.creator))
        .where(SpreadBet.status == SpreadBetStatus.OPEN)
    )
    open_bets = result.scalars().all()

    if not open_bets:
        return None

    bet = random.choice(open_bets)

    # Find eligible takers (not creator, has balance)
    eligible_takers = []
    for user in users:
        if user.id == bet.creator_id:
            continue
        wallet_result = await db.execute(
            select(Wallet).where(Wallet.user_id == user.id)
        )
        wallet = wallet_result.scalar_one_or_none()
        if wallet and wallet.balance >= bet.stake_amount:
            eligible_takers.append((user, wallet))

    if not eligible_takers:
        return None

    taker, taker_wallet = random.choice(eligible_takers)

    # Get creator's wallet
    creator_wallet_result = await db.execute(
        select(Wallet).where(Wallet.user_id == bet.creator_id)
    )
    creator_wallet = creator_wallet_result.scalar_one_or_none()

    if not creator_wallet or creator_wallet.balance < bet.stake_amount:
        return None

    # Lock funds for both parties
    creator_wallet.balance -= bet.stake_amount
    creator_wallet.escrow += bet.stake_amount
    taker_wallet.balance -= bet.stake_amount
    taker_wallet.escrow += bet.stake_amount

    # Create transactions
    creator_tx = Transaction(
        user_id=bet.creator_id,
        wallet_id=creator_wallet.id,
        type=TransactionType.ESCROW_LOCK,
        amount=-bet.stake_amount,
        balance_after=creator_wallet.balance,
        reference_id=bet.id,
        description=f"Escrow lock for spread bet #{bet.id}",
        status=TransactionStatus.COMPLETED
    )
    taker_tx = Transaction(
        user_id=taker.id,
        wallet_id=taker_wallet.id,
        type=TransactionType.ESCROW_LOCK,
        amount=-bet.stake_amount,
        balance_after=taker_wallet.balance,
        reference_id=bet.id,
        description=f"Escrow lock for spread bet #{bet.id}",
        status=TransactionStatus.COMPLETED
    )
    db.add(creator_tx)
    db.add(taker_tx)

    # Update bet
    bet.taker_id = taker.id
    bet.status = SpreadBetStatus.MATCHED
    bet.matched_at = datetime.utcnow()

    await db.commit()

    print(f"  [MATCH] {taker.username} took {bet.creator.username}'s ${bet.stake_amount} bet")
    return bet


async def cancel_random_bet(db) -> SpreadBet | None:
    """Cancel a random open bet."""
    result = await db.execute(
        select(SpreadBet)
        .options(selectinload(SpreadBet.creator))
        .where(SpreadBet.status == SpreadBetStatus.OPEN)
    )
    open_bets = result.scalars().all()

    if not open_bets:
        return None

    # 20% chance to cancel
    if random.random() > 0.2:
        return None

    bet = random.choice(open_bets)
    bet.status = SpreadBetStatus.CANCELLED
    await db.commit()

    print(f"  [CANCEL] {bet.creator.username} cancelled their ${bet.stake_amount} bet")
    return bet


async def add_event_comment(db, users: list[User], events: list[SportEvent]) -> EventComment | None:
    """Add a random comment to an event."""
    if not users or not events:
        return None

    user = random.choice(users)
    event = random.choice(events)
    content = random.choice(EVENT_COMMENTS)

    comment = EventComment(
        event_id=event.id,
        user_id=user.id,
        content=content
    )
    db.add(comment)
    await db.commit()

    print(f"  [COMMENT] {user.username} on {event.home_team} vs {event.away_team}: \"{content[:40]}...\"")
    return comment


async def add_match_comment(db, users: list[User]) -> MatchComment | None:
    """Add a random comment to a matched bet."""
    # Get matched bets
    result = await db.execute(
        select(SpreadBet)
        .options(selectinload(SpreadBet.creator), selectinload(SpreadBet.taker))
        .where(SpreadBet.status == SpreadBetStatus.MATCHED)
    )
    matched_bets = result.scalars().all()

    if not matched_bets:
        return None

    bet = random.choice(matched_bets)

    # Comment from either creator or taker
    user = random.choice([bet.creator, bet.taker])
    content = random.choice(MATCH_COMMENTS)

    comment = MatchComment(
        spread_bet_id=bet.id,
        user_id=user.id,
        content=content
    )
    db.add(comment)
    await db.commit()

    print(f"  [CHAT] {user.username} in match #{bet.id}: \"{content}\"")
    return comment


async def run_simulation(iterations: int = 10, delay: float = 2.0, continuous: bool = False):
    """Run the activity simulation."""
    print("=" * 60)
    print("H2H Activity Simulator")
    print("=" * 60)

    await init_db()

    iteration = 0
    while continuous or iteration < iterations:
        iteration += 1
        print(f"\n--- Iteration {iteration} ---")

        async with async_session() as db:
            # Get existing users and events
            users_result = await db.execute(select(User))
            users = list(users_result.scalars().all())

            events_result = await db.execute(
                select(SportEvent).where(SportEvent.status == EventStatus.UPCOMING)
            )
            events = list(events_result.scalars().all())

            if not events:
                print("  No upcoming events found. Run manage_events.py first.")
                if not continuous:
                    break
                await asyncio.sleep(delay)
                continue

            # Random actions with weighted probabilities
            actions = [
                (create_random_user, 0.15),           # 15% - Create user
                (lambda db: create_random_bet(db, users, events), 0.30),  # 30% - Create bet
                (lambda db: take_random_bet(db, users), 0.20),  # 20% - Take bet
                (lambda db: cancel_random_bet(db), 0.05),       # 5% - Cancel bet
                (lambda db: add_event_comment(db, users, events), 0.15),  # 15% - Event comment
                (lambda db: add_match_comment(db, users), 0.15),  # 15% - Match comment
            ]

            # Perform 1-3 random actions per iteration
            num_actions = random.randint(1, 3)
            for _ in range(num_actions):
                # Weighted random selection
                rand = random.random()
                cumulative = 0
                for action_fn, probability in actions:
                    cumulative += probability
                    if rand <= cumulative:
                        try:
                            if action_fn == create_random_user:
                                await action_fn(db)
                            else:
                                await action_fn(db)
                        except Exception as e:
                            print(f"  [ERROR] {e}")
                        break

        await asyncio.sleep(delay)

    print("\n" + "=" * 60)
    print("Simulation complete!")
    print("=" * 60)


def main():
    parser = argparse.ArgumentParser(description="Simulate H2H platform activity")
    parser.add_argument(
        "--iterations", "-n",
        type=int,
        default=10,
        help="Number of simulation iterations (default: 10)"
    )
    parser.add_argument(
        "--delay", "-d",
        type=float,
        default=2.0,
        help="Delay between iterations in seconds (default: 2.0)"
    )
    parser.add_argument(
        "--continuous", "-c",
        action="store_true",
        help="Run continuously until interrupted"
    )

    args = parser.parse_args()

    try:
        asyncio.run(run_simulation(
            iterations=args.iterations,
            delay=args.delay,
            continuous=args.continuous
        ))
    except KeyboardInterrupt:
        print("\n\nSimulation stopped by user.")


if __name__ == "__main__":
    main()
