"""
Test Reports Access Control
Verify that staff can only access data from their branch and portfolio
"""
import os
import django
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'branch_system.settings')
django.setup()

from django.utils import timezone
from users.models import CustomUser, Branch
from loans.models import Loan, LoanApplication, LoanProduct
from reports.simple_reports_service import SimpleReportsService
from decimal import Decimal
from datetime import datetime, timedelta

class AccessControlTest:
    def __init__(self):
        self.service = SimpleReportsService()
        self.branch1 = None
        self.branch2 = None
        self.loan_officer1 = None
        self.loan_officer2 = None
        self.admin = None
        
    def setup_test_data(self):
        """Create test data with multiple branches and loan officers"""
        print("\n" + "="*80)
        print("SETTING UP ACCESS CONTROL TEST DATA")
        print("="*80)
        
        # Create two branches
        self.branch1, _ = Branch.objects.get_or_create(
            name="Branch 1",
            defaults={'code': 'BR001', 'is_active': True}
        )
        self.branch2, _ = Branch.objects.get_or_create(
            name="Branch 2",
            defaults={'code': 'BR002', 'is_active': True}
        )
        print(f"Created branches: {self.branch1.name}, {self.branch2.name}")
        
        # Create loan officers
        self.loan_officer1, _ = CustomUser.objects.get_or_create(
            username='loan_officer1',
            defaults={
                'first_name': 'Officer',
                'last_name': 'One',
                'phone_number': '0711111111',
                'email': 'officer1@test.com',
                'role': 'loan_officer',
                'status': 'active',
                'branch': self.branch1
            }
        )
        
        self.loan_officer2, _ = CustomUser.objects.get_or_create(
            username='loan_officer2',
            defaults={
                'first_name': 'Officer',
                'last_name': 'Two',
                'phone_number': '0722222222',
                'email': 'officer2@test.com',
                'role': 'loan_officer',
                'status': 'active',
                'branch': self.branch2
            }
        )
        
        self.admin, _ = CustomUser.objects.get_or_create(
            username='admin_test',
            defaults={
                'first_name': 'Admin',
                'last_name': 'User',
                'phone_number': '0733333333',
                'email': 'admin@test.com',
                'role': 'admin',
                'status': 'active',
                'is_superuser': True
            }
        )
        print(f"Created users: {self.loan_officer1.username}, {self.loan_officer2.username}, {self.admin.username}")

        
        # Create borrowers for each branch and assign to loan officers
        self.borrowers_branch1 = []
        self.borrowers_branch2 = []
        
        # Branch 1 borrowers (5 assigned to officer1, 5 unassigned)
        for i in range(10):
            borrower, _ = CustomUser.objects.get_or_create(
                username=f'borrower_b1_{i}',
                defaults={
                    'first_name': f'Borrower',
                    'last_name': f'B1_{i}',
                    'phone_number': f'07111111{i:02d}',
                    'email': f'borrower_b1_{i}@test.com',
                    'role': 'borrower',
                    'status': 'active',
                    'branch': self.branch1,
                    'portfolio_manager': self.loan_officer1 if i < 5 else None
                }
            )
            self.borrowers_branch1.append(borrower)
        
        # Branch 2 borrowers (5 assigned to officer2, 5 unassigned)
        for i in range(10):
            borrower, _ = CustomUser.objects.get_or_create(
                username=f'borrower_b2_{i}',
                defaults={
                    'first_name': f'Borrower',
                    'last_name': f'B2_{i}',
                    'phone_number': f'07222222{i:02d}',
                    'email': f'borrower_b2_{i}@test.com',
                    'role': 'borrower',
                    'status': 'active',
                    'branch': self.branch2,
                    'portfolio_manager': self.loan_officer2 if i < 5 else None
                }
            )
            self.borrowers_branch2.append(borrower)
        
        print(f"Created 10 borrowers for Branch 1 (5 assigned to officer1)")
        print(f"Created 10 borrowers for Branch 2 (5 assigned to officer2)")
        
        # Create loan product
        product, _ = LoanProduct.objects.get_or_create(
            name='Test Product',
            defaults={
                'product_type': 'boost',
                'description': 'Test product',
                'interest_rate': 20,
                'processing_fee': 2.5,
                'min_amount': 1000,
                'max_amount': 500000,
                'min_duration': 7,
                'max_duration': 30,
                'duration_months': 1,
                'available_repayment_methods': ['daily'],
                'is_active': True
            }
        )
        
        # Create loans for each borrower
        self.loans_branch1 = []
        self.loans_branch2 = []
        
        today = timezone.now().date()
        
        for borrower in self.borrowers_branch1:
            loan = self.create_test_loan(borrower, product, 10000, today)
            self.loans_branch1.append(loan)
        
        for borrower in self.borrowers_branch2:
            loan = self.create_test_loan(borrower, product, 15000, today)
            self.loans_branch2.append(loan)
        
        print(f"Created {len(self.loans_branch1)} loans for Branch 1")
        print(f"Created {len(self.loans_branch2)} loans for Branch 2")
        print("="*80)
    
    def create_test_loan(self, borrower, product, principal, today):
        """Helper to create a test loan"""
        import random
        
        # Create application
        application, _ = LoanApplication.objects.get_or_create(
            borrower=borrower,
            loan_product=product,
            requested_amount=principal,
            defaults={
                'status': 'approved',
                'repayment_method': 'daily',
                'requested_duration': 30
            }
        )
        
        # Calculate amounts
        interest_amount = principal * Decimal('0.20')
        processing_fee = principal * Decimal('0.025')
        total_amount = principal + interest_amount + processing_fee
        
        # Create loan
        loan, _ = Loan.objects.get_or_create(
            borrower=borrower,
            application=application,
            defaults={
                'loan_number': f'TST{random.randint(100000, 999999)}',
                'principal_amount': principal,
                'interest_amount': interest_amount,
                'processing_fee': processing_fee,
                'total_amount': total_amount,
                'amount_paid': Decimal('0.00'),
                'disbursement_date': datetime.combine(today - timedelta(days=15), datetime.min.time()),
                'due_date': datetime.combine(today, datetime.min.time()),
                'duration_days': 30,
                'status': 'active',
                'is_deleted': False
            }
        )
        
        return loan

    
    def test_portfolio_filtering(self):
        """Test that loan officers only see their portfolio"""
        print("\n" + "="*80)
        print("TEST 1: PORTFOLIO FILTERING")
        print("="*80)
        
        # Test loan officer 1 - should only see their 5 assigned clients
        metrics_officer1 = self.service.get_summary_metrics(
            portfolio_manager_id=self.loan_officer1.id
        )
        
        print(f"\nLoan Officer 1 (Portfolio Manager):")
        print(f"  Total Active Loans: {metrics_officer1['total_active_loans']}")
        print(f"  Expected: 5 (only their assigned clients)")
        print(f"  Result: {'PASS' if metrics_officer1['total_active_loans'] == 5 else 'FAIL'}")
        
        # Test loan officer 2 - should only see their 5 assigned clients
        metrics_officer2 = self.service.get_summary_metrics(
            portfolio_manager_id=self.loan_officer2.id
        )
        
        print(f"\nLoan Officer 2 (Portfolio Manager):")
        print(f"  Total Active Loans: {metrics_officer2['total_active_loans']}")
        print(f"  Expected: 5 (only their assigned clients)")
        print(f"  Result: {'PASS' if metrics_officer2['total_active_loans'] == 5 else 'FAIL'}")
        
        # Verify they don't see each other's loans
        if metrics_officer1['total_active_loans'] == 5 and metrics_officer2['total_active_loans'] == 5:
            print(f"\n✓ PORTFOLIO FILTERING WORKING: Each officer sees only their assigned clients")
        else:
            print(f"\n✗ PORTFOLIO FILTERING FAILED: Officers seeing incorrect data")
    
    def test_branch_filtering(self):
        """Test that branch filtering works correctly"""
        print("\n" + "="*80)
        print("TEST 2: BRANCH FILTERING")
        print("="*80)
        
        # Test branch 1 - should see all 10 loans from branch 1
        metrics_branch1 = self.service.get_summary_metrics(
            branch_id=self.branch1.id
        )
        
        print(f"\nBranch 1 Filtering:")
        print(f"  Total Active Loans: {metrics_branch1['total_active_loans']}")
        print(f"  Expected: 10 (all loans in branch 1)")
        print(f"  Result: {'PASS' if metrics_branch1['total_active_loans'] == 10 else 'FAIL'}")
        
        # Test branch 2 - should see all 10 loans from branch 2
        metrics_branch2 = self.service.get_summary_metrics(
            branch_id=self.branch2.id
        )
        
        print(f"\nBranch 2 Filtering:")
        print(f"  Total Active Loans: {metrics_branch2['total_active_loans']}")
        print(f"  Expected: 10 (all loans in branch 2)")
        print(f"  Result: {'PASS' if metrics_branch2['total_active_loans'] == 10 else 'FAIL'}")
        
        # Verify portfolio values are different
        print(f"\nBranch 1 Portfolio: KES {metrics_branch1['total_portfolio_value']:,.2f}")
        print(f"Branch 2 Portfolio: KES {metrics_branch2['total_portfolio_value']:,.2f}")
        
        if metrics_branch1['total_active_loans'] == 10 and metrics_branch2['total_active_loans'] == 10:
            print(f"\n✓ BRANCH FILTERING WORKING: Each branch sees only their loans")
        else:
            print(f"\n✗ BRANCH FILTERING FAILED: Branches seeing incorrect data")
    
    def test_admin_access(self):
        """Test that admin sees all data"""
        print("\n" + "="*80)
        print("TEST 3: ADMIN ACCESS (NO FILTERING)")
        print("="*80)
        
        # Admin should see all loans (no filtering)
        metrics_admin = self.service.get_summary_metrics()
        
        print(f"\nAdmin (No Filtering):")
        print(f"  Total Active Loans: {metrics_admin['total_active_loans']}")
        print(f"  Expected: 20 (all loans from both branches)")
        print(f"  Result: {'PASS' if metrics_admin['total_active_loans'] == 20 else 'FAIL'}")
        
        if metrics_admin['total_active_loans'] == 20:
            print(f"\n✓ ADMIN ACCESS WORKING: Admin sees all data")
        else:
            print(f"\n✗ ADMIN ACCESS FAILED: Admin not seeing all data")
    
    def test_all_report_types(self):
        """Test access control across all report types"""
        print("\n" + "="*80)
        print("TEST 4: ACCESS CONTROL ACROSS ALL REPORT TYPES")
        print("="*80)
        
        # Test each report type for loan officer 1
        print(f"\nTesting all reports for Loan Officer 1 (Portfolio: 5 loans):")
        
        # 1. Loans Due Today
        due_today = self.service.get_loans_due_today(portfolio_manager_id=self.loan_officer1.id)
        print(f"  1. Loans Due Today: {due_today['summary']['total_loans_due']} (Expected: ≤5)")
        
        # 2. Delinquent Loans
        delinquent = self.service.get_delinquent_loans(portfolio_manager_id=self.loan_officer1.id)
        print(f"  2. Delinquent Loans: {delinquent['summary']['total_delinquent']} (Expected: ≤5)")
        
        # 3. Processing Fees
        fees = self.service.get_processing_fees_current_month(portfolio_manager_id=self.loan_officer1.id)
        print(f"  3. Processing Fees: {fees['summary']['total_loans_processed']} loans (Expected: ≤5)")
        
        # 4. Interest Income
        interest = self.service.get_interest_income_current_month(portfolio_manager_id=self.loan_officer1.id)
        print(f"  4. Interest Income: {interest['summary']['total_loans']} loans (Expected: ≤5)")
        
        # 5. Completed Loans
        completed = self.service.get_completed_loans_analytics(portfolio_manager_id=self.loan_officer1.id)
        print(f"  5. Completed Loans: {completed['summary']['total_completed_loans']} (Expected: 0)")
        
        # 6. Overdue Loans
        overdue = self.service.get_overdue_loans_analytics(portfolio_manager_id=self.loan_officer1.id)
        print(f"  6. Overdue Loans: {overdue['summary']['total_overdue_loans']} (Expected: ≤5)")
        
        # 7. Client Growth
        clients = self.service.get_client_growth_analytics(portfolio_manager_id=self.loan_officer1.id)
        print(f"  7. Client Growth: {clients['summary']['total_clients']} clients (Expected: 5)")
        
        # Verify all counts are ≤ 5 (their portfolio size)
        all_correct = (
            due_today['summary']['total_loans_due'] <= 5 and
            delinquent['summary']['total_delinquent'] <= 5 and
            fees['summary']['total_loans_processed'] <= 5 and
            interest['summary']['total_loans'] <= 5 and
            overdue['summary']['total_overdue_loans'] <= 5 and
            clients['summary']['total_clients'] == 5
        )
        
        if all_correct:
            print(f"\n✓ ALL REPORT TYPES RESPECTING PORTFOLIO FILTERING")
        else:
            print(f"\n✗ SOME REPORT TYPES NOT RESPECTING PORTFOLIO FILTERING")
    
    def test_cross_branch_isolation(self):
        """Test that officers cannot see data from other branches"""
        print("\n" + "="*80)
        print("TEST 5: CROSS-BRANCH ISOLATION")
        print("="*80)
        
        # Get loans due for officer 1
        due_officer1 = self.service.get_loans_due_today(portfolio_manager_id=self.loan_officer1.id)
        
        # Check that none of the loans belong to branch 2
        branch2_loan_found = False
        for loan_data in due_officer1['loans']:
            # Check if any loan belongs to a branch 2 borrower
            loan_id = loan_data['id']
            loan = Loan.objects.get(id=loan_id)
            if loan.borrower.branch == self.branch2:
                branch2_loan_found = True
                break
        
        print(f"\nLoan Officer 1 (Branch 1):")
        print(f"  Can see Branch 2 loans: {branch2_loan_found}")
        print(f"  Result: {'FAIL - SECURITY ISSUE!' if branch2_loan_found else 'PASS - Isolated'}")
        
        # Get loans due for officer 2
        due_officer2 = self.service.get_loans_due_today(portfolio_manager_id=self.loan_officer2.id)
        
        # Check that none of the loans belong to branch 1
        branch1_loan_found = False
        for loan_data in due_officer2['loans']:
            loan_id = loan_data['id']
            loan = Loan.objects.get(id=loan_id)
            if loan.borrower.branch == self.branch1:
                branch1_loan_found = True
                break
        
        print(f"\nLoan Officer 2 (Branch 2):")
        print(f"  Can see Branch 1 loans: {branch1_loan_found}")
        print(f"  Result: {'FAIL - SECURITY ISSUE!' if branch1_loan_found else 'PASS - Isolated'}")
        
        if not branch2_loan_found and not branch1_loan_found:
            print(f"\n✓ CROSS-BRANCH ISOLATION WORKING: Officers cannot see other branches")
        else:
            print(f"\n✗ CROSS-BRANCH ISOLATION FAILED: SECURITY ISSUE DETECTED!")
    
    def run_all_tests(self):
        """Run all access control tests"""
        print("\n" + "="*80)
        print("REPORTS ACCESS CONTROL TEST SUITE")
        print("="*80)
        print("Testing that staff can only access their branch and portfolio data")
        
        try:
            self.setup_test_data()
            self.test_portfolio_filtering()
            self.test_branch_filtering()
            self.test_admin_access()
            self.test_all_report_types()
            self.test_cross_branch_isolation()
            
            print("\n" + "="*80)
            print("TEST SUMMARY")
            print("="*80)
            print("✓ All access control tests completed")
            print("✓ Portfolio filtering verified")
            print("✓ Branch filtering verified")
            print("✓ Cross-branch isolation verified")
            print("="*80)
            
        except Exception as e:
            print(f"\n✗ ERROR: {str(e)}")
            import traceback
            traceback.print_exc()

if __name__ == '__main__':
    test = AccessControlTest()
    test.run_all_tests()
