from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from typing import List
from datetime import datetime, timedelta, date
from pydantic import BaseModel

from sqlalchemy import func

from app.models.user_points import UserPoints
from app.schemas.user_points import UserPointsResponse
from app.api.deps import get_db, get_current_user
from app.models.user import User
from app.models.point_transaction import PointTransaction
from app.schemas.point_transaction import PointTransactionResponse
from app.schemas.aggregated_points import AggregatedPointsResponse
from app.services.point_service import calculate_unconverted_points, convert_steps_to_points

router = APIRouter()

@router.get("/", response_model=UserPointsResponse)
async def get_user_points(
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """
    로그인한 사용자의 포인트를 조회합니다.
    만약 사용자에 대한 포인트 레코드가 없으면, 0으로 초기화합니다.
    """
    user_points = db.query(UserPoints).filter(UserPoints.user_id == current_user.id).first()
    if not user_points:
        # 레코드가 없으면 기본값 0으로 신규 생성합니다.
        user_points = UserPoints(user_id=current_user.id, total_points=0)
        db.add(user_points)
        db.commit()
        db.refresh(user_points)
    return user_points

@router.get("/transaction", response_model=List[PointTransactionResponse])
async def get_point_transactions(
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """
    현재 로그인한 사용자의 포인트 트랜잭션 내역을 반환합니다.
    반환되는 항목은 transaction_type, amount, description (그리고 필요시 created_at) 입니다.
    """
    transactions = (
        db.query(PointTransaction)
        .filter(PointTransaction.user_id == current_user.id)
        .order_by(PointTransaction.created_at.desc())
        .all()
    )
    return transactions

@router.get("/weekly", response_model=List[AggregatedPointsResponse])
async def get_weekly_points(
    db: Session = Depends(get_db)
):
    """
    모든 유저의 주간 누적 포인트를 조회합니다.
    현재 주(월요일부터 시작) 이후에 발생한 포인트 트랜잭션을 집계하여
    각 유저별 주간 누적 포인트를 반환합니다.
    """
    now_dt = datetime.utcnow()
    # Compute start of the week (assume Monday as the first day)
    start_of_week = datetime(now_dt.year, now_dt.month, now_dt.day) - timedelta(days=now_dt.weekday())
    
    weekly_points = (
        db.query(
            PointTransaction.user_id,
            func.coalesce(func.sum(PointTransaction.amount), 0).label("total_points")
        )
        .filter(PointTransaction.created_at >= start_of_week)
        .group_by(PointTransaction.user_id)
        .having(func.coalesce(func.sum(PointTransaction.amount), 0) >= 0)
        .all()
    )
    
    # weekly_points is a list of tuples: (user_id, total_points)
    return [{"user_id": user_id, "total_points": total_points} for user_id, total_points in weekly_points]

@router.get("/monthly", response_model=List[AggregatedPointsResponse])
async def get_monthly_points(
    db: Session = Depends(get_db)
):
    """
    모든 유저의 월간 누적 포인트를 조회합니다.
    이번 달의 초(1일) 이후에 발생한 포인트 트랜잭션을 집계하여
    각 유저별 월간 누적 포인트를 반환합니다.
    """
    now_dt = datetime.utcnow()
    # Compute start of the current month
    start_of_month = datetime(now_dt.year, now_dt.month, 1)
    
    monthly_points = (
        db.query(
            PointTransaction.user_id,
            func.coalesce(func.sum(PointTransaction.amount), 0).label("total_points")
        )
        .filter(PointTransaction.created_at >= start_of_month)
        .group_by(PointTransaction.user_id)
        .having(func.coalesce(func.sum(PointTransaction.amount), 0) >= 0)
        .all()
    )
    
    # monthly_points is a list of tuples: (user_id, total_points)
    return [{"user_id": user_id, "total_points": total_points} for user_id, total_points in monthly_points]

@router.get("/unconverted", response_model=dict)
async def get_unconverted_points(
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """
    아직 포인트로 전환되지 않은 걸음수를 조회합니다.
    마지막 포인트 전환 이후의 걸음 기록에서 얻을 수 있는 잠재적 포인트와
    100점 단위로 실제 전환 가능한 포인트를 계산합니다.
    """
    return calculate_unconverted_points(db, current_user.id, current_user.country_code)

@router.post("/convert", response_model=dict)
async def convert_steps_to_points_endpoint(
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """
    걸음수를 포인트로 전환합니다.
    
    - 마지막 포인트 전환 이후의 모든 걸음 기록을 확인합니다.
    - 전환 가능한 포인트가 1000점 이상인 경우에만 포인트를 적립합니다.
    - 포인트는 1000점 단위로만 전환됩니다 (예: 1950걸음 → 1000포인트만 전환, 950걸음은 다음 전환까지 유지).
    - 전환된 포인트는 "step_conversion" 타입의 트랜잭션으로 기록됩니다.
    - 실시간 걸음 기록은 계속되지만, 자동으로 포인트로 전환되지 않고 반드시 이 엔드포인트를 호출해야 합니다.
    """
    result = convert_steps_to_points(db, current_user.id, current_user.country_code)
    
    # 전환에 실패했고, 최소 포인트 조건 때문이라면 400 상태코드 반환
    if not result["success"] and "Minimum 1000 points required" in result["message"]:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=result["message"]
        )
    
    return result

# 새로운 포인트 게임 DTO 정의
class PointGameRequest(BaseModel):
    result_point: int

@router.post("/point-game", response_model=dict)
async def point_game_endpoint(
    game_data: PointGameRequest,
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """
    포인트 게임을 통해 획득한 포인트를 적립합니다.
    
    - 하루에 한 번만 게임 포인트를 적립할 수 있습니다.
    - 적립된 포인트는 사용자의 총 포인트에 더해집니다.
    - 포인트 적립 내역은 "point_game" 타입의 트랜잭션으로 기록됩니다.
    """
    # 포인트 값 검증
    if game_data.result_point <= 0:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Point value must be positive"
        )
    
    # 오늘 날짜 계산
    today = date.today()
    today_start = datetime.combine(today, datetime.min.time())
    today_end = datetime.combine(today, datetime.max.time())
    
    # 오늘 이미 게임 포인트를 받았는지 확인
    existing_game = db.query(PointTransaction).filter(
        PointTransaction.user_id == current_user.id,
        PointTransaction.transaction_type == "point_game",
        PointTransaction.created_at >= today_start,
        PointTransaction.created_at <= today_end
    ).first()
    
    if existing_game:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="You have already played the point game today"
        )
    
    # 포인트 트랜잭션 생성
    point_tx = PointTransaction(
        user_id=current_user.id,
        transaction_type="point_game",
        amount=game_data.result_point,
        description=f"Get the event {game_data.result_point} points"
    )
    db.add(point_tx)
    
    # 사용자 포인트 업데이트
    user_points = db.query(UserPoints).filter(UserPoints.user_id == current_user.id).first()
    if user_points:
        user_points.total_points += game_data.result_point
    else:
        user_points = UserPoints(user_id=current_user.id, total_points=game_data.result_point)
        db.add(user_points)
    
    db.commit()
    
    return {
        "success": True,
        "message": f"Successfully added {game_data.result_point} points from the point game.",
        "added_points": game_data.result_point,
        "current_total_points": user_points.total_points
    }

