from test_main import *
from PIL import Image
import io

def create_test_image():
    img = Image.new('RGB', (100, 100), color='red')
    img_byte_arr = io.BytesIO()
    img.save(img_byte_arr, format='JPEG')
    img_byte_arr.seek(0)
    return img_byte_arr

def ensure_friendship(user_id, user_id_second):
    """Crée une amitié entre deux utilisateurs si elle n'existe pas déjà."""
    friend = db["friends"].find_one({
        "$or": [
            {"user_id": user_id, "friend_user_id": user_id_second},
            {"user_id": user_id_second, "friend_user_id": user_id}
        ]
    })
    if not friend:
        db["friends"].insert_one({
            "user_id": user_id,
            "friend_user_id": user_id_second,
            "status": "accepted"
        })

def remove_friendship(user_id, user_id_second):
    """Supprime l'amitié entre deux utilisateurs si elle existe."""
    friend = db["friends"].find_one({
        "$or": [
            {"user_id": user_id, "friend_user_id": user_id_second},
            {"user_id": user_id_second, "friend_user_id": user_id}
        ]
    })
    if friend:
        db["friends"].delete_one({"_id": friend["_id"]})


def add_test_image(token):
    test_image = create_test_image()
    response = client.post(
        "/image/pin/null/add",
        files={"image": ("test.jpg", test_image, "image/jpeg")},
        data={"exif_date": "2024-03-20T12:00:00", "caption": "Test caption"},
        headers={"Authorization": f"Bearer {token}"}
    )
    return response.json()["id"]

def create_test_pin(token, user_id, image_id=None, with_date=False):
    if image_id is None:
        image_id = add_test_image(token)
    
    pin_data = {
        "title": "Test Pin", 
        "description": "Test Description", 
        "location": [0,0], 
        "complete_address": "Paris, Île-de-France, France métropolitaine, France",
        "files": [image_id], 
        "user_id": user_id
    }
    
    if with_date:
        pin_data["date"] = "2024-03-20T12:00:00"
    
    response = client.post(
        "/pin/add", 
        json=pin_data,
        headers={"Authorization": f"Bearer {token}"}
    )
    return response.json()["id"]

def test_add_pin(token, user_id):
    # Test sans date
    image_id = add_test_image(token)
    pin_id = create_test_pin(token, user_id, image_id)
    assert pin_id is not None
    
    # Test avec date
    pin_id_with_date = create_test_pin(token, user_id, image_id, with_date=True)
    assert pin_id_with_date is not None

def test_add_pin_invalid_files_format(token, user_id):
    response = client.post(
        "/pin/add", 
        json={
            "title": "Test Pin", 
            "description": "Test Description", 
            "location": [0,0], 
            "complete_address": "Marseille, Bouches-du-Rhône, Provence-Alpes-Côte d'Azur, France métropolitaine, France",
            "files": ["abc"], 
            "user_id": user_id
        }, 
        headers={"Authorization": f"Bearer {token}"}
    )
    assert response.status_code == 422
    assert "Invalid image ID format" in response.json()["detail"][0]["msg"]

def test_list_pins(token):
    response = client.get("/pins", headers={"Authorization": f"Bearer {token}"})
    assert response.status_code == 200
    data = response.json()
    assert isinstance(data, list)

def test_get_pin(token, user_id):
    # Test sans date
    image_id = add_test_image(token)
    pin_id = create_test_pin(token, user_id, image_id)
    
    response = client.get(
        f"/pin/{pin_id}", 
        headers={"Authorization": f"Bearer {token}"}
    )
    assert response.status_code == 200
    data = response.json()
    assert data["title"] == "Test Pin"
    assert data["description"] == "Test Description"
    assert data["location"] == [0,0]
    assert image_id in data["files"]
    assert data["date"] is None
    
    # Test avec date
    pin_id_with_date = create_test_pin(token, user_id, image_id, with_date=True)
    response = client.get(
        f"/pin/{pin_id_with_date}", 
        headers={"Authorization": f"Bearer {token}"}
    )
    assert response.status_code == 200
    data = response.json()
    assert data["date"] == "2024-03-20T12:00:00"

def test_get_pin_wrong_format(token):
    response = client.get(f"/pin/randomIdThatDoesntExists", headers={"Authorization": f"Bearer {token}"})
    assert response.status_code == 422

def test_get_pin_unknown_id(token):
    response = client.get(f"/pin/123456789987654321abcdef", headers={"Authorization": f"Bearer {token}"})
    assert response.status_code == 404

def test_update_pin(token, user_id):
    image_id = add_test_image(token)
    pin_id = create_test_pin(token, user_id, image_id)
    
    # Test mise à jour sans date
    response = client.patch(
        f"/pin/{pin_id}", 
        json={
            "title": "Updated Pin", 
            "description": "Updated Description", 
            "location": [1,1], 
            "complete_address": "Mont Saint-Michel, Grande Terrasse de l'Ouest, Le Mont-Saint-Michel, Avranches, Manche, Normandie, France métropolitaine, 50170, France",
            "files": [image_id],
            "user_id": user_id
        }, 
        headers={"Authorization": f"Bearer {token}"}
    )
    assert response.status_code == 200
    
    get_response = client.get(
        f"/pin/{pin_id}", 
        headers={"Authorization": f"Bearer {token}"}
    )
    data = get_response.json()
    assert data["title"] == "Updated Pin"
    assert data["description"] == "Updated Description"
    assert data["location"] == [1,1]
    assert data["complete_address"] == "Mont Saint-Michel, Grande Terrasse de l'Ouest, Le Mont-Saint-Michel, Avranches, Manche, Normandie, France métropolitaine, 50170, France"
    assert image_id in data["files"]
    assert data["date"] is None
    
    # Test mise à jour avec date
    response = client.patch(
        f"/pin/{pin_id}", 
        json={
            "title": "Updated Pin With Date", 
            "description": "Updated Description", 
            "location": [1,1], 
            "complete_address": "Bordeaux, Gironde, Nouvelle-Aquitaine, France métropolitaine, France",
            "files": [image_id],
            "user_id": user_id,
            "date": "2024-03-21T12:00:00"
        }, 
        headers={"Authorization": f"Bearer {token}"}
    )
    assert response.status_code == 200
    
    get_response = client.get(
        f"/pin/{pin_id}", 
        headers={"Authorization": f"Bearer {token}"}
    )
    data = get_response.json()
    assert data["title"] == "Updated Pin With Date"
    assert data["complete_address"] == "Bordeaux, Gironde, Nouvelle-Aquitaine, France métropolitaine, France"
    assert data["date"] == "2024-03-21T12:00:00"

def test_update_wrong_format(token):
    response = client.get(f"/pin/randomIdThatDoesntExists", headers={"Authorization": f"Bearer {token}"})
    assert response.status_code == 422

def test_update_wrong_unknown_id(token):
    response = client.get(f"/pin/123456789987654321abcdef", headers={"Authorization": f"Bearer {token}"})
    assert response.status_code == 404

def test_delete_pin(token, user_id):
    image_id = add_test_image(token)
    pin_id = create_test_pin(token, user_id, image_id)
    
    response = client.delete(
        f"/pin/{pin_id}", 
        headers={"Authorization": f"Bearer {token}"}
    )
    assert response.status_code == 200
    
    get_response = client.get(
        f"/pin/{pin_id}", 
        headers={"Authorization": f"Bearer {token}"}
    )
    assert get_response.status_code == 404

def test_share_pin(token, user_id, token_second, user_id_second):
    # Créer un pin
    image_id = add_test_image(token)
    pin_id = create_test_pin(token, user_id, image_id)
    
    # S'assurer que les utilisateurs sont amis
    ensure_friendship(user_id, user_id_second)

    # Partager le pin
    response = client.post(
        f"/pin/{pin_id}/share",
        json={"friend_id": user_id_second},
        headers={"Authorization": f"Bearer {token}"}
    )
    assert response.status_code == 200
    
    # Vérifier que l'autre utilisateur peut voir le pin
    response = client.get(
        f"/pin/{pin_id}",
        headers={"Authorization": f"Bearer {token_second}"}
    )
    assert response.status_code == 200

def test_share_pin_not_owner(token, user_id, token_second, user_id_second):
    # Créer un pin avec le deuxième utilisateur
    image_id = add_test_image(token_second)
    pin_id = create_test_pin(token_second, user_id_second, image_id)
    
    # Essayer de partager le pin (ne devrait pas fonctionner car on n'est pas le propriétaire)
    response = client.post(
        f"/pin/{pin_id}/share",
        json={"friend_id": user_id},
        headers={"Authorization": f"Bearer {token}"}
    )
    assert response.status_code == 403
    assert "You can only share your own pins" in response.json()["detail"]

def test_share_pin_already_shared(token, user_id, token_second, user_id_second):
    # Créer un pin
    image_id = add_test_image(token)
    pin_id = create_test_pin(token, user_id, image_id)
    
    # S'assurer que les utilisateurs sont amis
    ensure_friendship(user_id, user_id_second)

    # Partager le pin une première fois
    response = client.post(
        f"/pin/{pin_id}/share",
        json={"friend_id": user_id_second},
        headers={"Authorization": f"Bearer {token}"}
    )
    assert response.status_code == 200
    
    # Essayer de partager à nouveau (ne devrait pas fonctionner)
    response = client.post(
        f"/pin/{pin_id}/share",
        json={"friend_id": user_id_second},
        headers={"Authorization": f"Bearer {token}"}
    )
    assert response.status_code == 409
    assert "Pin is already shared with this user" in response.json()["detail"]

def test_update_shared_pin(token, user_id, token_second, user_id_second):
    # Créer un pin
    image_id = add_test_image(token)
    pin_id = create_test_pin(token, user_id, image_id)
    
    # S'assurer que les utilisateurs sont amis
    ensure_friendship(user_id, user_id_second)

    # Partager le pin
    client.post(
        f"/pin/{pin_id}/share",
        json={"friend_id": user_id_second},
        headers={"Authorization": f"Bearer {token}"}
    )
    
    # L'autre utilisateur essaie de modifier le pin
    response = client.patch(
        f"/pin/{pin_id}",
        json={
            "title": "Updated by friend",
            "description": "Updated Description",
            "location": [1,1],
            "complete_address": "Marseille, Bouches-du-Rhône, Provence-Alpes-Côte d'Azur, France métropolitaine, France",
            "files": [image_id],
            "user_id": user_id_second
        },
        headers={"Authorization": f"Bearer {token_second}"}
    )
    assert response.status_code == 200
    
    # Vérifier que les modifications ont été appliquées
    response = client.get(
        f"/pin/{pin_id}",
        headers={"Authorization": f"Bearer {token_second}"}
    )
    data = response.json()
    assert data["title"] == "Updated by friend"
    assert data["complete_address"] == "Marseille, Bouches-du-Rhône, Provence-Alpes-Côte d'Azur, France métropolitaine, France"

def test_delete_shared_pin_access(token, user_id, token_second, user_id_second):
    # Créer un pin
    image_id = add_test_image(token)
    pin_id = create_test_pin(token, user_id, image_id)
    
    # S'assurer que les utilisateurs sont amis
    ensure_friendship(user_id, user_id_second)

    # Partager le pin
    client.post(
        f"/pin/{pin_id}/share",
        json={"friend_id": user_id_second},
        headers={"Authorization": f"Bearer {token}"}
    )
    
    # L'autre utilisateur supprime son accès au pin
    response = client.delete(
        f"/pin/{pin_id}",
        headers={"Authorization": f"Bearer {token_second}"}
    )
    assert response.status_code == 200
    assert "Pin access removed" in response.json()["message"]
    
    # Vérifier que l'autre utilisateur n'a plus accès au pin
    response = client.get(
        f"/pin/{pin_id}",
        headers={"Authorization": f"Bearer {token_second}"}
    )
    assert response.status_code == 403
    
def test_share_pin_not_friend(token, user_id, token_second, user_id_second):
    # Créer un pin
    image_id = add_test_image(token)
    pin_id = create_test_pin(token, user_id, image_id)
    
    # S'assurer que les utilisateurs ne sont pas amis
    remove_friendship(user_id, user_id_second)
    
    # Essayer de partager avec quelqu'un qui n'est pas ami
    response = client.post(
        f"/pin/{pin_id}/share",
        json={"friend_id": user_id_second},
        headers={"Authorization": f"Bearer {token}"}
    )
    assert response.status_code == 403
    assert "You can only share pins with your friends" in response.json()["detail"]

def test_get_pin_shares(token, user_id, token_second, user_id_second):
    # Créer un pin
    image_id = add_test_image(token)
    pin_id = create_test_pin(token, user_id, image_id)
    
    # S'assurer que les utilisateurs sont amis
    ensure_friendship(user_id, user_id_second)

    # Partager le pin
    client.post(
        f"/pin/{pin_id}/share",
        json={"friend_id": user_id_second},
        headers={"Authorization": f"Bearer {token}"}
    )
    
    # Vérifier les partages du pin
    response = client.get(
        f"/pin/{pin_id}/shares",
        headers={"Authorization": f"Bearer {token}"}
    )
    assert response.status_code == 200
    data = response.json()
    assert "shares" in data
    assert len(data["shares"]) == 1
    assert data["shares"][0]["user_id"] == user_id_second
    assert data["shares"][0]["can_edit"] is True
    assert data["shares"][0]["can_delete"] is False

def test_get_pin_shares_not_owner(token, user_id, token_second, user_id_second):
    # Créer un pin avec le deuxième utilisateur
    image_id = add_test_image(token_second)
    pin_id = create_test_pin(token_second, user_id_second, image_id)
    
    # Essayer de voir les partages (ne devrait pas fonctionner car on n'est pas le propriétaire)
    response = client.get(
        f"/pin/{pin_id}/shares",
        headers={"Authorization": f"Bearer {token}"}
    )
    assert response.status_code == 403
    assert "You can only view shares of your own pins" in response.json()["detail"]

def test_delete_pin_share(token, user_id, token_second, user_id_second):
    # Créer un pin
    image_id = add_test_image(token)
    pin_id = create_test_pin(token, user_id, image_id)
    
    # S'assurer que les utilisateurs sont amis
    ensure_friendship(user_id, user_id_second)

    # Partager le pin
    client.post(
        f"/pin/{pin_id}/share",
        json={"friend_id": user_id_second},
        headers={"Authorization": f"Bearer {token}"}
    )
    
    # Supprimer le partage
    response = client.delete(
        f"/pin/{pin_id}/share/{user_id_second}",
        headers={"Authorization": f"Bearer {token}"}
    )
    assert response.status_code == 200
    assert "Share removed successfully" in response.json()["message"]
    
    # Vérifier que l'autre utilisateur n'a plus accès au pin
    response = client.get(
        f"/pin/{pin_id}",
        headers={"Authorization": f"Bearer {token_second}"}
    )
    assert response.status_code == 403

def test_delete_pin_share_shareduser(token, user_id, token_second, user_id_second):
    # Créer un pin avec le deuxième utilisateur
    image_id = add_test_image(token_second)
    pin_id = create_test_pin(token_second, user_id_second, image_id)
    
    # S'assurer que les utilisateurs sont amis
    ensure_friendship(user_id, user_id_second)
    
    # Partager le pin avec le premier utilisateur
    client.post(
        f"/pin/{pin_id}/share",
        json={"friend_id": user_id},
        headers={"Authorization": f"Bearer {token_second}"}
    )
    
    # Essayer de supprimer le partage (devrait fonctionner car il essaie de se supprimer lui même)
    response = client.delete(
        f"/pin/{pin_id}/share/{user_id}",
        headers={"Authorization": f"Bearer {token}"}
    )
    assert response.status_code == 200
    assert "Share removed successfully" in response.json()["message"]

def test_delete_pin_share_not_found(token, user_id):
    # Créer un pin
    image_id = add_test_image(token)
    pin_id = create_test_pin(token, user_id, image_id)
    
    # Essayer de supprimer un partage inexistant
    response = client.delete(
        f"/pin/{pin_id}/share/507f1f77bcf86cd799439011",
        headers={"Authorization": f"Bearer {token}"}
    )
    assert response.status_code == 404
    assert "Share not found" in response.json()["detail"]

def test_delete_pin_share_invalid_id(token, user_id):
    # Créer un pin
    image_id = add_test_image(token)
    pin_id = create_test_pin(token, user_id, image_id)
    
    # Essayer de supprimer un partage avec un ID invalide
    response = client.delete(
        f"/pin/{pin_id}/share/invalid_id",
        headers={"Authorization": f"Bearer {token}"}
    )
    assert response.status_code == 404

def test_delete_pin_share_self(token, user_id, token_second, user_id_second):
    # Créer un pin
    image_id = add_test_image(token)
    pin_id = create_test_pin(token, user_id, image_id)
    
    # S'assurer que les utilisateurs sont amis
    ensure_friendship(user_id, user_id_second)

    # Partager le pin
    client.post(
        f"/pin/{pin_id}/share",
        json={"friend_id": user_id_second},
        headers={"Authorization": f"Bearer {token}"}
    )
    
    # L'utilisateur qui a le partage supprime son propre accès
    response = client.delete(
        f"/pin/{pin_id}/share/{user_id_second}",
        headers={"Authorization": f"Bearer {token_second}"}
    )
    assert response.status_code == 200
    assert "Share removed successfully" in response.json()["message"]
    
    # Vérifier que l'utilisateur n'a plus accès au pin
    response = client.get(
        f"/pin/{pin_id}",
        headers={"Authorization": f"Bearer {token_second}"}
    )
    assert response.status_code == 403