from typing import List
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlmodel import Session, select, func
from database import get_session
from models import Performance, PerformanceNickname, Tag, EntityTag, Show, Venue, Rating, Review
from schemas import PerformanceDetailRead, PerformanceNicknameCreate, PerformanceNicknameRead, PerformanceReadWithShow
from auth import get_current_user

router = APIRouter(prefix="/performances", tags=["performances"])

@router.get("/{slug}", response_model=PerformanceDetailRead)
def read_performance(slug: str, session: Session = Depends(get_session)):
    performance = session.exec(
        select(Performance).where(Performance.slug == slug)
    ).first()
    
    if not performance:
        raise HTTPException(status_code=404, detail="Performance not found")
    
    performance_id = performance.id  # Use actual ID for lookups
    
    # --- Calculate Stats & Navigation ---
    # Get all performances of this song, ordered by date
    # Join Show and Venue for list display
    all_perfs_data = session.exec(
        select(Performance, Show, Venue)
        .join(Show, Performance.show_id == Show.id)
        .outerjoin(Venue, Show.venue_id == Venue.id)
        .where(Performance.song_id == performance.song_id)
        .order_by(Show.date)
    ).all()
    
    # Find current index
    current_index = -1
    for i, (p, s, v) in enumerate(all_perfs_data):
        if p.id == performance_id:
            current_index = i
            break
            
    prev_slug = None
    next_slug = None
    prev_id = None
    next_id = None
    gap = 0
    times_played = current_index + 1 # 1-based count
    
    if current_index > 0:
        prev_perf = all_perfs_data[current_index - 1][0]
        prev_id = prev_perf.id
        prev_slug = prev_perf.slug
        
        # Calculate Gap
        prev_date = all_perfs_data[current_index - 1][1].date
        current_date = all_perfs_data[current_index][1].date
        
        gap = session.exec(
            select(func.count(Show.id))
            .where(Show.date > prev_date)
            .where(Show.date < current_date)
        ).one()
        
    if current_index < len(all_perfs_data) - 1:
        next_perf = all_perfs_data[current_index + 1][0]
        next_id = next_perf.id
        next_slug = next_perf.slug

    # Fetch ratings AND review scores for all performances of this song
    # Combine both Rating and Review tables for comprehensive scores
    rating_stats = session.exec(
        select(Rating.performance_id, func.avg(Rating.score), func.count(Rating.id))
        .where(Rating.performance_id.is_not(None))
        .group_by(Rating.performance_id)
    ).all()
    
    review_stats = session.exec(
        select(Review.performance_id, func.avg(Review.score), func.count(Review.id))
        .where(Review.performance_id.is_not(None))
        .where(Review.score.is_not(None))
        .group_by(Review.performance_id)
    ).all()
    
    # Merge rating and review stats
    rating_map = {}
    for row in rating_stats:
        perf_id = row[0]
        rating_map[perf_id] = {"avg": row[1] or 0.0, "count": row[2] or 0}
    
    for row in review_stats:
        perf_id = row[0]
        if perf_id in rating_map:
            # Combine averages weighted by count
            existing = rating_map[perf_id]
            total_count = existing["count"] + (row[2] or 0)
            if total_count > 0:
                combined_avg = ((existing["avg"] * existing["count"]) + ((row[1] or 0) * (row[2] or 0))) / total_count
                rating_map[perf_id] = {"avg": combined_avg, "count": total_count}
        else:
            rating_map[perf_id] = {"avg": row[1] or 0.0, "count": row[2] or 0}

    # Build other_performances list
    other_performances = []
    for p, s, v in all_perfs_data:
        if p.id == performance_id:
            continue
            
        stats = rating_map.get(p.id, {"avg": 0.0, "count": 0})
        
        perf_read = PerformanceReadWithShow(
            **p.model_dump(),
            song=performance.song, # Reuse loaded song object
            show_date=s.date,
            show_slug=s.slug,
            venue_name=v.name if v else "Unknown Venue",
            venue_city=v.city if v else "Unknown City",
            venue_state=v.state if v else None,
            avg_rating=stats["avg"],
            total_reviews=stats["count"],
            nicknames=p.nicknames
        )
        other_performances.append(perf_read)

    # Sort by rating desc, then date desc
    other_performances.sort(key=lambda x: (x.avg_rating or 0, x.show_date), reverse=True)

    # --- Calculate Ranking for Current Performance ---
    current_perf_stats = rating_map.get(performance_id, {"avg": 0.0, "count": 0})
    current_avg = current_perf_stats["avg"] or 0.0
    current_rating_count = current_perf_stats["count"]
    
    # Build list of all performances with their ratings for ranking
    all_rated_perfs = []
    for p, s, v in all_perfs_data:
        stats = rating_map.get(p.id, {"avg": 0.0, "count": 0})
        all_rated_perfs.append({"id": p.id, "avg": stats["avg"] or 0.0, "count": stats["count"]})
    
    # Sort by avg rating desc
    all_rated_perfs.sort(key=lambda x: x["avg"], reverse=True)
    
    # Find rank (1-indexed)
    rank = None
    for i, item in enumerate(all_rated_perfs):
        if item["id"] == performance_id:
            rank = i + 1
            break
    
    total_versions = len(all_perfs_data)
    
    # Heady = #1 ranked with avg >= 8.0 OR top 20% with at least 1 rating and avg >= 8.0
    is_heady = False
    if current_rating_count > 0 and current_avg >= 8.0:
        if rank == 1:  # Top ranked version always heady if avg >= 8.0
            is_heady = True
        elif rank and total_versions > 0:
            percentile = rank / total_versions
            is_heady = percentile <= 0.2  # Top 20%

    # Construct response manually to include extra fields
    # We need to ensure nested models (show, song) are validated correctly
    perf_dict = performance.model_dump()
    perf_dict['show'] = performance.show
    perf_dict['song'] = performance.song
    perf_dict['nicknames'] = performance.nicknames
    perf_dict['previous_performance_id'] = prev_id
    perf_dict['previous_performance_slug'] = prev_slug
    perf_dict['next_performance_id'] = next_id
    perf_dict['next_performance_slug'] = next_slug
    perf_dict['gap'] = gap
    perf_dict['times_played'] = times_played
    perf_dict['other_performances'] = other_performances
    perf_dict['rank'] = rank
    perf_dict['total_versions'] = total_versions
    perf_dict['avg_rating'] = current_avg
    perf_dict['rating_count'] = current_rating_count
    perf_dict['is_heady'] = is_heady
    
    return perf_dict

@router.post("/{performance_id}/nicknames", response_model=PerformanceNicknameRead)
def suggest_nickname(
    performance_id: int, 
    nickname: PerformanceNicknameCreate, 
    session: Session = Depends(get_session),
    current_user = Depends(get_current_user)
):
    # Check if performance exists
    perf = session.get(Performance, performance_id)
    if not perf:
        raise HTTPException(status_code=404, detail="Performance not found")
        
    db_nickname = PerformanceNickname.model_validate(nickname)
    db_nickname.performance_id = performance_id
    db_nickname.suggested_by = current_user.id
    db_nickname.status = "pending" # Default to pending
    
    session.add(db_nickname)
    session.commit()
    session.refresh(db_nickname)
    return db_nickname
