from bson import ObjectId
import bson
from fastapi import APIRouter, HTTPException, status
from fastapi.params import Depends
import pymongo

from app.dto import FriendAddDTO
from app.models import HTTPError, User, Friend, PushSubscription
from .utils import friend_not_found, get_current_user, objectid_misformatted
import app.config as config
from app.push_service import push_service

# Best workaround found for _id typed as ObjectId (creating Exception bcause JSON doesn't support custom types countrary to BSON, used by Mongo)
# also allows to create DTOs at the time, but not at it's best (project structure is chaotic FTM :s)
import app.serializers as serializers # Import all serializers (detailed in __init__.py) 

# Database setup
client = pymongo.MongoClient(config.MONGODB_URL, username=config.MONGODB_USERNAME, password=config.MONGODB_PASSWORD)
db = client[config.MONGODB_DATABASE]

friends_collection = db["friends"]
users_collection = db["users"]
pin_permissions_collection = db["pin_permissions"]

friends_router = APIRouter(
    prefix="/friend",
    tags=["Friends"]
)

@friends_router.get(
    path="/{id}",
    responses={401: {"model": HTTPError}, 404: {"model": HTTPError}, 422: {"model": HTTPError}}
)
async def get_friend(id: str, current_user: User = Depends(get_current_user)):
    try:
        friend = friends_collection.find_one({"_id": ObjectId(id)})
    except bson.errors.InvalidId:
        objectid_misformatted()
    
    if friend is None: friend_not_found()
    
    return serializers.friend_serialize(friend,False if friend['user_id']==current_user.uid else True)

@friends_router.post(
    path="/add", 
    responses={401: {"model": HTTPError}, 409: {"model": HTTPError}}
)
async def add_friend(friend_to_add: FriendAddDTO, current_user: User = Depends(get_current_user)):
    friend: Friend = friend_to_add.model_dump()

    if(current_user.uid == friend["friend_user_id"]):
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail="Cannot add yourself as a friend"
        )
    
    # Test if already friends
    if friends_collection.find_one({"user_id": current_user.uid, "friend_user_id": friend["friend_user_id"]}) is not None:
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail="Friend already exists"
        )
    
    friend_user_id = ObjectId(friend["friend_user_id"])
    friend_user = users_collection.find_one({"_id": friend_user_id})

    # Test if user exists
    if not friend_user:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="User not found"
        )

    friend["user_id"] = current_user.uid
    friend["status"] = "pending"
    friend_id = friends_collection.insert_one(friend).inserted_id

    # Send notification to friend
    payload = push_service.create_notification_payload(
        title="Nouvelle demande d'ami",
        body=f"{current_user.username} vous a envoyé une demande d'ami."
    )
    await push_service.send_notification(friend_user_id, payload);

    return {"id": str(friend_id)}

@friends_router.delete(
    path="/{id}/delete",
    responses={401: {"model": HTTPError}, 404: {"model": HTTPError}, 422: {"model": HTTPError}}
)
async def delete_friend(id: str, current_user: User = Depends(get_current_user)):
    try:
        # Récupérer l'ami avant de le supprimer pour avoir son ID
        friend = friends_collection.find_one({"_id": ObjectId(id)})
        if friend is None:
            friend_not_found()
        
        # Déterminer l'ID de l'autre utilisateur
        other_user_id = friend["friend_user_id"] if friend["user_id"] == current_user.uid else friend["user_id"]
        
        # Supprimer l'ami
        friends_collection.delete_one({"_id": ObjectId(id)})
        
        # Supprimer toutes les permissions de partage de pins entre les deux utilisateurs
        pin_permissions_collection.delete_many({
            "$or": [
                # Pins que l'utilisateur courant a partagés avec l'ami
                {"user_id": other_user_id, "pin_id": {"$in": [pin["_id"] for pin in db["pins"].find({"user_id": current_user.uid})]}},
                # Pins que l'ami a partagés avec l'utilisateur courant
                {"user_id": current_user.uid, "pin_id": {"$in": [pin["_id"] for pin in db["pins"].find({"user_id": other_user_id})]}}
            ]
        })
        
        return {"message": "Friend and associated pin shares deleted"}
        
    except bson.errors.InvalidId:
        objectid_misformatted()

@friends_router.patch(
    path="/{id}/accept",
    responses={401: {"model": HTTPError}, 404: {"model": HTTPError}, 422: {"model": HTTPError}}
)
async def accept_friend(id: str, current_user: User = Depends(get_current_user)):
    try:
        check_friend = friends_collection.find_one({"_id": ObjectId(id)})
        if check_friend is None: friend_not_found()
        
        if check_friend["status"] != "pending":
            raise HTTPException(
                status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
                detail="Friend request already accepted"
            )
        
        friends_collection.update_one({"_id": ObjectId(id)}, {"$set": {"status": "accepted"}})
    except bson.errors.InvalidId:
        objectid_misformatted()
    
    return {"message": "Friend request accepted"}

@friends_router.delete(
    path="/{id}/deny",    
    responses={401: {"model": HTTPError}, 404: {"model": HTTPError}, 422: {"model": HTTPError}}
)
async def deny_friend(id: str, current_user: User = Depends(get_current_user)):
    try:
        result = friends_collection.delete_one({"_id": ObjectId(id)})
    except bson.errors.InvalidId:
        objectid_misformatted()
    
    if result.deleted_count == 0: friend_not_found()
    
    return {"message": "Friend request denied"}

@friends_router.get(
    path="s",
    response_model=list[Friend],
    responses={401: {"model": HTTPError}}
)
async def list_friends(current_user: User = Depends(get_current_user)):
    return serializers.friends_serialize(friends_collection.find({"user_id": current_user.uid}).to_list(), friends_collection.find({"friend_user_id": current_user.uid}).to_list())
