from typing import List, Dict
from sqlalchemy.orm import Session
from sqlalchemy import or_, case
from datetime import datetime
from app.models.friendship import Friendship
from app.models.user import User
from app.models.user_referral import UserReferral
from app.services import message_service
from fastapi import HTTPException

def get_friend_list(db: Session, user_id: int) -> List[Dict]:
    """
    기존 Friendship 테이블에서 직접 추가된 친구 목록을 조회합니다.
    computed_friend_id를 기준으로 중복 제거합니다.
    """
    computed_friend_id = case(
        (Friendship.user_id == user_id, Friendship.friend_id),
        else_=Friendship.user_id
    ).label("computed_friend_id")
    
    friendship_rows = (
        db.query(
            Friendship,
            User.first_name,
            User.last_name,
            User.profile_image,
            computed_friend_id
        )
        .join(
            User,
            ((Friendship.user_id == user_id) & (User.id == Friendship.friend_id)) |
            ((Friendship.friend_id == user_id) & (User.id == Friendship.user_id))
        )
        .filter(Friendship.status == 'accepted')
        .all()
    )
    
    friend_map = {}
    for friendship, first_name, last_name, profile_image, comp_friend_id in friendship_rows:
        if comp_friend_id not in friend_map:
            friend_map[comp_friend_id] = {
                "id": friendship.id,
                "friend_id": comp_friend_id,
                "first_name": first_name,
                "last_name": last_name,
                "profile_image": profile_image,
                "status": friendship.status,
                "created_at": friendship.created_at,
                "updated_at": friendship.updated_at,
                "source": "friendship"   # 직접 등록된 친구 관계임을 표시
            }
    
    return list(friend_map.values())

def get_extended_friend_list(db: Session, user_id: int) -> List[dict]:
    """
    Retrieve the friend list and include additional information:
      - requested_by / requested_to (who sent/received the friend request)
      - is_request_received flag (true if the logged in user is the recipient)
    """
    computed_friend_id = case(
        (Friendship.user_id == user_id, Friendship.friend_id),
        else_=Friendship.user_id
    ).label("computed_friend_id")
    
    friendship_rows = (
        db.query(
            Friendship,
            User.first_name,
            User.last_name,
            User.profile_image,
            Friendship.user_id,
            Friendship.friend_id,
            computed_friend_id
        )
        .join(
            User,
            or_(
                (Friendship.user_id == user_id) & (User.id == Friendship.friend_id),
                (Friendship.friend_id == user_id) & (User.id == Friendship.user_id)
            )
        )
        .filter(Friendship.status.in_(["pending", "accepted", "blocked"]))
        .all()
    )
    
    friend_map = {}
    for friendship, first_name, last_name, profile_image, requested_by, requested_to, comp_friend_id in friendship_rows:
        is_request_received = (requested_to == user_id)
        if comp_friend_id not in friend_map:
            friend_map[comp_friend_id] = {
                "id": friendship.id,
                "requested_by": requested_by,
                "requested_to": requested_to,
                "friend_id": comp_friend_id,
                "first_name": first_name,
                "last_name": last_name,
                "profile_image": profile_image,
                "status": friendship.status,
                "created_at": friendship.created_at,
                "updated_at": friendship.updated_at,
                "is_request_received": is_request_received,
            }
    
    return list(friend_map.values())

def _serialize_friendship(db: Session, friendship: Friendship, current_user_id: int) -> dict:
    """
    Serialize a Friendship instance so the response includes:
      - requested_by: original requester (friendship.user_id)
      - requested_to: recipient (friendship.friend_id)
      - is_request_received: whether the current user is the recipient.
      - friend_id: the 'other' user's id (convenience field)
    """
    requested_by = friendship.user_id
    requested_to = friendship.friend_id
    # Determine the 'other' user in relation to the current user.
    computed_friend_id = requested_to if requested_by == current_user_id else requested_by
    # The current user receives a request if they match requested_to.
    is_request_received = (requested_to == current_user_id)
    
    # Retrieve the friend details from the User model.
    friend = db.query(User).filter(User.id == computed_friend_id).first()
    if not friend:
        raise HTTPException(status_code=404, detail="Friend not found")
    
    return {
        "id": friendship.id,
        "requested_by": requested_by,
        "requested_to": requested_to,
        "friend_id": computed_friend_id,
        "first_name": friend.first_name,
        "last_name": friend.last_name,
        "profile_image": friend.profile_image,
        "status": friendship.status,
        "is_request_received": is_request_received,
        "created_at": friendship.created_at,
        "updated_at": friendship.updated_at,
    }

def create_friendship(db: Session, user_id: int, friend_id: int, status: str = "accepted") -> Dict:
    """
    두 사용자 간의 친구 관계를 생성합니다.
    이미 동일한 관계가 존재하면 해당 레코드를 직렬화하여 반환합니다.
    """
    # 중복 체크: 이미 존재하는 친구 관계라면 바로 직렬화하여 반환
    existing = db.query(Friendship).filter(
        or_(
            ((Friendship.user_id == user_id) & (Friendship.friend_id == friend_id)),
            ((Friendship.user_id == friend_id) & (Friendship.friend_id == user_id))
        )
    ).first()
    if existing:
        return _serialize_friendship(db, existing, user_id)

    new_friendship = Friendship(
        user_id=user_id,
        friend_id=friend_id,
        status=status  # 'pending', 'accepted', 'rejected', etc.
    )
    db.add(new_friendship)
    db.commit()
    db.refresh(new_friendship)
    return _serialize_friendship(db, new_friendship, user_id)

def update_friendship_status(db: Session, current_user_id: int, friend_id: int, new_status: str) -> dict:
    """
    현재 사용자와 friend_id 간의 친구 관계 상태를 변경하고, 
    응답으로 FriendsResponse에 정의된 필드를 포함한 딕셔너리를 반환합니다.
    new_status는 "accepted", "blocked", (또는 enum에 추가할 경우 "rejected") 등으로 지정할 수 있습니다.
    """
    friendship = db.query(Friendship).filter(
        or_(
            ((Friendship.user_id == current_user_id) & (Friendship.friend_id == friend_id)),
            ((Friendship.user_id == friend_id) & (Friendship.friend_id == current_user_id))
        )
    ).first()
    
    if not friendship:
        raise HTTPException(status_code=404, detail="Friendship not found")
    
    friendship.status = new_status
    db.commit()
    db.refresh(friendship)
    
    return _serialize_friendship(db, friendship, current_user_id)

def delete_friendship(db: Session, current_user_id: int, friend_id: int) -> dict:
    """
    현재 사용자와 friend_id 간의 친구 관계를 삭제하는 대신,
    상태를 'blocked'로 업데이트하며,  
    응답으로 FriendshipResponse 스키마에 맞는 데이터를 포함한 딕셔너리를 반환합니다.
    """
    friendship = db.query(Friendship).filter(
        or_(
            ((Friendship.user_id == current_user_id) & (Friendship.friend_id == friend_id)),
            ((Friendship.user_id == friend_id) & (Friendship.friend_id == current_user_id))
        )
    ).first()
    if not friendship:
        raise HTTPException(status_code=404, detail="Friendship not found")
    
    # 상태를 'blocked'로 업데이트합니다.
    friendship.status = "blocked"
    db.commit()
    db.refresh(friendship)
    
    return _serialize_friendship(db, friendship, current_user_id)