"""
Integration tests for repository implementations.

These tests verify that repositories correctly interact with the database,
including CRUD operations, error handling, and edge cases.
"""
import pytest
import uuid
from datetime import datetime, timezone
from typing import List

from src.foundation.data.models import (
    User,
    Agent,
    Context,
    ToolConfig,
    APIKey,
    AccountBalance,
    UsageTracking,
    Knowledge,
    Cron,
)
from src.foundation.repositories.users import SupabaseUserRepository
from src.foundation.repositories.agents import SupabaseAgentRepository
from src.foundation.repositories.contexts import SupabaseContextRepository
from src.foundation.repositories.tool_configs import SupabaseToolConfigRepository
from src.foundation.repositories.api_keys import SupabaseAPIKeyRepository
from src.foundation.repositories.balance import SupabaseBalanceRepository
from src.foundation.repositories.usage_tracking import SupabaseUsageTrackingRepository
from src.foundation.repositories.knowledge import SupabaseKnowledgeRepository
from src.foundation.repositories.cron import SupabaseCronRepository


class TestUserRepository:
    """Integration tests for UserRepository"""
    
    @pytest.mark.asyncio
    async def test_create_user(self, user_repository: SupabaseUserRepository):
        """Test creating a new user"""
        user = User(
            id=str(uuid.uuid4()),
            name="Test User",
            email="test@example.com",
            password_hash="hashed_password",
            user_type="regular",
            email_verified=False,
        )
        
        created = await user_repository.create_user(user)
        
        assert created.id == user.id
        assert created.name == user.name
        assert created.email == user.email
        assert created.created_at is not None
    
    @pytest.mark.asyncio
    async def test_get_user_by_id(self, user_repository: SupabaseUserRepository):
        """Test retrieving a user by ID"""
        user = User(
            id=str(uuid.uuid4()),
            name="Test User",
            email="test@example.com",
            password_hash="hashed_password",
            user_type="regular",
        )
        
        created = await user_repository.create_user(user)
        retrieved = await user_repository.get_user_by_id(created.id)
        
        assert retrieved is not None
        assert retrieved.id == created.id
        assert retrieved.email == created.email
    
    @pytest.mark.asyncio
    async def test_get_user_by_email(self, user_repository: SupabaseUserRepository):
        """Test retrieving a user by email"""
        user = User(
            id=str(uuid.uuid4()),
            name="Test User",
            email="test@example.com",
            password_hash="hashed_password",
            user_type="regular",
        )
        
        created = await user_repository.create_user(user)
        retrieved = await user_repository.get_user_by_email(created.email)
        
        assert retrieved is not None
        assert retrieved.id == created.id
        assert retrieved.email == created.email
    
    @pytest.mark.asyncio
    async def test_update_user(self, user_repository: SupabaseUserRepository):
        """Test updating a user"""
        user = User(
            id=str(uuid.uuid4()),
            name="Test User",
            email="test@example.com",
            password_hash="hashed_password",
            user_type="regular",
        )
        
        created = await user_repository.create_user(user)
        created.name = "Updated Name"
        
        updated = await user_repository.update_user(created)
        
        assert updated.name == "Updated Name"
        assert updated.updated_at is not None
    
    @pytest.mark.asyncio
    async def test_get_users(self, user_repository: SupabaseUserRepository):
        """Test retrieving all users"""
        # Create multiple users
        for i in range(3):
            user = User(
                id=str(uuid.uuid4()),
                name=f"Test User {i}",
                email=f"test{i}@example.com",
                password_hash="hashed_password",
                user_type="regular",
            )
            await user_repository.create_user(user)
        
        users = await user_repository.get_users()
        
        assert len(users) >= 3
    
    @pytest.mark.asyncio
    async def test_delete_user(self, user_repository: SupabaseUserRepository):
        """Test deleting a user"""
        user = User(
            id=str(uuid.uuid4()),
            name="Test User",
            email="test@example.com",
            password_hash="hashed_password",
            user_type="regular",
        )
        
        created = await user_repository.create_user(user)
        success = await user_repository.delete_user(created.id)
        
        assert success is True
        
        # Verify user is deleted
        retrieved = await user_repository.get_user_by_id(created.id)
        assert retrieved is None


class TestAgentRepository:
    """Integration tests for AgentRepository"""
    
    @pytest.mark.asyncio
    async def test_create_agent(
        self,
        agent_repository: SupabaseAgentRepository,
        user_repository: SupabaseUserRepository,
    ):
        """Test creating a new agent"""
        # Create a user first
        user = User(
            id=str(uuid.uuid4()),
            name="Test User",
            email="test@example.com",
            password_hash="hashed_password",
            user_type="regular",
        )
        created_user = await user_repository.create_user(user)
        
        agent = Agent(
            id=str(uuid.uuid4()),
            name="Test Agent",
            description="Test agent description",
            creator_id=created_user.id,
            model="openai/gpt-4o-mini",
            provider="openai",
            system_prompt="You are a helpful assistant",
        )
        
        created = await agent_repository.create_agent(agent)
        
        assert created.id == agent.id
        assert created.name == agent.name
        assert created.creator_id == created_user.id
    
    @pytest.mark.asyncio
    async def test_get_agent_by_id(
        self,
        agent_repository: SupabaseAgentRepository,
        user_repository: SupabaseUserRepository,
    ):
        """Test retrieving an agent by ID"""
        user = User(
            id=str(uuid.uuid4()),
            name="Test User",
            email="test@example.com",
            password_hash="hashed_password",
            user_type="regular",
        )
        created_user = await user_repository.create_user(user)
        
        agent = Agent(
            id=str(uuid.uuid4()),
            name="Test Agent",
            description="Test agent description",
            creator_id=created_user.id,
            model="openai/gpt-4o-mini",
            provider="openai",
            system_prompt="You are a helpful assistant",
        )
        
        created = await agent_repository.create_agent(agent)
        retrieved = await agent_repository.get_agent_by_id(created.id)
        
        assert retrieved is not None
        assert retrieved.id == created.id
        assert retrieved.name == created.name
    
    @pytest.mark.asyncio
    async def test_get_agents_by_creator(
        self,
        agent_repository: SupabaseAgentRepository,
        user_repository: SupabaseUserRepository,
    ):
        """Test retrieving agents by creator ID"""
        user = User(
            id=str(uuid.uuid4()),
            name="Test User",
            email="test@example.com",
            password_hash="hashed_password",
            user_type="regular",
        )
        created_user = await user_repository.create_user(user)
        
        # Create multiple agents for the same user
        for i in range(3):
            agent = Agent(
                id=str(uuid.uuid4()),
                name=f"Test Agent {i}",
                description="Test agent description",
                creator_id=created_user.id,
                model="openai/gpt-4o-mini",
                provider="openai",
                system_prompt="You are a helpful assistant",
            )
            await agent_repository.create_agent(agent)
        
        agents = await agent_repository.get_agents_by_creator(created_user.id)
        
        assert len(agents) >= 3


class TestBalanceRepository:
    """Integration tests for BalanceRepository"""
    
    @pytest.mark.asyncio
    async def test_create_account_balance(
        self,
        balance_repository: SupabaseBalanceRepository,
        user_repository: SupabaseUserRepository,
    ):
        """Test creating an account balance"""
        user = User(
            id=str(uuid.uuid4()),
            name="Test User",
            email="test@example.com",
            password_hash="hashed_password",
            user_type="regular",
        )
        created_user = await user_repository.create_user(user)
        
        balance = AccountBalance(
            user_id=created_user.id,
            balance=100.0,
            free_credits=10.0,
            paid_credits=90.0,
        )
        
        created = await balance_repository.upsert_account_balance(balance)
        
        assert created.user_id == created_user.id
        assert created.balance == 100.0
        assert created.free_credits == 10.0
        assert created.paid_credits == 90.0
    
    @pytest.mark.asyncio
    async def test_get_account_balance(
        self,
        balance_repository: SupabaseBalanceRepository,
        user_repository: SupabaseUserRepository,
    ):
        """Test retrieving an account balance"""
        user = User(
            id=str(uuid.uuid4()),
            name="Test User",
            email="test@example.com",
            password_hash="hashed_password",
            user_type="regular",
        )
        created_user = await user_repository.create_user(user)
        
        balance = AccountBalance(
            user_id=created_user.id,
            balance=100.0,
            free_credits=10.0,
            paid_credits=90.0,
        )
        
        created = await balance_repository.upsert_account_balance(balance)
        retrieved = await balance_repository.get_account_balance(created_user.id)
        
        assert retrieved is not None
        assert retrieved.user_id == created_user.id
        assert retrieved.balance == 100.0
    
    @pytest.mark.asyncio
    async def test_update_account_balance(
        self,
        balance_repository: SupabaseBalanceRepository,
        user_repository: SupabaseUserRepository,
    ):
        """Test updating an account balance"""
        user = User(
            id=str(uuid.uuid4()),
            name="Test User",
            email="test@example.com",
            password_hash="hashed_password",
            user_type="regular",
        )
        created_user = await user_repository.create_user(user)
        
        balance = AccountBalance(
            user_id=created_user.id,
            balance=100.0,
            free_credits=10.0,
            paid_credits=90.0,
        )
        
        created = await balance_repository.upsert_account_balance(balance)
        created.paid_credits = 150.0
        created.balance = 160.0
        
        updated = await balance_repository.upsert_account_balance(created)
        
        assert updated.paid_credits == 150.0
        assert updated.balance == 160.0


class TestUsageTrackingRepository:
    """Integration tests for UsageTrackingRepository"""
    
    @pytest.mark.asyncio
    async def test_create_usage_tracking(
        self,
        usage_tracking_repository: SupabaseUsageTrackingRepository,
        user_repository: SupabaseUserRepository,
    ):
        """Test creating a usage tracking record"""
        user = User(
            id=str(uuid.uuid4()),
            name="Test User",
            email="test@example.com",
            password_hash="hashed_password",
            user_type="regular",
        )
        created_user = await user_repository.create_user(user)
        
        usage = UsageTracking(
            user_id=created_user.id,
            provider="openai",
            model="gpt-4o-mini",
            prompt_tokens=100,
            completion_tokens=50,
            total_tokens=150,
            prompt_cost=0.001,
            completion_cost=0.002,
            total_cost=0.003,
        )
        
        created = await usage_tracking_repository.create_usage_tracking(usage)
        
        assert created.user_id == created_user.id
        assert created.provider == "openai"
        assert created.total_tokens == 150
        assert created.total_cost == 0.003
    
    @pytest.mark.asyncio
    async def test_get_usage_by_date_range(
        self,
        usage_tracking_repository: SupabaseUsageTrackingRepository,
        user_repository: SupabaseUserRepository,
    ):
        """Test retrieving usage by date range"""
        user = User(
            id=str(uuid.uuid4()),
            name="Test User",
            email="test@example.com",
            password_hash="hashed_password",
            user_type="regular",
        )
        created_user = await user_repository.create_user(user)
        
        # Create multiple usage records
        for i in range(3):
            usage = UsageTracking(
                user_id=created_user.id,
                provider="openai",
                model="gpt-4o-mini",
                prompt_tokens=100,
                completion_tokens=50,
                total_tokens=150,
                total_cost=0.003,
            )
            await usage_tracking_repository.create_usage_tracking(usage)
        
        from datetime import date, timedelta
        start_date = date.today() - timedelta(days=7)
        end_date = date.today()
        
        usage_records = await usage_tracking_repository.get_usage_by_date_range(
            created_user.id, start_date, end_date
        )
        
        assert len(usage_records) >= 3

