import pytest
from fastapi.testclient import TestClient
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from app.main import app
from app.core.database import Base, get_db
from app.models.trip import Trip
from app.models.participant import Participant
from app.core.auth import get_password_hash, verify_password, create_access_token

# Create test database
SQLALCHEMY_DATABASE_URL = "sqlite:///./test.db"
engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False})
TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

def override_get_db():
    try:
        db = TestingSessionLocal()
        yield db
    finally:
        db.close()

app.dependency_overrides[get_db] = override_get_db

@pytest.fixture(autouse=True)
def setup_database():
    """Create tables before each test and drop after"""
    Base.metadata.create_all(bind=engine)
    yield
    Base.metadata.drop_all(bind=engine)

client = TestClient(app)

def test_create_trip_without_password():
    """Test creating a trip without a password"""
    response = client.post("/api/trips", json={
        "name": "Test Trip",
        "creator_name": "John",
        "currency_code": "USD"
    })
    assert response.status_code == 200
    data = response.json()
    assert data["name"] == "Test Trip"
    assert "share_code" in data

def test_create_trip_with_password():
    """Test creating a trip with a password"""
    response = client.post("/api/trips", json={
        "name": "Private Trip",
        "creator_name": "John",
        "currency_code": "USD",
        "password": "secret123"
    })
    assert response.status_code == 200
    data = response.json()
    assert data["name"] == "Private Trip"
    assert "share_code" in data

def test_access_trip_without_password():
    """Test accessing a trip without password (should work)"""
    # Create trip without password
    create_response = client.post("/api/trips", json={
        "name": "Public Trip",
        "creator_name": "John",
        "currency_code": "USD"
    })
    trip_id = create_response.json()["id"]
    
    # Access trip without auth token
    response = client.get(f"/api/trips/{trip_id}")
    assert response.status_code == 200
    assert response.json()["name"] == "Public Trip"

def test_access_trip_with_password_without_auth():
    """Test accessing a password-protected trip without authentication"""
    # Create trip with password
    create_response = client.post("/api/trips", json={
        "name": "Private Trip",
        "creator_name": "John",
        "currency_code": "USD",
        "password": "secret123"
    })
    trip_id = create_response.json()["id"]
    
    # Try to access without auth token
    response = client.get(f"/api/trips/{trip_id}")
    assert response.status_code == 401
    assert "password protected" in response.json()["detail"].lower()

def test_authenticate_with_correct_password():
    """Test authenticating with correct password"""
    # Create trip with password
    create_response = client.post("/api/trips", json={
        "name": "Private Trip",
        "creator_name": "John",
        "currency_code": "USD",
        "password": "secret123"
    })
    trip_id = create_response.json()["id"]
    
    # Authenticate
    auth_response = client.post("/api/auth/trip/authenticate", json={
        "trip_id": trip_id,
        "password": "secret123"
    })
    assert auth_response.status_code == 200
    data = auth_response.json()
    assert "access_token" in data
    assert data["trip_id"] == trip_id

def test_authenticate_with_incorrect_password():
    """Test authenticating with incorrect password"""
    # Create trip with password
    create_response = client.post("/api/trips", json={
        "name": "Private Trip",
        "creator_name": "John",
        "currency_code": "USD",
        "password": "secret123"
    })
    trip_id = create_response.json()["id"]
    
    # Try to authenticate with wrong password
    auth_response = client.post("/api/auth/trip/authenticate", json={
        "trip_id": trip_id,
        "password": "wrongpassword"
    })
    assert auth_response.status_code == 401
    assert "incorrect password" in auth_response.json()["detail"].lower()

def test_access_trip_with_password_after_auth():
    """Test accessing a password-protected trip after authentication"""
    # Create trip with password
    create_response = client.post("/api/trips", json={
        "name": "Private Trip",
        "creator_name": "John",
        "currency_code": "USD",
        "password": "secret123"
    })
    trip_id = create_response.json()["id"]
    
    # Authenticate
    auth_response = client.post("/api/auth/trip/authenticate", json={
        "trip_id": trip_id,
        "password": "secret123"
    })
    token = auth_response.json()["access_token"]
    
    # Access trip with token
    response = client.get(
        f"/api/trips/{trip_id}",
        headers={"Authorization": f"Bearer {token}"}
    )
    assert response.status_code == 200
    assert response.json()["name"] == "Private Trip"

def test_add_expense_to_protected_trip_without_auth():
    """Test adding expense to password-protected trip without authentication"""
    # Create trip with password
    create_response = client.post("/api/trips", json={
        "name": "Private Trip",
        "creator_name": "John",
        "currency_code": "USD",
        "password": "secret123"
    })
    trip_id = create_response.json()["id"]
    
    # Get participant ID
    participants_response = client.get(
        f"/api/participants/{trip_id}/participants",
        headers={"Authorization": "Bearer invalid"}
    )
    # Should fail without auth
    assert participants_response.status_code == 401
    
    # Try to add expense without auth
    expense_response = client.post(
        f"/api/expenses/{trip_id}/expenses",
        json={
            "description": "Test expense",
            "amount": 100.0,
            "currency_code": "USD",
            "exchange_rate": 1.0,
            "paid_by_id": 1,
            "splits": [{"participant_id": 1, "percentage": 100}]
        }
    )
    assert expense_response.status_code == 401

def test_add_expense_to_protected_trip_with_auth():
    """Test adding expense to password-protected trip with authentication"""
    # Create trip with password
    create_response = client.post("/api/trips", json={
        "name": "Private Trip",
        "creator_name": "John",
        "currency_code": "USD",
        "password": "secret123"
    })
    trip_id = create_response.json()["id"]
    
    # Authenticate
    auth_response = client.post("/api/auth/trip/authenticate", json={
        "trip_id": trip_id,
        "password": "secret123"
    })
    token = auth_response.json()["access_token"]
    
    # Get participant ID
    participants_response = client.get(
        f"/api/participants/{trip_id}/participants",
        headers={"Authorization": f"Bearer {token}"}
    )
    assert participants_response.status_code == 200
    participant_id = participants_response.json()[0]["id"]
    
    # Add expense with auth
    expense_response = client.post(
        f"/api/expenses/{trip_id}/expenses",
        headers={"Authorization": f"Bearer {token}"},
        json={
            "description": "Test expense",
            "amount": 100.0,
            "currency_code": "USD",
            "exchange_rate": 1.0,
            "paid_by_id": participant_id,
            "splits": [{"participant_id": participant_id, "percentage": 100}]
        }
    )
    assert expense_response.status_code == 200
    assert expense_response.json()["description"] == "Test expense"

def test_add_expense_to_unprotected_trip():
    """Test adding expense to trip without password (should work without auth)"""
    # Create trip without password
    create_response = client.post("/api/trips", json={
        "name": "Public Trip",
        "creator_name": "John",
        "currency_code": "USD"
    })
    trip_id = create_response.json()["id"]
    
    # Get participant ID
    participants_response = client.get(f"/api/participants/{trip_id}/participants")
    assert participants_response.status_code == 200
    participant_id = participants_response.json()[0]["id"]
    
    # Add expense without auth (should work)
    expense_response = client.post(
        f"/api/expenses/{trip_id}/expenses",
        json={
            "description": "Test expense",
            "amount": 100.0,
            "currency_code": "USD",
            "exchange_rate": 1.0,
            "paid_by_id": participant_id,
            "splits": [{"participant_id": participant_id, "percentage": 100}]
        }
    )
    assert expense_response.status_code == 200

def test_get_trip_by_share_code_without_password():
    """Test getting trip by share code when trip has no password"""
    # Create trip without password
    create_response = client.post("/api/trips", json={
        "name": "Public Trip",
        "creator_name": "John",
        "currency_code": "USD"
    })
    share_code = create_response.json()["share_code"]
    
    # Get by share code
    response = client.get(f"/api/trips/share/{share_code}")
    assert response.status_code == 200
    assert response.json()["name"] == "Public Trip"

def test_get_trip_by_share_code_with_password():
    """Test getting trip by share code when trip has password"""
    # Create trip with password
    create_response = client.post("/api/trips", json={
        "name": "Private Trip",
        "creator_name": "John",
        "currency_code": "USD",
        "password": "secret123"
    })
    share_code = create_response.json()["share_code"]
    
    # Get by share code (should require password)
    response = client.get(f"/api/trips/share/{share_code}")
    assert response.status_code == 401
    assert "password protected" in response.json()["detail"].lower()

def test_wrong_token_for_different_trip():
    """Test that token for one trip cannot access another trip"""
    # Create two trips with passwords
    trip1_response = client.post("/api/trips", json={
        "name": "Trip 1",
        "creator_name": "John",
        "currency_code": "USD",
        "password": "pass1"
    })
    trip1_id = trip1_response.json()["id"]
    
    trip2_response = client.post("/api/trips", json={
        "name": "Trip 2",
        "creator_name": "Jane",
        "currency_code": "USD",
        "password": "pass2"
    })
    trip2_id = trip2_response.json()["id"]
    
    # Authenticate for trip 1
    auth_response = client.post("/api/auth/trip/authenticate", json={
        "trip_id": trip1_id,
        "password": "pass1"
    })
    token1 = auth_response.json()["access_token"]
    
    # Try to access trip 2 with trip 1's token
    response = client.get(
        f"/api/trips/{trip2_id}",
        headers={"Authorization": f"Bearer {token1}"}
    )
    assert response.status_code == 403  # Forbidden

