from fastapi import APIRouter, Depends, HTTPException, status, UploadFile, File, Form, Request, Response
from sqlalchemy.orm import Session
from typing import Optional
from slowapi import Limiter
from slowapi.util import get_remote_address
from ..database import get_db
from ..models.user import User
from ..models.watch_history import WatchHistory
from ..models.stream import Stream
from ..schemas.user import UserResponse, UserProfileUpdate
from ..schemas.watch_history import WatchHistoryCreate, WatchHistoryUpdate, WatchHistoryResponse, WatchHistoryWithStream
from ..auth import get_current_active_user, require_permission, require_any_permission, get_current_user_optional
from ..config import settings
import os
import uuid
import hashlib
import json
from datetime import datetime, timezone

router = APIRouter(prefix="/api/users", tags=["users"])
limiter = Limiter(key_func=get_remote_address)


@router.get("/me", response_model=UserResponse)
async def get_my_profile(current_user: User = Depends(get_current_active_user)):
    return UserResponse.from_orm(current_user)


@router.put("/me", response_model=UserResponse)
async def update_my_profile(
    profile_data: UserProfileUpdate,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_permission("user:update:own"))
):
    if profile_data.profile_image_url is not None:
        current_user.profile_image_url = profile_data.profile_image_url
    current_user.updated_at = datetime.now(timezone.utc)
    db.commit()
    db.refresh(current_user)
    return UserResponse.from_orm(current_user)


@router.post("/me/profile-image")
async def upload_profile_image(
    file: UploadFile = File(...),
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_active_user)
):
    # Validate file type
    if not file.content_type or not file.content_type.startswith("image/"):
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Only image files are allowed"
        )

    # Validate PNG or JPEG format
    allowed_types = ["image/png", "image/jpeg", "image/jpg"]
    if file.content_type not in allowed_types:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Only PNG and JPEG images are allowed"
        )

    # Create profile images directory if it doesn't exist
    profile_images_dir = os.path.join(settings.STORAGE_PATH, "profile_images")
    os.makedirs(profile_images_dir, exist_ok=True)

    # Generate unique filename based on content type
    file_extension = "png" if file.content_type == "image/png" else "jpg"
    filename = f"{current_user.id}.{file_extension}"
    file_path = os.path.join(profile_images_dir, filename)

    # Save file
    with open(file_path, "wb") as buffer:
        content = await file.read()
        # Limit file size to 5MB
        if len(content) > 5 * 1024 * 1024:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="File size exceeds 5MB limit"
            )
        buffer.write(content)

    # Update user profile
    current_user.profile_image_url = f"/storage/profile_images/{filename}"
    current_user.updated_at = datetime.now(timezone.utc)
    db.commit()
    db.refresh(current_user)

    return {"message": "Profile image uploaded successfully", "profile_image_url": current_user.profile_image_url}


@router.get("/me/watch-history")
async def get_my_watch_history(
    request: Request,
    skip: int = 0,
    limit: int = 50,
    db: Session = Depends(get_db),
    current_user: Optional[User] = Depends(get_current_user_optional)
):
    # Return null if user is not authenticated
    if not current_user:
        return []
    
    watch_history = db.query(WatchHistory).filter(
        WatchHistory.user_id == current_user.id
    ).order_by(WatchHistory.last_watched_at.desc()).offset(skip).limit(limit).all()

    # Add stream information
    result = []
    for wh in watch_history:
        stream = db.query(Stream).filter(Stream.id == wh.stream_id).first()
        wh_dict = WatchHistoryResponse.from_orm(wh).model_dump()
        if stream:
            wh_dict["stream_title"] = stream.title
            wh_dict["stream_thumbnail_url"] = stream.thumbnail_url
            wh_dict["stream_duration_seconds"] = stream.duration_seconds
        else:
            wh_dict["stream_title"] = None
            wh_dict["stream_thumbnail_url"] = None
            wh_dict["stream_duration_seconds"] = None
        result.append(WatchHistoryWithStream(**wh_dict))

    # Generate ETag
    etag = hashlib.md5(json.dumps([{
        'id': str(r.id),
        'last_watched_at': r.last_watched_at.isoformat() if r.last_watched_at else None
    } for r in result], sort_keys=True).encode()).hexdigest()

    # Check ETag
    if_none_match = request.headers.get("if-none-match")
    if if_none_match == etag:
        return Response(status_code=304)

    # Return data with ETag - convert UUIDs and datetimes to strings
    response_data = []
    for r in result:
        r_dict = r.model_dump()
        r_dict['id'] = str(r_dict['id'])
        r_dict['user_id'] = str(r_dict['user_id'])
        r_dict['stream_id'] = str(r_dict['stream_id'])
        # Convert datetime fields to ISO strings
        for key in ['last_watched_at', 'created_at', 'updated_at']:
            if key in r_dict and r_dict[key] is not None:
                r_dict[key] = r_dict[key].isoformat()
        response_data.append(r_dict)

    response = Response(content=json.dumps(response_data), media_type="application/json")
    response.headers["ETag"] = etag
    return response


@router.post("/me/watch-history", response_model=WatchHistoryResponse | None)
async def create_or_update_watch_history(
    watch_data: WatchHistoryCreate,
    db: Session = Depends(get_db),
    current_user: Optional[User] = Depends(get_current_user_optional)
):
    # Return null if user is not authenticated
    if not current_user:
        return None
    
    # Check if stream exists
    stream = db.query(Stream).filter(Stream.id == watch_data.stream_id).first()
    if not stream:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Stream not found"
        )
    
    # Check if watch history already exists
    existing = db.query(WatchHistory).filter(
        WatchHistory.user_id == current_user.id,
        WatchHistory.stream_id == watch_data.stream_id
    ).first()
    
    if existing:
        # Update existing
        existing.last_position_seconds = watch_data.last_position_seconds
        existing.completed = watch_data.completed
        existing.last_watched_at = datetime.now(timezone.utc)
        db.commit()
        db.refresh(existing)
        return WatchHistoryResponse.from_orm(existing)
    else:
        # Create new
        new_watch_history = WatchHistory(
            user_id=current_user.id,
            stream_id=watch_data.stream_id,
            last_position_seconds=watch_data.last_position_seconds,
            completed=watch_data.completed,
            last_watched_at=datetime.now(timezone.utc)
        )
        db.add(new_watch_history)
        db.commit()
        db.refresh(new_watch_history)
        return WatchHistoryResponse.from_orm(new_watch_history)


@router.put("/me/watch-history/{stream_id}", response_model=WatchHistoryResponse | None)
async def update_watch_history(
    stream_id: str,
    watch_data: WatchHistoryUpdate,
    db: Session = Depends(get_db),
    current_user: Optional[User] = Depends(get_current_user_optional)
):
    # Return null if user is not authenticated
    if not current_user:
        return None
    
    watch_history = db.query(WatchHistory).filter(
        WatchHistory.user_id == current_user.id,
        WatchHistory.stream_id == stream_id
    ).first()
    
    if not watch_history:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Watch history not found"
        )
    
    watch_history.last_position_seconds = watch_data.last_position_seconds
    if watch_data.completed is not None:
        watch_history.completed = watch_data.completed
    watch_history.last_watched_at = datetime.now(timezone.utc)
    db.commit()
    db.refresh(watch_history)
    
    return WatchHistoryResponse.from_orm(watch_history)


@router.delete("/me/watch-history/{stream_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_watch_history(
    stream_id: str,
    db: Session = Depends(get_db),
    current_user: Optional[User] = Depends(get_current_user_optional)
):
    # Return without error if user is not authenticated
    if not current_user:
        return
    
    watch_history = db.query(WatchHistory).filter(
        WatchHistory.user_id == current_user.id,
        WatchHistory.stream_id == stream_id
    ).first()
    
    if not watch_history:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Watch history not found"
        )
    
    db.delete(watch_history)
    db.commit()
    
    return None


@router.get("/me/watch-history/stream/{stream_id}", response_model=WatchHistoryResponse | None)
async def get_stream_watch_history(
    stream_id: str,
    db: Session = Depends(get_db),
    current_user: Optional[User] = Depends(get_current_user_optional)
):
    # Return null if user is not authenticated
    if not current_user:
        return None
    
    watch_history = db.query(WatchHistory).filter(
        WatchHistory.user_id == current_user.id,
        WatchHistory.stream_id == stream_id
    ).first()
    
    # Return null instead of 404 if no watch history exists
    if not watch_history:
        return None
    
    return WatchHistoryResponse.from_orm(watch_history)
