"""
Recommendation API - Personalized suggestions for users.
"""
from typing import List, Optional
from fastapi import APIRouter, Depends, Query
from sqlmodel import Session, select, desc, func
from pydantic import BaseModel
from datetime import datetime, timedelta
from database import get_session
from models import Show, Vertical, UserVerticalPreference, Attendance, Rating, Performance, Song, Venue, BandMembership
from auth import get_current_user
from models import User

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


class RecommendedShow(BaseModel):
    id: int
    date: str
    venue_name: str | None
    vertical_name: str
    vertical_slug: str
    reason: str  # "Recent Show", "Highly Rated", "Trending"


class RecommendedPerformance(BaseModel):
    id: int
    song_title: str
    show_date: str
    vertical_name: str
    avg_rating: float
    notes: str | None


@router.get("/shows/recent", response_model=List[RecommendedShow])
def get_recent_subscriptions(
    limit: int = 10,
    session: Session = Depends(get_session),
    current_user: User = Depends(get_current_user)
):
    """
    Get recent shows from bands the user follows, excluding attended shows.
    """
    # 1. Get user preferences
    prefs = session.exec(
        select(UserVerticalPreference).where(UserVerticalPreference.user_id == current_user.id)
    ).all()
    
    if not prefs:
        # Fallback: Just return recent shows from featured verticals
        # For now, return empty or generic
        return []

    subscribed_vertical_ids = [p.vertical_id for p in prefs]
    
    # 2. Get Attended Show IDs
    attended = session.exec(
        select(Attendance.show_id).where(Attendance.user_id == current_user.id)
    ).all()
    attended_ids = set(attended)
    
    # 3. Query Recent Shows
    query = (
        select(Show)
        .where(Show.vertical_id.in_(subscribed_vertical_ids))
        .where(Show.date <= datetime.now())  # Past shows only
        .where(Show.date >= datetime.now() - timedelta(days=90)) # Last 90 days
        .order_by(desc(Show.date))
        .limit(limit * 2) # Fetch extra to filter
    )
    
    shows = session.exec(query).all()
    
    results = []
    for show in shows:
        if show.id in attended_ids:
            continue
            
        vertical = session.get(Vertical, show.vertical_id)
        venue = session.get(Venue, show.venue_id) if show.venue_id else None
        
        results.append(RecommendedShow(
            id=show.id,
            date=show.date.strftime("%Y-%m-%d"),
            venue_name=venue.name if venue else "Unknown Venue",
            vertical_name=vertical.name,
            vertical_slug=vertical.slug,
            reason="Recent from your bands"
        ))
        
        if len(results) >= limit:
            break
            
    return results


@router.get("/performances/top", response_model=List[RecommendedPerformance])
def get_top_rated_tracks(
    limit: int = 10,
    session: Session = Depends(get_session),
    current_user: User = Depends(get_current_user)
):
    """
    Get top rated performances from bands the user follows.
    """
    prefs = session.exec(
        select(UserVerticalPreference).where(UserVerticalPreference.user_id == current_user.id)
    ).all()
    
    if not prefs:
        return []

    subscribed_vertical_ids = [p.vertical_id for p in prefs]
    
    # Complex query: Join Performance -> Show -> Vertical, Join Rating
    # Getting avg rating per performance
    
    # This might be slow on large datasets without materialized view. 
    # Optimized approach: Query Rating table, group by performance_id, filter by subscribed verticals
    
    results = session.exec(
        select(
            Rating.performance_id,
            func.avg(Rating.score).label("average"),
            func.count(Rating.id).label("count")
        )
        .join(Performance, Rating.performance_id == Performance.id)
        .join(Show, Performance.show_id == Show.id)
        .where(Show.vertical_id.in_(subscribed_vertical_ids))
        .where(Rating.performance_id.isnot(None))
        .group_by(Rating.performance_id)
        .having(func.count(Rating.id) >= 1) # At least 1 rating
        .order_by(desc("average"))
        .limit(limit)
    ).all()
    
    recommendations = []
    for row in results:
        perf_id, avg, count = row
        perf = session.get(Performance, perf_id)
        if not perf: continue
        
        show = session.get(Show, perf.show_id)
        song = session.get(Song, perf.song_id)
        vertical = session.get(Vertical, show.vertical_id)
        
        recommendations.append(RecommendedPerformance(
            id=perf.id,
            song_title=song.title,
            show_date=show.date.strftime("%Y-%m-%d"),
            vertical_name=vertical.name,
            avg_rating=round(avg, 1),
            notes=f"Rated {round(avg, 1)}/10 by {count} fans"
        ))
        
    return recommendations
