"""
Unit tests for utils app models

This module tests all models in the utils app:
- SystemSetting
- AuditLog
- Notification
- Document
- DocumentShare
- Receipt
- LoanStatement
"""

import pytest
from decimal import Decimal
from datetime import timedelta
from django.utils import timezone
from django.contrib.auth import get_user_model
from utils.models import (
    SystemSetting, AuditLog, Notification,
    Document, DocumentShare, Receipt, LoanStatement
)
from users.models import Branch

User = get_user_model()


@pytest.mark.django_db
class TestSystemSetting:
    """Test cases for SystemSetting model"""
    
    def test_system_setting_creation(self):
        """Test system setting is created successfully"""
        setting = SystemSetting.objects.create(
            key='biashara_interest_rate',
            value='18.0',
            category='loans',
            description='Interest rate for Biashara loans'
        )
        assert setting.id is not None
        assert setting.key == 'biashara_interest_rate'
        assert setting.value == '18.0'
    
    def test_get_string_value(self):
        """Test getting string value"""
        SystemSetting.objects.create(
            key='test_string',
            value='test_value'
        )
        assert SystemSetting.get_string('test_string') == 'test_value'
    
    def test_get_string_with_default(self):
        """Test getting string with default value"""
        assert SystemSetting.get_string('nonexistent', 'default') == 'default'
    
    def test_get_int_value(self):
        """Test getting integer value"""
        SystemSetting.objects.create(
            key='test_int',
            value='42'
        )
        assert SystemSetting.get_int('test_int') == 42
    
    def test_get_int_with_default(self):
        """Test getting integer with default value"""
        assert SystemSetting.get_int('nonexistent', 10) == 10
    
    def test_get_float_value(self):
        """Test getting float value"""
        SystemSetting.objects.create(
            key='test_float',
            value='18.5'
        )
        assert SystemSetting.get_float('test_float') == 18.5
    
    def test_get_float_with_default(self):
        """Test getting float with default value"""
        assert SystemSetting.get_float('nonexistent', 5.0) == 5.0
    
    def test_get_bool_value_true(self):
        """Test getting boolean value (true)"""
        SystemSetting.objects.create(
            key='test_bool',
            value='true'
        )
        assert SystemSetting.get_bool('test_bool') is True
    
    def test_get_bool_value_false(self):
        """Test getting boolean value (false)"""
        SystemSetting.objects.create(
            key='test_bool',
            value='false'
        )
        assert SystemSetting.get_bool('test_bool') is False
    
    def test_get_bool_with_default(self):
        """Test getting boolean with default value"""
        assert SystemSetting.get_bool('nonexistent', True) is True
    
    def test_set_value(self):
        """Test setting a value"""
        SystemSetting.set_value('new_setting', 'new_value')
        assert SystemSetting.get_string('new_setting') == 'new_value'
    
    def test_set_value_updates_existing(self):
        """Test setting value updates existing setting"""
        SystemSetting.objects.create(
            key='update_test',
            value='old_value'
        )
        SystemSetting.set_value('update_test', 'new_value')
        assert SystemSetting.get_string('update_test') == 'new_value'


@pytest.mark.django_db
class TestAuditLog:
    """Test cases for AuditLog model"""
    
    @pytest.fixture
    def branch(self):
        """Create a test branch"""
        return Branch.objects.create(
            name='Main Branch',
            code='MAIN'
        )
    
    @pytest.fixture
    def user(self, branch):
        """Create a test user"""
        return User.objects.create_user(
            username='testuser',
            phone_number='+254700000001',
            role='admin',
            branch=branch
        )
    
    def test_audit_log_creation(self, user):
        """Test audit log is created successfully"""
        log = AuditLog.objects.create(
            user=user,
            action='create',
            model_name='Loan',
            object_id='123',
            description='Created new loan'
        )
        assert log.id is not None
        assert log.user == user
        assert log.action == 'create'
    
    def test_audit_log_str_representation(self, user):
        """Test string representation"""
        log = AuditLog.objects.create(
            user=user,
            action='update',
            model_name='User',
            object_id='456'
        )
        expected = f'update by {user} on {log.created_at}'
        assert str(log) == expected
    
    def test_audit_log_with_changes(self, user):
        """Test audit log with change tracking"""
        log = AuditLog.objects.create(
            user=user,
            action='update',
            model_name='Loan',
            object_id='789',
            changes={'status': ['active', 'paid']}
        )
        assert log.changes == {'status': ['active', 'paid']}


@pytest.mark.django_db
class TestNotification:
    """Test cases for Notification model"""
    
    @pytest.fixture
    def branch(self):
        """Create a test branch"""
        return Branch.objects.create(
            name='Main Branch',
            code='MAIN'
        )
    
    @pytest.fixture
    def user(self, branch):
        """Create a test user"""
        return User.objects.create_user(
            username='testuser',
            phone_number='+254700000001',
            role='borrower',
            branch=branch
        )
    
    def test_notification_creation(self, user):
        """Test notification is created successfully"""
        notification = Notification.objects.create(
            user=user,
            notification_type='loan_approved',
            title='Loan Approved',
            message='Your loan has been approved',
            priority='high'
        )
        assert notification.id is not None
        assert notification.is_read is False
    
    def test_notification_mark_as_read(self, user):
        """Test marking notification as read"""
        notification = Notification.objects.create(
            user=user,
            notification_type='payment_received',
            title='Payment Received',
            message='We received your payment'
        )
        notification.mark_as_read()
        
        assert notification.is_read is True
        assert notification.read_at is not None
    
    def test_notification_priorities(self):
        """Test notification priorities"""
        priorities = [choice[0] for choice in Notification.PRIORITY_CHOICES]
        assert 'low' in priorities
        assert 'normal' in priorities
        assert 'high' in priorities
        assert 'urgent' in priorities


@pytest.mark.django_db
class TestDocument:
    """Test cases for Document model"""
    
    @pytest.fixture
    def branch(self):
        """Create a test branch"""
        return Branch.objects.create(
            name='Main Branch',
            code='MAIN'
        )
    
    @pytest.fixture
    def user(self, branch):
        """Create a test user"""
        return User.objects.create_user(
            username='testuser',
            phone_number='+254700000001',
            role='borrower',
            branch=branch
        )
    
    @pytest.fixture
    def uploader(self, branch):
        """Create uploader user"""
        return User.objects.create_user(
            username='uploader',
            phone_number='+254700000002',
            role='loan_officer',
            is_staff=True,
            branch=branch
        )
    
    def test_document_creation(self, user, uploader):
        """Test document is created successfully"""
        doc = Document.objects.create(
            document_type='id_document',
            title='National ID',
            description='Scanned ID document',
            file_path='/media/docs/id.pdf',
            uploaded_by=uploader,
            related_user=user
        )
        assert doc.id is not None
        assert doc.document_type == 'id_document'
        assert doc.uploaded_by == uploader
    
    def test_document_types(self):
        """Test all document types are available"""
        types = [choice[0] for choice in Document.DOCUMENT_TYPES]
        assert 'loan_agreement' in types
        assert 'id_document' in types
        assert 'bank_statement' in types


@pytest.mark.django_db
class TestDocumentShare:
    """Test cases for DocumentShare model"""
    
    @pytest.fixture
    def branch(self):
        """Create a test branch"""
        return Branch.objects.create(
            name='Main Branch',
            code='MAIN'
        )
    
    @pytest.fixture
    def user(self, branch):
        """Create a test user"""
        return User.objects.create_user(
            username='user1',
            phone_number='+254700000001',
            branch=branch
        )
    
    @pytest.fixture
    def recipient(self, branch):
        """Create recipient user"""
        return User.objects.create_user(
            username='user2',
            phone_number='+254700000002',
            branch=branch
        )
    
    @pytest.fixture
    def document(self, user):
        """Create a document"""
        return Document.objects.create(
            document_type='loan_agreement',
            title='Loan Agreement',
            file_path='/media/docs/agreement.pdf',
            uploaded_by=user,
            related_user=user
        )
    
    def test_document_share_creation(self, document, user, recipient):
        """Test document share is created successfully"""
        share = DocumentShare.objects.create(
            document=document,
            shared_by=user,
            shared_with=recipient,
            permission='view'
        )
        assert share.id is not None
        assert share.shared_with == recipient
        assert share.is_active is True
    
    def test_document_share_expiry(self, document, user, recipient):
        """Test document share with expiry"""
        expires_at = timezone.now() + timedelta(days=7)
        share = DocumentShare.objects.create(
            document=document,
            shared_by=user,
            shared_with=recipient,
            permission='view',
            expires_at=expires_at
        )
        assert share.is_expired() is False


@pytest.mark.django_db
class TestReceipt:
    """Test cases for Receipt model"""
    
    @pytest.fixture
    def branch(self):
        """Create a test branch"""
        return Branch.objects.create(
            name='Main Branch',
            code='MAIN'
        )
    
    @pytest.fixture
    def borrower(self, branch):
        """Create a borrower"""
        return User.objects.create_user(
            username='borrower',
            phone_number='+254700000001',
            role='borrower',
            branch=branch
        )
    
    @pytest.fixture
    def loan(self, borrower):
        """Create a loan"""
        from loans.models import LoanProduct, LoanApplication, Loan
        
        product = LoanProduct.objects.create(
            name='Test Product',
            product_type='biashara',
            description='Test',
            min_amount=Decimal('10000.00'),
            max_amount=Decimal('500000.00'),
            interest_rate=Decimal('18.00'),
            processing_fee=Decimal('5.00'),
            duration_months=6,
            min_duration=30,
            max_duration=180,
            available_repayment_methods=['monthly']
        )
        application = LoanApplication.objects.create(
            borrower=borrower,
            loan_product=product,
            requested_amount=Decimal('100000.00'),
            requested_duration=90,
            purpose='Test',
            status='approved'
        )
        return Loan.objects.create(
            application=application,
            borrower=borrower,
            principal_amount=Decimal('100000.00'),
            interest_amount=Decimal('18000.00'),
            processing_fee=Decimal('5000.00'),
            total_amount=Decimal('100000.00'),
            disbursement_date=timezone.now(),
            due_date=timezone.now() + timedelta(days=90),
            duration_days=90,
            status='active'
        )
    
    def test_receipt_creation(self, loan, borrower):
        """Test receipt is created successfully"""
        receipt = Receipt.objects.create(
            loan=loan,
            borrower=borrower,
            amount=Decimal('10000.00'),
            payment_method='mpesa',
            transaction_ref='ABC123'
        )
        assert receipt.id is not None
        assert receipt.receipt_number is not None
    
    def test_receipt_number_generation(self, loan, borrower):
        """Test receipt number is auto-generated"""
        receipt = Receipt.objects.create(
            loan=loan,
            borrower=borrower,
            amount=Decimal('5000.00'),
            payment_method='cash'
        )
        assert receipt.receipt_number.startswith('REC-')


@pytest.mark.django_db
class TestLoanStatement:
    """Test cases for LoanStatement model"""
    
    @pytest.fixture
    def branch(self):
        """Create a test branch"""
        return Branch.objects.create(
            name='Main Branch',
            code='MAIN'
        )
    
    @pytest.fixture
    def borrower(self, branch):
        """Create a borrower"""
        return User.objects.create_user(
            username='borrower',
            phone_number='+254700000001',
            role='borrower',
            branch=branch
        )
    
    @pytest.fixture
    def loan(self, borrower):
        """Create a loan"""
        from loans.models import LoanProduct, LoanApplication, Loan
        
        product = LoanProduct.objects.create(
            name='Test Product',
            product_type='biashara',
            description='Test',
            min_amount=Decimal('10000.00'),
            max_amount=Decimal('500000.00'),
            interest_rate=Decimal('18.00'),
            processing_fee=Decimal('5.00'),
            duration_months=6,
            min_duration=30,
            max_duration=180,
            available_repayment_methods=['monthly']
        )
        application = LoanApplication.objects.create(
            borrower=borrower,
            loan_product=product,
            requested_amount=Decimal('100000.00'),
            requested_duration=90,
            purpose='Test',
            status='approved'
        )
        return Loan.objects.create(
            application=application,
            borrower=borrower,
            principal_amount=Decimal('100000.00'),
            interest_amount=Decimal('18000.00'),
            processing_fee=Decimal('5000.00'),
            total_amount=Decimal('100000.00'),
            disbursement_date=timezone.now(),
            due_date=timezone.now() + timedelta(days=90),
            duration_days=90,
            status='active'
        )
    
    def test_loan_statement_creation(self, loan, borrower):
        """Test loan statement is created successfully"""
        statement = LoanStatement.objects.create(
            loan=loan,
            borrower=borrower,
            statement_date=timezone.now(),
            generated_by=borrower
        )
        assert statement.id is not None
        assert statement.loan == loan
