"""Tests for referral investment summary API endpoints."""

from decimal import Decimal
from django.test import TestCase
from django.utils import timezone
from rest_framework.test import APIClient
from rest_framework import status

from apps.users.models import User
from apps.investments.models import Investment
from apps.referrals.models import Commission


class ReferralInvestmentSummaryTestCase(TestCase):
    """Test referral network with investment summary calculations."""

    def setUp(self):
        """Set up test data."""
        self.client = APIClient()
        
        # Create users
        self.upline = User.objects.create_user(
            telegram_user_id=111111,
            first_name="Upline",
            username="upline_user",
        )
        self.downline1 = User.objects.create_user(
            telegram_user_id=222222,
            first_name="Downline1",
            username="downline1",
            referred_by=self.upline,
        )
        self.downline2 = User.objects.create_user(
            telegram_user_id=333333,
            first_name="Downline2",
            username="downline2",
            referred_by=self.upline,
        )
        
        # Authenticate as upline
        self.client.force_authenticate(user=self.upline)

    def test_referral_network_no_investments(self):
        """Test referral network returns zero investment totals when no investments exist."""
        response = self.client.get('/api/referrals/')
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data["results"]), 2)
        
        for referral in response.data["results"]:
            self.assertEqual(Decimal(str(referral['active_investment_total'])), Decimal('0'))
            self.assertEqual(Decimal(str(referral['total_investment'])), Decimal('0'))

    def test_referral_network_with_pending_investments(self):
        """Test pending investments are included in both active and total."""
        # Create pending investments
        Investment.objects.create(
            user=self.downline1,
            amount=Decimal('100.00000000'),
            tier=1,
            daily_reward_rate=Decimal('1.00'),
            duration_days=120,
            status=Investment.STATUS_PENDING,
            start_date=timezone.now(),
            end_date=timezone.now() + timezone.timedelta(days=120),
        )
        
        response = self.client.get('/api/referrals/')
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        downline1_data = next(r for r in response.data["results"] if r['id'] == self.downline1.id)
        
        self.assertEqual(Decimal(str(downline1_data['active_investment_total'])), Decimal('100.00000000'))
        self.assertEqual(Decimal(str(downline1_data['total_investment'])), Decimal('100.00000000'))

    def test_referral_network_with_active_investments(self):
        """Test active investments are included in both active and total."""
        Investment.objects.create(
            user=self.downline1,
            amount=Decimal('500.50000000'),
            tier=2,
            daily_reward_rate=Decimal('1.50'),
            duration_days=130,
            status=Investment.STATUS_ACTIVE,
            start_date=timezone.now(),
            end_date=timezone.now() + timezone.timedelta(days=130),
        )
        
        response = self.client.get('/api/referrals/')
        
        downline1_data = next(r for r in response.data["results"] if r['id'] == self.downline1.id)
        
        self.assertEqual(Decimal(str(downline1_data['active_investment_total'])), Decimal('500.50000000'))
        self.assertEqual(Decimal(str(downline1_data['total_investment'])), Decimal('500.50000000'))

    def test_referral_network_with_completed_investments(self):
        """Test completed investments are NOT in active but ARE in total."""
        Investment.objects.create(
            user=self.downline1,
            amount=Decimal('1000.00000000'),
            tier=3,
            daily_reward_rate=Decimal('2.00'),
            duration_days=140,
            status=Investment.STATUS_COMPLETED,
            start_date=timezone.now() - timezone.timedelta(days=150),
            end_date=timezone.now() - timezone.timedelta(days=10),
        )
        
        response = self.client.get('/api/referrals/')
        
        downline1_data = next(r for r in response.data["results"] if r['id'] == self.downline1.id)
        
        self.assertEqual(Decimal(str(downline1_data['active_investment_total'])), Decimal('0'))
        self.assertEqual(Decimal(str(downline1_data['total_investment'])), Decimal('1000.00000000'))

    def test_referral_network_mixed_investment_statuses(self):
        """Test correct totals with mixed investment statuses."""
        # Pending
        Investment.objects.create(
            user=self.downline1,
            amount=Decimal('100.00000000'),
            tier=1,
            daily_reward_rate=Decimal('1.00'),
            duration_days=120,
            status=Investment.STATUS_PENDING,
            start_date=timezone.now(),
            end_date=timezone.now() + timezone.timedelta(days=120),
        )
        # Active
        Investment.objects.create(
            user=self.downline1,
            amount=Decimal('500.00000000'),
            tier=2,
            daily_reward_rate=Decimal('1.50'),
            duration_days=130,
            status=Investment.STATUS_ACTIVE,
            start_date=timezone.now(),
            end_date=timezone.now() + timezone.timedelta(days=130),
        )
        # Completed
        Investment.objects.create(
            user=self.downline1,
            amount=Decimal('1000.00000000'),
            tier=3,
            daily_reward_rate=Decimal('2.00'),
            duration_days=140,
            status=Investment.STATUS_COMPLETED,
            start_date=timezone.now() - timezone.timedelta(days=150),
            end_date=timezone.now() - timezone.timedelta(days=10),
        )
        
        response = self.client.get('/api/referrals/')
        
        downline1_data = next(r for r in response.data["results"] if r['id'] == self.downline1.id)
        
        # Active = pending + active = 100 + 500 = 600
        self.assertEqual(Decimal(str(downline1_data['active_investment_total'])), Decimal('600.00000000'))
        # Total = pending + active + completed = 100 + 500 + 1000 = 1600
        self.assertEqual(Decimal(str(downline1_data['total_investment'])), Decimal('1600.00000000'))

    def test_referral_network_multiple_downlines(self):
        """Test investment totals calculated separately for each downline."""
        # Downline 1 investments
        Investment.objects.create(
            user=self.downline1,
            amount=Decimal('300.00000000'),
            tier=1,
            daily_reward_rate=Decimal('1.00'),
            duration_days=120,
            status=Investment.STATUS_ACTIVE,
            start_date=timezone.now(),
            end_date=timezone.now() + timezone.timedelta(days=120),
        )
        
        # Downline 2 investments
        Investment.objects.create(
            user=self.downline2,
            amount=Decimal('700.00000000'),
            tier=2,
            daily_reward_rate=Decimal('1.50'),
            duration_days=130,
            status=Investment.STATUS_ACTIVE,
            start_date=timezone.now(),
            end_date=timezone.now() + timezone.timedelta(days=130),
        )
        
        response = self.client.get('/api/referrals/')
        
        downline1_data = next(r for r in response.data["results"] if r['id'] == self.downline1.id)
        downline2_data = next(r for r in response.data["results"] if r['id'] == self.downline2.id)
        
        self.assertEqual(Decimal(str(downline1_data['active_investment_total'])), Decimal('300.00000000'))
        self.assertEqual(Decimal(str(downline2_data['active_investment_total'])), Decimal('700.00000000'))

    def test_referral_network_high_precision_amounts(self):
        """Test investment totals maintain 8-decimal precision."""
        Investment.objects.create(
            user=self.downline1,
            amount=Decimal('123.45678901'),  # More than 8 decimals
            tier=1,
            daily_reward_rate=Decimal('1.00'),
            duration_days=120,
            status=Investment.STATUS_ACTIVE,
            start_date=timezone.now(),
            end_date=timezone.now() + timezone.timedelta(days=120),
        )
        
        response = self.client.get('/api/referrals/')
        
        downline1_data = next(r for r in response.data["results"] if r['id'] == self.downline1.id)
        
        # Should be stored with 8 decimal precision
        amount = Decimal(str(downline1_data['active_investment_total']))
        self.assertEqual(amount, Decimal('123.45678901'))

    def test_referral_network_unauthenticated(self):
        """Test unauthenticated request is rejected."""
        self.client.force_authenticate(user=None)
        response = self.client.get('/api/referrals/')
        
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

    def test_referral_network_only_direct_referrals(self):
        """Test only direct referrals (Level 1) are included, not Level 2."""
        # Create Level 2 referral
        level2_user = User.objects.create_user(
            telegram_user_id=444444,
            first_name="Level2",
            username="level2",
            referred_by=self.downline1,  # Referred by downline1, not upline
        )
        
        Investment.objects.create(
            user=level2_user,
            amount=Decimal('999.00000000'),
            tier=3,
            daily_reward_rate=Decimal('2.00'),
            duration_days=140,
            status=Investment.STATUS_ACTIVE,
            start_date=timezone.now(),
            end_date=timezone.now() + timezone.timedelta(days=140),
        )
        
        response = self.client.get('/api/referrals/')
        
        # Should only have 2 referrals (downline1 and downline2), not level2_user
        self.assertEqual(len(response.data["results"]), 2)
        user_ids = [r['id'] for r in response.data["results"]]
        self.assertIn(self.downline1.id, user_ids)
        self.assertIn(self.downline2.id, user_ids)
        self.assertNotIn(level2_user.id, user_ids)

    def test_referral_network_large_investment_amounts(self):
        """Test handling of very large investment amounts."""
        Investment.objects.create(
            user=self.downline1,
            amount=Decimal('999999999.99999999'),  # Max possible with 20,8
            tier=6,
            daily_reward_rate=Decimal('3.50'),
            duration_days=180,
            status=Investment.STATUS_ACTIVE,
            start_date=timezone.now(),
            end_date=timezone.now() + timezone.timedelta(days=180),
        )
        
        response = self.client.get('/api/referrals/')
        
        downline1_data = next(r for r in response.data["results"] if r['id'] == self.downline1.id)
        
        self.assertEqual(
            Decimal(str(downline1_data['active_investment_total'])),
            Decimal('1000000000.00000000')  # Database rounds to 8 decimals
        )

    def test_referral_network_zero_amount_investment(self):
        """Test edge case: investment with zero amount (if allowed by validation)."""
        # Note: This might fail validation, but testing data integrity if it somehow exists
        try:
            Investment.objects.create(
                user=self.downline1,
                amount=Decimal('0.00000000'),
                tier=1,
                daily_reward_rate=Decimal('1.00'),
                duration_days=120,
                status=Investment.STATUS_ACTIVE,
                start_date=timezone.now(),
                end_date=timezone.now() + timezone.timedelta(days=120),
            )
            
            response = self.client.get('/api/referrals/')
            downline1_data = next(r for r in response.data["results"] if r['id'] == self.downline1.id)
            
            self.assertEqual(Decimal(str(downline1_data['active_investment_total'])), Decimal('0'))
        except Exception:
            # If validation prevents zero amounts, that's expected
            pass

