"""Tests for deposit address and history endpoints."""

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

from apps.users.models import User
from apps.payments.models import PaymentAddress, Deposit


class DepositAddressTestCase(TestCase):
    """Test deposit address generation and retrieval."""

    def setUp(self):
        """Set up test data."""
        self.client = APIClient()
        self.user = User.objects.create_user(
            telegram_user_id=111111,
            first_name="Test User",
        )
        self.client.force_authenticate(user=self.user)

    def test_get_or_create_address_tron_first_time(self):
        """Test creating a new Tron deposit address."""
        response = self.client.post(
            '/api/payments/addresses/get-or-create/',
            {'blockchain': 'tron'}
        )
        
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(response.data['blockchain'], 'tron')
        self.assertEqual(response.data['asset'], 'USDT')
        self.assertEqual(response.data['status'], 'active')
        self.assertTrue(response.data['address'].startswith('T'))

    def test_get_or_create_address_bnb_first_time(self):
        """Test creating a new BNB deposit address."""
        response = self.client.post(
            '/api/payments/addresses/get-or-create/',
            {'blockchain': 'bnb'}
        )
        
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(response.data['blockchain'], 'bnb')
        self.assertTrue(response.data['address'].startswith('0x'))

    def test_get_or_create_address_returns_existing(self):
        """Test retrieving existing address instead of creating duplicate."""
        # Create address first time
        first_response = self.client.post(
            '/api/payments/addresses/get-or-create/',
            {'blockchain': 'tron'}
        )
        first_address = first_response.data['address']
        
        # Request again
        second_response = self.client.post(
            '/api/payments/addresses/get-or-create/',
            {'blockchain': 'tron'}
        )
        
        self.assertEqual(second_response.status_code, status.HTTP_200_OK)
        self.assertEqual(second_response.data['address'], first_address)
        
        # Verify only one address exists
        address_count = PaymentAddress.objects.filter(
            user=self.user,
            blockchain='tron'
        ).count()
        self.assertEqual(address_count, 1)

    def test_get_or_create_address_invalid_blockchain(self):
        """Test error for unsupported blockchain."""
        response = self.client.post(
            '/api/payments/addresses/get-or-create/',
            {'blockchain': 'ethereum'}
        )
        
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

    def test_get_or_create_address_case_insensitive(self):
        """Test blockchain parameter is case-insensitive."""
        response1 = self.client.post(
            '/api/payments/addresses/get-or-create/',
            {'blockchain': 'TRON'}
        )
        response2 = self.client.post(
            '/api/payments/addresses/get-or-create/',
            {'blockchain': 'TrOn'}
        )
        
        self.assertEqual(response1.status_code, status.HTTP_201_CREATED)
        self.assertEqual(response2.status_code, status.HTTP_200_OK)
        self.assertEqual(response1.data['address'], response2.data['address'])

    def test_get_or_create_address_separate_per_blockchain(self):
        """Test separate addresses are created for different blockchains."""
        tron_response = self.client.post(
            '/api/payments/addresses/get-or-create/',
            {'blockchain': 'tron'}
        )
        bnb_response = self.client.post(
            '/api/payments/addresses/get-or-create/',
            {'blockchain': 'bnb'}
        )
        
        self.assertNotEqual(tron_response.data['address'], bnb_response.data['address'])
        
        address_count = PaymentAddress.objects.filter(user=self.user).count()
        self.assertEqual(address_count, 2)

    def test_get_or_create_address_unauthenticated(self):
        """Test unauthenticated request is rejected."""
        self.client.force_authenticate(user=None)
        response = self.client.post(
            '/api/payments/addresses/get-or-create/',
            {'blockchain': 'tron'}
        )
        
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

    def test_list_payment_addresses(self):
        """Test listing user's payment addresses."""
        # Create addresses
        PaymentAddress.objects.create(
            user=self.user,
            blockchain='tron',
            asset='USDT',
            address='T1234567890',
            status=PaymentAddress.STATUS_ACTIVE,
        )
        PaymentAddress.objects.create(
            user=self.user,
            blockchain='bnb',
            asset='USDT',
            address='0x1234567890',
            status=PaymentAddress.STATUS_ACTIVE,
        )
        
        response = self.client.get('/api/payments/addresses/')
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data["results"]), 2)

    def test_list_payment_addresses_filters_by_user(self):
        """Test user only sees their own addresses."""
        other_user = User.objects.create_user(
            telegram_user_id=222222,
            first_name="Other User",
        )
        
        PaymentAddress.objects.create(
            user=self.user,
            blockchain='tron',
            asset='USDT',
            address='T1111111111',
            status=PaymentAddress.STATUS_ACTIVE,
        )
        PaymentAddress.objects.create(
            user=other_user,
            blockchain='tron',
            asset='USDT',
            address='T2222222222',
            status=PaymentAddress.STATUS_ACTIVE,
        )
        
        response = self.client.get('/api/payments/addresses/')
        
        self.assertEqual(len(response.data["results"]), 1)
        self.assertEqual(response.data["results"][0]['address'], 'T1111111111')


class DepositHistoryTestCase(TestCase):
    """Test deposit history retrieval."""

    def setUp(self):
        """Set up test data."""
        self.client = APIClient()
        self.user = User.objects.create_user(
            telegram_user_id=111111,
            first_name="Test User",
        )
        self.address = PaymentAddress.objects.create(
            user=self.user,
            blockchain='tron',
            asset='USDT',
            address='T1234567890',
            status=PaymentAddress.STATUS_ACTIVE,
        )
        self.client.force_authenticate(user=self.user)

    def test_deposit_history_empty(self):
        """Test empty deposit history."""
        response = self.client.get('/api/payments/deposits/')
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data["results"]), 0)

    def test_deposit_history_with_deposits(self):
        """Test retrieving deposit history."""
        Deposit.objects.create(
            user=self.user,
            address=self.address,
            blockchain='tron',
            asset='USDT',
            amount=Decimal('100.00000000'),
            transaction_hash='0xabc123',
            confirmations=10,
            status=Deposit.STATUS_CONFIRMED,
        )
        Deposit.objects.create(
            user=self.user,
            address=self.address,
            blockchain='tron',
            asset='USDT',
            amount=Decimal('50.50000000'),
            transaction_hash='0xdef456',
            confirmations=5,
            status=Deposit.STATUS_PENDING,
        )
        
        response = self.client.get('/api/payments/deposits/')
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data["results"]), 2)

    def test_deposit_history_precision(self):
        """Test deposit amounts maintain 8-decimal precision."""
        Deposit.objects.create(
            user=self.user,
            address=self.address,
            blockchain='tron',
            asset='USDT',
            amount=Decimal('123.45678901'),
            transaction_hash='0xabc123',
            confirmations=10,
            status=Deposit.STATUS_CONFIRMED,
        )
        
        response = self.client.get('/api/payments/deposits/')
        
        self.assertEqual(
            Decimal(str(response.data["results"][0]['amount'])),
            Decimal('123.45678901')
        )

    def test_deposit_history_filters_by_user(self):
        """Test user only sees their own deposits."""
        other_user = User.objects.create_user(
            telegram_user_id=222222,
            first_name="Other User",
        )
        other_address = PaymentAddress.objects.create(
            user=other_user,
            blockchain='tron',
            asset='USDT',
            address='T9999999999',
            status=PaymentAddress.STATUS_ACTIVE,
        )
        
        Deposit.objects.create(
            user=self.user,
            address=self.address,
            blockchain='tron',
            asset='USDT',
            amount=Decimal('100.00000000'),
            transaction_hash='0xabc123',
            status=Deposit.STATUS_CONFIRMED,
        )
        Deposit.objects.create(
            user=other_user,
            address=other_address,
            blockchain='tron',
            asset='USDT',
            amount=Decimal('200.00000000'),
            transaction_hash='0xdef456',
            status=Deposit.STATUS_CONFIRMED,
        )
        
        response = self.client.get('/api/payments/deposits/')
        
        self.assertEqual(len(response.data["results"]), 1)
        self.assertEqual(Decimal(str(response.data["results"][0]['amount'])), Decimal('100.00000000'))

    def test_deposit_history_ordering(self):
        """Test deposits are ordered by detected_at (latest first)."""
        from django.utils import timezone
        from datetime import timedelta
        
        old_deposit = Deposit.objects.create(
            user=self.user,
            address=self.address,
            blockchain='tron',
            asset='USDT',
            amount=Decimal('100.00000000'),
            transaction_hash='0xold',
            status=Deposit.STATUS_CONFIRMED,
        )
        old_deposit.detected_at = timezone.now() - timedelta(days=5)
        old_deposit.save()
        
        new_deposit = Deposit.objects.create(
            user=self.user,
            address=self.address,
            blockchain='tron',
            asset='USDT',
            amount=Decimal('200.00000000'),
            transaction_hash='0xnew',
            status=Deposit.STATUS_PENDING,
        )
        
        response = self.client.get('/api/payments/deposits/')
        
        # Latest first
        self.assertEqual(response.data["results"][0]['transaction_hash'], '0xnew')
        self.assertEqual(response.data["results"][1]['transaction_hash'], '0xold')

    def test_deposit_history_status_values(self):
        """Test all deposit status values are returned correctly."""
        statuses = ['pending', 'confirmed', 'failed', 'duplicate']
        
        for idx, status_value in enumerate(statuses):
            Deposit.objects.create(
                user=self.user,
                address=self.address,
                blockchain='tron',
                asset='USDT',
                amount=Decimal(f'{idx + 1}00.00000000'),
                transaction_hash=f'0x{status_value}',
                status=status_value,
            )
        
        response = self.client.get('/api/payments/deposits/')
        
        returned_statuses = [d['status'] for d in response.data["results"]]
        for status_value in statuses:
            self.assertIn(status_value, returned_statuses)

    def test_deposit_history_large_amounts(self):
        """Test handling very large deposit amounts."""
        Deposit.objects.create(
            user=self.user,
            address=self.address,
            blockchain='tron',
            asset='USDT',
            amount=Decimal('999999999.99999999'),
            transaction_hash='0xhuge',
            status=Deposit.STATUS_CONFIRMED,
        )
        
        response = self.client.get('/api/payments/deposits/')
        
        self.assertEqual(
            Decimal(str(response.data["results"][0]['amount'])),
            Decimal('1000000000.00000000')  # Database rounds to 8 decimals
        )

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

