"""
Unit tests for loan editing functionality.

Feature: comprehensive-reports-and-fixes
Tests specific examples and edge cases for loan editing.
"""

from django.test import TestCase, Client
from django.urls import reverse
from decimal import Decimal
from django.contrib.auth import get_user_model
from django.utils import timezone
from datetime import timedelta
from loans.models import Loan, LoanApplication, LoanProduct
from utils.models import AuditLog
from users.models import Branch

User = get_user_model()


class LoanEditUnitTests(TestCase):
    """Unit tests for loan editing functionality"""
    
    def setUp(self):
        """Set up test data"""
        # Create a branch
        self.branch = Branch.objects.create(
            name="Test Branch",
            code="TB001",
            address="Test Address"
        )
        
        # Create admin user
        self.admin_user = User.objects.create_user(
            username='admin@test.com',
            email='admin@test.com',
            password='testpass123',
            first_name='Admin',
            last_name='User',
            role='admin',
            branch=self.branch,
            phone_number='+254700000001'
        )
        
        # Create non-admin user
        self.staff_user = User.objects.create_user(
            username='staff@test.com',
            email='staff@test.com',
            password='testpass123',
            first_name='Staff',
            last_name='User',
            role='staff',
            branch=self.branch,
            phone_number='+254700000002'
        )
        
        # Create borrower
        self.borrower = User.objects.create_user(
            username='borrower@test.com',
            email='borrower@test.com',
            password='testpass123',
            first_name='Test',
            last_name='Borrower',
            role='borrower',
            branch=self.branch,
            phone_number='+254700000003'
        )
        
        # Create loan product
        self.loan_product = LoanProduct.objects.create(
            name="Test Product",
            product_type="boost",
            description="Test product",
            min_amount=Decimal('1000.00'),
            max_amount=Decimal('100000.00'),
            interest_rate=Decimal('10.00'),
            processing_fee=Decimal('5.00'),
            min_duration=7,
            max_duration=365,
            available_repayment_methods=['monthly']
        )
        
        # Create a loan application
        self.application = LoanApplication.objects.create(
            application_number="APP-000001",
            borrower=self.borrower,
            loan_product=self.loan_product,
            requested_amount=Decimal('10000.00'),
            requested_duration=30,
            purpose="Test loan",
            interest_amount=Decimal('1000.00'),
            processing_fee_amount=Decimal('500.00'),
            total_amount=Decimal('11500.00'),
            status='approved'
        )
        
        # Create a loan
        self.loan = Loan.objects.create(
            loan_number="LOAN-000001",
            application=self.application,
            borrower=self.borrower,
            principal_amount=Decimal('10000.00'),
            interest_amount=Decimal('1000.00'),
            processing_fee=Decimal('500.00'),
            total_amount=Decimal('11500.00'),
            disbursement_date=timezone.now(),
            due_date=timezone.now() + timedelta(days=30),
            duration_days=30,
            status='active'
        )
        
        self.client = Client()
    
    def test_admin_user_can_access_edit_form(self):
        """Test that admin user can access the loan edit form"""
        self.client.login(username='admin@test.com', password='testpass123')
        url = reverse('loans:edit_loan', kwargs={'pk': self.loan.pk})
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'Edit Loan')
        self.assertContains(response, self.loan.loan_number)
    
    def test_non_admin_user_cannot_access_edit_form(self):
        """Test that non-admin user cannot access the loan edit form"""
        self.client.login(username='staff@test.com', password='testpass123')
        url = reverse('loans:edit_loan', kwargs={'pk': self.loan.pk})
        response = self.client.get(url)
        
        # Should redirect to loans list
        self.assertEqual(response.status_code, 302)
    
    def test_active_to_paid_transition(self):
        """Test allowed status transition from active to paid"""
        # First add sufficient repayments
        from loans.models import Repayment
        Repayment.objects.create(
            loan=self.loan,
            amount=self.loan.total_amount,
            payment_method='cash',
            receipt_number='RCP-TEST001',
            payment_date=timezone.now()
        )
        
        self.loan.refresh_from_db()
        
        self.client.login(username='admin@test.com', password='testpass123')
        url = reverse('loans:edit_loan', kwargs={'pk': self.loan.pk})
        
        response = self.client.post(url, {
            'principal_amount': str(self.loan.principal_amount),
            'interest_amount': str(self.loan.interest_amount),
            'processing_fee': str(self.loan.processing_fee),
            'duration_days': self.loan.duration_days,
            'status': 'paid'
        })
        
        self.loan.refresh_from_db()
        self.assertEqual(self.loan.status, 'paid')
    
    def test_active_to_defaulted_transition(self):
        """Test allowed status transition from active to defaulted"""
        self.client.login(username='admin@test.com', password='testpass123')
        url = reverse('loans:edit_loan', kwargs={'pk': self.loan.pk})
        
        response = self.client.post(url, {
            'principal_amount': str(self.loan.principal_amount),
            'interest_amount': str(self.loan.interest_amount),
            'processing_fee': str(self.loan.processing_fee),
            'duration_days': self.loan.duration_days,
            'status': 'defaulted'
        })
        
        self.loan.refresh_from_db()
        self.assertEqual(self.loan.status, 'defaulted')
    
    def test_paid_to_active_transition_allowed(self):
        """Test allowed status transition from paid to active (e.g., payment reversal)"""
        # Set loan to paid status
        self.loan.status = 'paid'
        self.loan.save()
        
        self.client.login(username='admin@test.com', password='testpass123')
        url = reverse('loans:edit_loan', kwargs={'pk': self.loan.pk})
        
        response = self.client.post(url, {
            'principal_amount': str(self.loan.principal_amount),
            'interest_amount': str(self.loan.interest_amount),
            'processing_fee': str(self.loan.processing_fee),
            'duration_days': self.loan.duration_days,
            'disbursement_date': self.loan.disbursement_date.strftime('%Y-%m-%d'),
            'due_date': self.loan.due_date.strftime('%Y-%m-%d'),
            'status': 'active'
        })
        
        self.loan.refresh_from_db()
        # Status should change to active
        self.assertEqual(self.loan.status, 'active')
    
    def test_defaulted_to_written_off_transition(self):
        """Test allowed status transition from defaulted to written_off"""
        # Set loan to defaulted status
        self.loan.status = 'defaulted'
        self.loan.save()
        
        self.client.login(username='admin@test.com', password='testpass123')
        url = reverse('loans:edit_loan', kwargs={'pk': self.loan.pk})
        
        response = self.client.post(url, {
            'principal_amount': str(self.loan.principal_amount),
            'interest_amount': str(self.loan.interest_amount),
            'processing_fee': str(self.loan.processing_fee),
            'duration_days': self.loan.duration_days,
            'status': 'written_off'
        })
        
        self.loan.refresh_from_db()
        self.assertEqual(self.loan.status, 'written_off')
    
    def test_amount_recalculation_after_edits(self):
        """Test that total_amount is recalculated after editing amounts"""
        self.client.login(username='admin@test.com', password='testpass123')
        url = reverse('loans:edit_loan', kwargs={'pk': self.loan.pk})
        
        new_principal = Decimal('15000.00')
        new_interest = Decimal('1500.00')
        new_processing_fee = Decimal('750.00')
        
        response = self.client.post(url, {
            'principal_amount': str(new_principal),
            'interest_amount': str(new_interest),
            'processing_fee': str(new_processing_fee),
            'duration_days': self.loan.duration_days,
            'status': self.loan.status
        })
        
        self.loan.refresh_from_db()
        expected_total = new_principal + new_interest + new_processing_fee
        
        self.assertEqual(self.loan.principal_amount, new_principal)
        self.assertEqual(self.loan.interest_amount, new_interest)
        self.assertEqual(self.loan.processing_fee, new_processing_fee)
        self.assertEqual(self.loan.total_amount, expected_total)
    
    def test_validation_error_messages(self):
        """Test that validation errors are displayed correctly"""
        self.client.login(username='admin@test.com', password='testpass123')
        url = reverse('loans:edit_loan', kwargs={'pk': self.loan.pk})
        
        # Try to set zero principal amount
        response = self.client.post(url, {
            'principal_amount': '0.00',
            'interest_amount': str(self.loan.interest_amount),
            'processing_fee': str(self.loan.processing_fee),
            'duration_days': self.loan.duration_days,
            'status': self.loan.status
        })
        
        # Should show error message (check for any error indication)
        # The validation happens and returns to the form
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'Edit Loan')
    
    def test_audit_log_created_on_edit(self):
        """Test that audit log is created when loan is edited"""
        initial_audit_count = AuditLog.objects.filter(
            model_name='Loan',
            object_id=str(self.loan.pk)
        ).count()
        
        self.client.login(username='admin@test.com', password='testpass123')
        url = reverse('loans:edit_loan', kwargs={'pk': self.loan.pk})
        
        response = self.client.post(url, {
            'principal_amount': '12000.00',
            'interest_amount': str(self.loan.interest_amount),
            'processing_fee': str(self.loan.processing_fee),
            'duration_days': self.loan.duration_days,
            'status': self.loan.status
        })
        
        final_audit_count = AuditLog.objects.filter(
            model_name='Loan',
            object_id=str(self.loan.pk)
        ).count()
        
        self.assertGreater(final_audit_count, initial_audit_count)
        
        # Check audit log contains loan number
        latest_audit = AuditLog.objects.filter(
            model_name='Loan',
            object_id=str(self.loan.pk)
        ).latest('created_at')
        
        self.assertIn(self.loan.loan_number, latest_audit.description)
        self.assertEqual(latest_audit.user, self.admin_user)
