"""
Test Suite for Opora Vie - PostgreSQL Migration Testing
Testing:
- All API endpoints work with PostgreSQL (migrated from MongoDB)
- /api/specialties returns list
- /api/auth/register creates user in PostgreSQL
- /api/auth/login authenticates user
- /api/practitioners GET/POST
- /api/appointments POST
"""

import pytest
import requests
import os
import uuid

BASE_URL = os.environ.get('REACT_APP_BACKEND_URL', '').rstrip('/')
if not BASE_URL:
    BASE_URL = 'https://soins-match.preview.emergentagent.com'

# Test data with unique identifiers
TEST_CLIENT_EMAIL = f"TEST_pgsql_client_{uuid.uuid4().hex[:8]}@test.com"
TEST_PRACTITIONER_EMAIL = f"TEST_pgsql_pract_{uuid.uuid4().hex[:8]}@test.com"
TEST_PASSWORD = "TestPassword123!"
PRACTITIONER_ACCESS_CODE = "OporaVie2026!"


class TestAPIHealth:
    """Basic API health checks"""
    
    def test_api_is_accessible(self):
        """Test that API responds"""
        response = requests.get(f"{BASE_URL}/api/")
        # API should respond (may be 200 or 404 depending on root endpoint)
        assert response.status_code in [200, 404, 405]
        print(f"✓ API accessible at {BASE_URL}")
    
    def test_specialties_endpoint_returns_list(self):
        """Test /api/specialties returns list of specialties"""
        response = requests.get(f"{BASE_URL}/api/specialties")
        
        assert response.status_code == 200, f"Specialties failed: {response.text}"
        data = response.json()
        
        assert isinstance(data, list), "Response should be a list"
        assert len(data) >= 1, "Should have at least 1 specialty"
        
        # Validate structure
        for specialty in data:
            assert "id" in specialty, "Specialty should have 'id'"
            assert "name" in specialty, "Specialty should have 'name'"
        
        specialty_ids = [s["id"] for s in data]
        print(f"✓ Specialties endpoint returns {len(data)} specialties: {specialty_ids}")


class TestClientAuthFlow:
    """Test client registration and login with PostgreSQL"""
    
    def test_client_registration(self):
        """Test POST /api/auth/register creates a client"""
        register_data = {
            "email": TEST_CLIENT_EMAIL,
            "password": TEST_PASSWORD,
            "first_name": "Test",
            "last_name": "Client",
            "phone": "0601020304",
            "role": "client"
        }
        
        response = requests.post(f"{BASE_URL}/api/auth/register", json=register_data)
        
        assert response.status_code == 200, f"Registration failed: {response.text}"
        data = response.json()
        
        # Validate response structure
        assert "access_token" in data, "Response should contain access_token"
        assert "user" in data, "Response should contain user"
        assert data["user"]["email"] == TEST_CLIENT_EMAIL
        assert data["user"]["first_name"] == "Test"
        assert data["user"]["last_name"] == "Client"
        assert data["user"]["role"] == "client"
        assert "id" in data["user"], "User should have ID"
        
        print(f"✓ Client registration successful: {data['user']['email']}")
        return data["access_token"]
    
    def test_client_login(self):
        """Test POST /api/auth/login authenticates client"""
        # First ensure user exists
        requests.post(f"{BASE_URL}/api/auth/register", json={
            "email": TEST_CLIENT_EMAIL,
            "password": TEST_PASSWORD,
            "first_name": "Test",
            "last_name": "Client",
            "role": "client"
        })
        
        login_data = {
            "email": TEST_CLIENT_EMAIL,
            "password": TEST_PASSWORD
        }
        
        response = requests.post(f"{BASE_URL}/api/auth/login", json=login_data)
        
        assert response.status_code == 200, f"Login failed: {response.text}"
        data = response.json()
        
        assert "access_token" in data
        assert "user" in data
        assert data["user"]["email"] == TEST_CLIENT_EMAIL
        
        print(f"✓ Client login successful")
        return data["access_token"]
    
    def test_login_invalid_credentials(self):
        """Test login with wrong password returns 401"""
        response = requests.post(f"{BASE_URL}/api/auth/login", json={
            "email": TEST_CLIENT_EMAIL,
            "password": "wrongpassword"
        })
        
        assert response.status_code == 401, f"Expected 401, got {response.status_code}"
        print(f"✓ Invalid login returns 401")
    
    def test_duplicate_registration_fails(self):
        """Test that registering with same email fails"""
        # First registration
        requests.post(f"{BASE_URL}/api/auth/register", json={
            "email": TEST_CLIENT_EMAIL,
            "password": TEST_PASSWORD,
            "first_name": "Test",
            "last_name": "Client",
            "role": "client"
        })
        
        # Second registration should fail
        response = requests.post(f"{BASE_URL}/api/auth/register", json={
            "email": TEST_CLIENT_EMAIL,
            "password": TEST_PASSWORD,
            "first_name": "Test",
            "last_name": "Client",
            "role": "client"
        })
        
        assert response.status_code == 400, f"Expected 400 for duplicate, got {response.status_code}"
        print(f"✓ Duplicate registration returns 400")


class TestPractitionerAccessCode:
    """Test practitioner access code verification"""
    
    def test_valid_access_code(self):
        """Test valid access code is accepted"""
        response = requests.post(f"{BASE_URL}/api/auth/verify-practitioner-access", 
                                json={"code": PRACTITIONER_ACCESS_CODE})
        
        assert response.status_code == 200, f"Valid code failed: {response.text}"
        data = response.json()
        assert data.get("valid") == True
        print(f"✓ Valid access code accepted")
    
    def test_invalid_access_code(self):
        """Test invalid access code is rejected"""
        response = requests.post(f"{BASE_URL}/api/auth/verify-practitioner-access",
                                json={"code": "wrongcode123"})
        
        assert response.status_code == 403, f"Expected 403 for invalid code, got {response.status_code}"
        print(f"✓ Invalid access code rejected with 403")


class TestPractitionerFlow:
    """Test practitioner registration and profile creation"""
    
    @pytest.fixture(scope="class")
    def practitioner_auth(self):
        """Register and login a practitioner"""
        # Register
        register_response = requests.post(f"{BASE_URL}/api/auth/register", json={
            "email": TEST_PRACTITIONER_EMAIL,
            "password": TEST_PASSWORD,
            "first_name": "Test",
            "last_name": "Practitioner",
            "phone": "0611223344",
            "role": "practitioner"
        })
        
        if register_response.status_code == 400:
            # Already exists, login
            login_response = requests.post(f"{BASE_URL}/api/auth/login", json={
                "email": TEST_PRACTITIONER_EMAIL,
                "password": TEST_PASSWORD
            })
            if login_response.status_code == 200:
                return login_response.json()
        elif register_response.status_code == 200:
            return register_response.json()
        
        pytest.skip("Could not create practitioner account")
    
    def test_practitioner_registration(self, practitioner_auth):
        """Verify practitioner registration works"""
        assert "access_token" in practitioner_auth
        assert practitioner_auth["user"]["role"] == "practitioner"
        print(f"✓ Practitioner registered: {practitioner_auth['user']['email']}")
    
    def test_create_practitioner_profile(self, practitioner_auth):
        """Test POST /api/practitioners creates a profile"""
        token = practitioner_auth["access_token"]
        
        profile_data = {
            "specialty": "naturopathe",
            "bio": "Test practitioner bio for PostgreSQL migration testing",
            "country": "FR",
            "region": "IDF",
            "city": "Paris",
            "postal_code": "75001",
            "consultation_types": ["cabinet", "visio"],
            "price": 60.0,
            "consultation_duration": 60
        }
        
        response = requests.post(f"{BASE_URL}/api/practitioners",
                                json=profile_data,
                                headers={"Authorization": f"Bearer {token}"})
        
        if response.status_code == 400 and "existant" in response.text:
            print(f"ℹ Practitioner profile already exists")
            return
        
        assert response.status_code == 200, f"Profile creation failed: {response.text}"
        data = response.json()
        
        assert data["specialty"] == "naturopathe"
        assert data["city"] == "Paris"
        assert "cabinet" in data["consultation_types"]
        assert "id" in data
        
        print(f"✓ Practitioner profile created with ID: {data['id']}")


class TestPractitionersAPI:
    """Test practitioners endpoints"""
    
    def test_get_practitioners_list(self):
        """Test GET /api/practitioners returns practitioners"""
        response = requests.get(f"{BASE_URL}/api/practitioners")
        
        assert response.status_code == 200, f"Failed: {response.text}"
        data = response.json()
        
        assert isinstance(data, list), "Should return a list"
        
        if len(data) > 0:
            practitioner = data[0]
            assert "id" in practitioner
            assert "specialty" in practitioner
            assert "user" in practitioner
            assert practitioner["user"] is not None
            print(f"✓ Practitioners list returns {len(data)} practitioners")
        else:
            print(f"ℹ No practitioners found (empty database)")
    
    def test_get_practitioners_with_specialty_filter(self):
        """Test GET /api/practitioners?specialty=naturopathe works"""
        response = requests.get(f"{BASE_URL}/api/practitioners?specialty=naturopathe")
        
        assert response.status_code == 200
        data = response.json()
        
        assert isinstance(data, list)
        for p in data:
            assert p["specialty"] == "naturopathe", f"Filtered wrong specialty: {p['specialty']}"
        
        print(f"✓ Specialty filter works, {len(data)} naturopathes found")
    
    def test_get_practitioners_with_country_filter(self):
        """Test GET /api/practitioners?country=FR works"""
        response = requests.get(f"{BASE_URL}/api/practitioners?country=FR")
        
        assert response.status_code == 200
        data = response.json()
        
        assert isinstance(data, list)
        for p in data:
            assert p.get("country") == "FR", f"Country filter failed: {p.get('country')}"
        
        print(f"✓ Country filter works, {len(data)} practitioners in FR")
    
    def test_get_practitioner_by_id(self):
        """Test GET /api/practitioners/{id} returns single practitioner"""
        # First get list to get an ID
        list_response = requests.get(f"{BASE_URL}/api/practitioners")
        if list_response.status_code == 200 and len(list_response.json()) > 0:
            practitioner_id = list_response.json()[0]["id"]
            
            response = requests.get(f"{BASE_URL}/api/practitioners/{practitioner_id}")
            
            assert response.status_code == 200, f"Failed: {response.text}"
            data = response.json()
            
            assert data["id"] == practitioner_id
            assert "specialty" in data
            assert "user" in data
            
            print(f"✓ GET practitioner by ID works")
        else:
            print(f"ℹ No practitioners to test detail view")
    
    def test_get_nonexistent_practitioner_returns_404(self):
        """Test GET /api/practitioners/{invalid_id} returns 404"""
        fake_id = str(uuid.uuid4())
        response = requests.get(f"{BASE_URL}/api/practitioners/{fake_id}")
        
        assert response.status_code == 404
        print(f"✓ Non-existent practitioner returns 404")


class TestAppointmentCreation:
    """Test appointment creation flow"""
    
    @pytest.fixture(scope="class")
    def client_token(self):
        """Get or create a client"""
        email = f"TEST_apt_client_{uuid.uuid4().hex[:8]}@test.com"
        
        # Register
        reg_response = requests.post(f"{BASE_URL}/api/auth/register", json={
            "email": email,
            "password": TEST_PASSWORD,
            "first_name": "Appointment",
            "last_name": "Client",
            "role": "client"
        })
        
        if reg_response.status_code == 200:
            return reg_response.json()["access_token"]
        elif reg_response.status_code == 400:
            # Login
            login_response = requests.post(f"{BASE_URL}/api/auth/login", json={
                "email": email,
                "password": TEST_PASSWORD
            })
            if login_response.status_code == 200:
                return login_response.json()["access_token"]
        
        pytest.skip("Could not get client token")
    
    def test_create_appointment(self, client_token):
        """Test POST /api/appointments creates an appointment"""
        # First get a practitioner
        practitioners_response = requests.get(f"{BASE_URL}/api/practitioners")
        
        if practitioners_response.status_code != 200 or len(practitioners_response.json()) == 0:
            pytest.skip("No practitioners available for appointment test")
        
        practitioner = practitioners_response.json()[0]
        practitioner_id = practitioner["id"]
        
        appointment_data = {
            "practitioner_id": practitioner_id,
            "date": "2026-02-15",
            "start_time": "10:00",
            "consultation_type": "cabinet",
            "notes": "Test appointment from PostgreSQL migration testing"
        }
        
        response = requests.post(f"{BASE_URL}/api/appointments",
                                json=appointment_data,
                                headers={"Authorization": f"Bearer {client_token}"})
        
        assert response.status_code == 200, f"Appointment creation failed: {response.text}"
        data = response.json()
        
        # Validate response
        assert "id" in data
        assert data["practitioner_id"] == practitioner_id
        assert data["date"] == "2026-02-15"
        assert data["start_time"] == "10:00"
        assert data["status"] == "pending"
        assert "end_time" in data  # Should be calculated
        
        print(f"✓ Appointment created: ID={data['id']}, date={data['date']}, time={data['start_time']}-{data['end_time']}")
    
    def test_appointment_without_auth_fails(self):
        """Test that appointment creation requires authentication"""
        appointment_data = {
            "practitioner_id": str(uuid.uuid4()),
            "date": "2026-02-15",
            "start_time": "10:00",
            "consultation_type": "cabinet"
        }
        
        response = requests.post(f"{BASE_URL}/api/appointments", json=appointment_data)
        
        assert response.status_code in [401, 403], f"Expected auth error, got {response.status_code}"
        print(f"✓ Appointment without auth returns {response.status_code}")


class TestUserProfile:
    """Test user profile operations"""
    
    @pytest.fixture(scope="class")
    def user_auth(self):
        """Get authenticated user"""
        email = f"TEST_profile_{uuid.uuid4().hex[:8]}@test.com"
        
        reg_response = requests.post(f"{BASE_URL}/api/auth/register", json={
            "email": email,
            "password": TEST_PASSWORD,
            "first_name": "Profile",
            "last_name": "Test",
            "role": "client"
        })
        
        if reg_response.status_code == 200:
            return reg_response.json()
        pytest.skip("Could not create user")
    
    def test_get_me(self, user_auth):
        """Test GET /api/auth/me returns current user"""
        token = user_auth["access_token"]
        
        response = requests.get(f"{BASE_URL}/api/auth/me",
                               headers={"Authorization": f"Bearer {token}"})
        
        assert response.status_code == 200, f"Failed: {response.text}"
        data = response.json()
        
        assert "id" in data
        assert "email" in data
        assert "first_name" in data
        assert "last_name" in data
        assert "role" in data
        
        print(f"✓ GET /api/auth/me returns user: {data['email']}")
    
    def test_update_me(self, user_auth):
        """Test PUT /api/auth/me updates user"""
        token = user_auth["access_token"]
        
        update_data = {
            "first_name": "UpdatedName",
            "phone": "0699887766"
        }
        
        response = requests.put(f"{BASE_URL}/api/auth/me",
                               json=update_data,
                               headers={"Authorization": f"Bearer {token}"})
        
        assert response.status_code == 200, f"Update failed: {response.text}"
        data = response.json()
        
        assert data["first_name"] == "UpdatedName"
        
        print(f"✓ PUT /api/auth/me updates user profile")


class TestTimeSlots:
    """Test practitioner time slots"""
    
    def test_get_practitioner_slots(self):
        """Test GET /api/slots/practitioner/{id} returns time slots"""
        # Get a practitioner
        practitioners_response = requests.get(f"{BASE_URL}/api/practitioners")
        
        if practitioners_response.status_code != 200 or len(practitioners_response.json()) == 0:
            print(f"ℹ No practitioners available for slot test")
            return
        
        practitioner_id = practitioners_response.json()[0]["id"]
        
        response = requests.get(f"{BASE_URL}/api/slots/practitioner/{practitioner_id}")
        
        assert response.status_code == 200, f"Failed: {response.text}"
        data = response.json()
        
        assert isinstance(data, list)
        print(f"✓ GET practitioner slots returns {len(data)} slots")
    
    def test_get_booked_appointments(self):
        """Test GET /api/appointments/practitioner/{id}/booked returns booked slots"""
        # Get a practitioner
        practitioners_response = requests.get(f"{BASE_URL}/api/practitioners")
        
        if practitioners_response.status_code != 200 or len(practitioners_response.json()) == 0:
            print(f"ℹ No practitioners available for booked slots test")
            return
        
        practitioner_id = practitioners_response.json()[0]["id"]
        
        response = requests.get(f"{BASE_URL}/api/appointments/practitioner/{practitioner_id}/booked")
        
        assert response.status_code == 200
        data = response.json()
        
        assert isinstance(data, list)
        for slot in data:
            assert "date" in slot
            assert "start_time" in slot
        
        print(f"✓ GET booked slots returns {len(data)} booked appointments")


if __name__ == "__main__":
    pytest.main([__file__, "-v", "--tb=short"])
