from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, and_
from sqlalchemy.orm import selectinload
from typing import List
from datetime import datetime

from app.database import get_db
from app.models import User, SportEvent, SpreadBet, AdminSettings, EventStatus, SpreadBetStatus, TeamSide, EventComment
from app.schemas.sport_event import SportEvent as SportEventSchema, SportEventWithBets
from app.schemas.event_comment import EventComment as EventCommentSchema, EventCommentCreate, EventCommentsResponse
from app.routers.auth import get_current_user
from app.routers.websocket import broadcast_to_event

router = APIRouter(prefix="/api/v1/sport-events", tags=["sport-events"])


def generate_spread_grid(min_spread: float, max_spread: float, increment: float = 0.5) -> List[float]:
    """Generate list of spread values from min to max with given increment"""
    spreads = []
    current = min_spread
    while current <= max_spread:
        spreads.append(round(current, 1))
        current += increment
    return spreads


@router.get("/public", response_model=List[SportEventSchema])
async def list_public_events(
    skip: int = 0,
    limit: int = 20,
    db: AsyncSession = Depends(get_db)
):
    """Public endpoint - no authentication required"""
    result = await db.execute(
        select(SportEvent)
        .where(SportEvent.status == EventStatus.UPCOMING)
        .order_by(SportEvent.game_time.asc())
        .offset(skip)
        .limit(limit)
    )
    return result.scalars().all()


@router.get("/public/{event_id}")
async def get_public_event_with_grid(
    event_id: int,
    db: AsyncSession = Depends(get_db)
):
    """Public endpoint - shows event details and bet counts without requiring login"""
    # Get event
    result = await db.execute(
        select(SportEvent).where(SportEvent.id == event_id)
    )
    event = result.scalar_one_or_none()
    if not event:
        raise HTTPException(status_code=404, detail="Event not found")

    # Get admin settings for spread increment
    settings_result = await db.execute(select(AdminSettings).limit(1))
    settings = settings_result.scalar_one_or_none()
    increment = settings.spread_increment if settings else 0.5

    # Generate spread grid
    spreads = generate_spread_grid(event.min_spread, event.max_spread, increment)

    # Get existing bets for this event
    bets_result = await db.execute(
        select(SpreadBet)
        .options(selectinload(SpreadBet.creator))
        .where(
            and_(
                SpreadBet.event_id == event_id,
                SpreadBet.status.in_([SpreadBetStatus.OPEN, SpreadBetStatus.MATCHED])
            )
        )
    )
    bets = bets_result.scalars().all()

    # Build spread grid with bet info - public view (no can_take, limited info)
    spread_grid = {}
    for spread in spreads:
        bets_at_spread = [b for b in bets if b.spread == spread]
        if bets_at_spread:
            spread_grid[str(spread)] = [
                {
                    "bet_id": bet.id,
                    "creator_id": bet.creator_id,
                    "creator_username": bet.creator.username,
                    "stake": float(bet.stake_amount),
                    "status": bet.status.value,
                    "team": bet.team.value,
                    "can_take": False  # Public users can't take bets
                }
                for bet in bets_at_spread
            ]
        else:
            spread_grid[str(spread)] = []

    # Count open bets
    open_bets_count = len([b for b in bets if b.status == SpreadBetStatus.OPEN])

    return {
        "id": event.id,
        "sport": event.sport.value,
        "home_team": event.home_team,
        "away_team": event.away_team,
        "official_spread": event.official_spread,
        "game_time": event.game_time.isoformat(),
        "venue": event.venue,
        "league": event.league,
        "min_spread": event.min_spread,
        "max_spread": event.max_spread,
        "min_bet_amount": event.min_bet_amount,
        "max_bet_amount": event.max_bet_amount,
        "status": event.status.value,
        "final_score_home": event.final_score_home,
        "final_score_away": event.final_score_away,
        "created_by": event.created_by,
        "created_at": event.created_at.isoformat(),
        "updated_at": event.updated_at.isoformat(),
        "spread_grid": spread_grid,
        "open_bets_count": open_bets_count
    }


@router.get("", response_model=List[SportEventSchema])
async def list_upcoming_events(
    skip: int = 0,
    limit: int = 20,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user)
):
    result = await db.execute(
        select(SportEvent)
        .where(SportEvent.status == EventStatus.UPCOMING)
        .order_by(SportEvent.game_time.asc())
        .offset(skip)
        .limit(limit)
    )
    return result.scalars().all()


@router.get("/{event_id}")
async def get_event_with_grid(
    event_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user)
):
    # Get event
    result = await db.execute(
        select(SportEvent).where(SportEvent.id == event_id)
    )
    event = result.scalar_one_or_none()
    if not event:
        raise HTTPException(status_code=404, detail="Event not found")

    # Get admin settings for spread increment
    settings_result = await db.execute(select(AdminSettings).limit(1))
    settings = settings_result.scalar_one_or_none()
    increment = settings.spread_increment if settings else 0.5

    # Generate spread grid
    spreads = generate_spread_grid(event.min_spread, event.max_spread, increment)

    # Get existing bets for this event
    bets_result = await db.execute(
        select(SpreadBet)
        .options(selectinload(SpreadBet.creator))
        .where(
            and_(
                SpreadBet.event_id == event_id,
                SpreadBet.status.in_([SpreadBetStatus.OPEN, SpreadBetStatus.MATCHED])
            )
        )
    )
    bets = bets_result.scalars().all()

    # Build spread grid with bet info - now supports multiple bets per spread
    spread_grid = {}
    for spread in spreads:
        # Get ALL bets at this spread (both open and matched)
        bets_at_spread = [b for b in bets if b.spread == spread]

        if bets_at_spread:
            spread_grid[str(spread)] = [
                {
                    "bet_id": bet.id,
                    "creator_id": bet.creator_id,
                    "creator_username": bet.creator.username,
                    "stake": float(bet.stake_amount),
                    "status": bet.status.value,
                    "team": bet.team.value,
                    "can_take": (
                        bet.status == SpreadBetStatus.OPEN and
                        bet.creator_id != current_user.id
                    )
                }
                for bet in bets_at_spread
            ]
        else:
            spread_grid[str(spread)] = []

    return {
        "id": event.id,
        "sport": event.sport.value,
        "home_team": event.home_team,
        "away_team": event.away_team,
        "official_spread": event.official_spread,
        "game_time": event.game_time.isoformat(),
        "venue": event.venue,
        "league": event.league,
        "min_spread": event.min_spread,
        "max_spread": event.max_spread,
        "min_bet_amount": event.min_bet_amount,
        "max_bet_amount": event.max_bet_amount,
        "status": event.status.value,
        "final_score_home": event.final_score_home,
        "final_score_away": event.final_score_away,
        "created_by": event.created_by,
        "created_at": event.created_at.isoformat(),
        "updated_at": event.updated_at.isoformat(),
        "spread_grid": spread_grid
    }


@router.get("/{event_id}/comments", response_model=EventCommentsResponse)
async def get_event_comments(
    event_id: int,
    skip: int = 0,
    limit: int = 50,
    db: AsyncSession = Depends(get_db)
):
    """Get comments for an event - public access."""
    # Verify event exists
    event_result = await db.execute(
        select(SportEvent).where(SportEvent.id == event_id)
    )
    event = event_result.scalar_one_or_none()
    if not event:
        raise HTTPException(status_code=404, detail="Event not found")

    # Get comments with user info
    result = await db.execute(
        select(EventComment)
        .options(selectinload(EventComment.user))
        .where(EventComment.event_id == event_id)
        .order_by(EventComment.created_at.asc())
        .offset(skip)
        .limit(limit)
    )
    comments = result.scalars().all()

    # Get total count
    count_result = await db.execute(
        select(EventComment).where(EventComment.event_id == event_id)
    )
    total = len(count_result.scalars().all())

    return EventCommentsResponse(
        comments=[
            EventCommentSchema(
                id=c.id,
                event_id=c.event_id,
                user_id=c.user_id,
                username=c.user.username,
                content=c.content,
                created_at=c.created_at
            )
            for c in comments
        ],
        total=total
    )


@router.post("/{event_id}/comments", response_model=EventCommentSchema)
async def add_event_comment(
    event_id: int,
    comment_data: EventCommentCreate,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user)
):
    """Add a comment to an event - requires authentication."""
    # Verify event exists
    event_result = await db.execute(
        select(SportEvent).where(SportEvent.id == event_id)
    )
    event = event_result.scalar_one_or_none()
    if not event:
        raise HTTPException(status_code=404, detail="Event not found")

    # Create comment
    comment = EventComment(
        event_id=event_id,
        user_id=current_user.id,
        content=comment_data.content
    )
    db.add(comment)
    await db.commit()
    await db.refresh(comment)

    comment_response = EventCommentSchema(
        id=comment.id,
        event_id=comment.event_id,
        user_id=comment.user_id,
        username=current_user.username,
        content=comment.content,
        created_at=comment.created_at
    )

    # Broadcast new comment to event subscribers
    await broadcast_to_event(
        event_id,
        "new_comment",
        {
            "id": comment.id,
            "event_id": comment.event_id,
            "user_id": comment.user_id,
            "username": current_user.username,
            "content": comment.content,
            "created_at": comment.created_at.isoformat()
        }
    )

    return comment_response
