"""
Tests for wallet_utils models.
"""
from decimal import Decimal
from django.test import TestCase
from django.core.exceptions import ValidationError

from wallet_utils.models import AbstractWalletTransaction, WalletTransaction
from tests.models import TestUser


class WalletTransactionModelTests(TestCase):
    """Tests for WalletTransaction model."""
    
    def test_create_transaction(self):
        """Test creating a transaction record."""
        transaction = WalletTransaction.objects.create(
            wtype="credit_balance",
            iid=-100,
            uid=123,
            type="c",
            amount=Decimal("100.00"),
            balance=Decimal("100.00"),
            trans_type=1000,
            descr="Test transaction",
        )
        
        self.assertIsNotNone(transaction.id)
        self.assertEqual(transaction.wtype, "credit_balance")
        self.assertEqual(transaction.iid, -100)
        self.assertEqual(transaction.uid, 123)
        self.assertEqual(transaction.type, "c")
        self.assertEqual(transaction.amount, Decimal("100.00"))
        self.assertEqual(transaction.balance, Decimal("100.00"))
        self.assertEqual(transaction.trans_type, 1000)
        self.assertEqual(transaction.descr, "Test transaction")
    
    def test_transaction_type_choices(self):
        """Test transaction type field choices."""
        # Credit transaction
        credit_txn = WalletTransaction.objects.create(
            wtype="credit_balance",
            iid=-100,
            uid=123,
            type="c",
            amount=Decimal("100.00"),
            balance=Decimal("100.00"),
        )
        self.assertEqual(credit_txn.type, "c")
        
        # Debit transaction
        debit_txn = WalletTransaction.objects.create(
            wtype="credit_balance",
            iid=-100,
            uid=123,
            type="d",
            amount=Decimal("50.00"),
            balance=Decimal("50.00"),
        )
        self.assertEqual(debit_txn.type, "d")
    
    def test_transaction_extra_data(self):
        """Test extra_data JSONField."""
        transaction = WalletTransaction.objects.create(
            wtype="credit_balance",
            iid=-100,
            uid=123,
            type="c",
            amount=Decimal("100.00"),
            balance=Decimal("100.00"),
            extra_data={"payment_id": 12345, "source": "bank"},
        )
        
        self.assertEqual(transaction.extra_data["payment_id"], 12345)
        self.assertEqual(transaction.extra_data["source"], "bank")
    
    def test_transaction_extra_data_default(self):
        """Test extra_data default value."""
        transaction = WalletTransaction.objects.create(
            wtype="credit_balance",
            iid=-100,
            uid=123,
            type="c",
            amount=Decimal("100.00"),
            balance=Decimal("100.00"),
        )
        
        self.assertEqual(transaction.extra_data, {})
    
    def test_transaction_descr_default(self):
        """Test descr default value."""
        transaction = WalletTransaction.objects.create(
            wtype="credit_balance",
            iid=-100,
            uid=123,
            type="c",
            amount=Decimal("100.00"),
            balance=Decimal("100.00"),
        )
        
        self.assertEqual(transaction.descr, "")
    
    def test_transaction_trans_type_nullable(self):
        """Test trans_type can be None."""
        transaction = WalletTransaction.objects.create(
            wtype="credit_balance",
            iid=-100,
            uid=123,
            type="c",
            amount=Decimal("100.00"),
            balance=Decimal("100.00"),
            trans_type=None,
        )
        
        self.assertIsNone(transaction.trans_type)
    
    def test_transaction_ordering(self):
        """Test default ordering (newest first)."""
        # Create transactions with slight delays
        import time
        
        txn1 = WalletTransaction.objects.create(
            wtype="credit_balance",
            iid=-100,
            uid=123,
            type="c",
            amount=Decimal("100.00"),
            balance=Decimal("100.00"),
        )
        
        time.sleep(0.01)
        
        txn2 = WalletTransaction.objects.create(
            wtype="credit_balance",
            iid=-100,
            uid=123,
            type="c",
            amount=Decimal("200.00"),
            balance=Decimal("200.00"),
        )
        
        # Query should return newest first
        transactions = list(WalletTransaction.objects.all())
        self.assertEqual(transactions[0].id, txn2.id)
        self.assertEqual(transactions[1].id, txn1.id)
    
    def test_transaction_string_representation(self):
        """Test __str__ method."""
        transaction = WalletTransaction.objects.create(
            wtype="credit_balance",
            iid=-100,
            uid=123,
            type="c",
            amount=Decimal("100.00"),
            balance=Decimal("100.00"),
        )
        
        str_repr = str(transaction)
        self.assertIn("C", str_repr)
        self.assertIn("100.00", str_repr)
        self.assertIn("credit_balance", str_repr)
        self.assertIn("123", str_repr)
    
    def test_transaction_decimal_precision(self):
        """Test decimal precision (default 2 decimal places)."""
        transaction = WalletTransaction.objects.create(
            wtype="credit_balance",
            iid=-100,
            uid=123,
            type="c",
            amount=Decimal("100.99"),
            balance=Decimal("100.99"),
        )
        
        self.assertEqual(transaction.amount, Decimal("100.99"))
        self.assertEqual(transaction.balance, Decimal("100.99"))
    
    def test_transaction_system_user_id(self):
        """Test using system user ID (-100) for uid."""
        transaction = WalletTransaction.objects.create(
            wtype="credit_balance",
            iid=-100,
            uid=-100,  # System user as wallet owner
            type="c",
            amount=Decimal("100.00"),
            balance=Decimal("100.00"),
        )
        
        self.assertEqual(transaction.uid, -100)
        # Should not raise foreign key error since uid doesn't have FK constraint
        self.assertIsNotNone(transaction.id)
    
    def test_transaction_indexes(self):
        """Test that indexes are created (indirectly by checking query performance)."""
        # Create multiple transactions
        for i in range(10):
            WalletTransaction.objects.create(
                wtype="credit_balance",
                iid=-100,
                uid=i,
                type="c",
                amount=Decimal("100.00"),
                balance=Decimal("100.00"),
            )
        
        # Query by uid (should use index)
        transactions = WalletTransaction.objects.filter(uid=5)
        self.assertEqual(transactions.count(), 1)
        
        # Query by wtype (should use index)
        transactions = WalletTransaction.objects.filter(wtype="credit_balance")
        self.assertEqual(transactions.count(), 10)
        
        # Query by trans_type (should use index)
        transactions = WalletTransaction.objects.filter(trans_type=1000)
        self.assertEqual(transactions.count(), 0)  # None set trans_type
    
    def test_transaction_composite_indexes(self):
        """Test composite indexes."""
        # Create transactions
        for uid in [1, 2, 3]:
            for wtype in ["credit_balance", "reward_points"]:
                WalletTransaction.objects.create(
                    wtype=wtype,
                    iid=-100,
                    uid=uid,
                    type="c",
                    amount=Decimal("100.00"),
                    balance=Decimal("100.00"),
                )
        
        # Query using composite index (uid, wtype)
        transactions = WalletTransaction.objects.filter(uid=1, wtype="credit_balance")
        self.assertEqual(transactions.count(), 1)


class AbstractWalletTransactionTests(TestCase):
    """Tests for AbstractWalletTransaction model."""
    
    def test_abstract_model_not_creates_table(self):
        """Test that AbstractWalletTransaction is abstract and doesn't create a table."""
        self.assertTrue(AbstractWalletTransaction._meta.abstract)
    
    def test_custom_model_extends_abstract(self):
        """Test creating a custom model extending AbstractWalletTransaction."""
        from django.db import models
        
        class CustomTransaction(AbstractWalletTransaction):
            """Custom transaction model."""
            class Meta(AbstractWalletTransaction.Meta):
                db_table = "custom_transactions"
        
        # Verify it's not abstract
        self.assertFalse(CustomTransaction._meta.abstract)
        
        # Verify it has all fields from AbstractWalletTransaction
        self.assertTrue(hasattr(CustomTransaction, 'wtype'))
        self.assertTrue(hasattr(CustomTransaction, 'iid'))
        self.assertTrue(hasattr(CustomTransaction, 'uid'))
        self.assertTrue(hasattr(CustomTransaction, 'type'))
        self.assertTrue(hasattr(CustomTransaction, 'amount'))
        self.assertTrue(hasattr(CustomTransaction, 'balance'))
    
    def test_custom_model_custom_decimal_places(self):
        """Test custom model with different decimal places."""
        from django.db import models
        
        class CustomTransaction(AbstractWalletTransaction):
            """Custom transaction model with 8 decimal places."""
            amount = models.DecimalField(max_digits=20, decimal_places=8)
            balance = models.DecimalField(max_digits=20, decimal_places=8)
            
            class Meta(AbstractWalletTransaction.Meta):
                db_table = "custom_transactions_8dp"
        
        # Verify custom decimal places
        amount_field = CustomTransaction._meta.get_field('amount')
        self.assertEqual(amount_field.decimal_places, 8)
        
        balance_field = CustomTransaction._meta.get_field('balance')
        self.assertEqual(balance_field.decimal_places, 8)
