"""
Unit Tests for Report Export Functionality

This module contains unit tests for the report export service,
testing specific examples, edge cases, and error conditions.

Feature: comprehensive-reports-and-fixes
"""

from django.test import TestCase
from django.http import HttpResponse
from decimal import Decimal
from datetime import date, timedelta
from io import BytesIO
import openpyxl
from PyPDF2 import PdfReader

from reports.export_service import ReportExportService


class ExportServiceUnitTests(TestCase):
    """
    Unit tests for ReportExportService.
    
    Tests specific examples, edge cases, and error conditions.
    Validates: Requirements 8.7, 8.8
    """
    
    def setUp(self):
        """Set up test service"""
        self.export_service = ReportExportService()
    
    def test_pdf_generation_produces_valid_pdf(self):
        """
        Test that PDF generation produces valid PDF files.
        
        Validates: Requirement 8.7
        """
        # Create test data
        report_data = {
            'loans': [
                {
                    'loan_number': 'LN0001',
                    'borrower_name': 'John Doe',
                    'phone_number': '0700000001',
                    'disbursement_date': date.today(),
                    'principal_amount': Decimal('10000.00'),
                    'interest_amount': Decimal('1000.00'),
                    'processing_fee': Decimal('500.00'),
                    'total_amount': Decimal('11500.00'),
                    'amount_paid': Decimal('0.00'),
                    'outstanding_amount': Decimal('11500.00'),
                }
            ]
        }
        filters = {'period': 'today'}
        
        # Generate PDF
        response = self.export_service.export_to_pdf(
            report_data, 'disbursed_loans', filters
        )
        
        # Verify response
        self.assertIsInstance(response, HttpResponse)
        self.assertEqual(response['Content-Type'], 'application/pdf')
        self.assertIn('attachment', response['Content-Disposition'])
        self.assertIn('.pdf', response['Content-Disposition'])
        
        # Verify PDF is valid
        pdf_content = BytesIO(response.content)
        pdf_reader = PdfReader(pdf_content)
        self.assertGreater(len(pdf_reader.pages), 0)
    
    def test_excel_generation_produces_valid_excel(self):
        """
        Test that Excel generation produces valid Excel files.
        
        Validates: Requirement 8.8
        """
        # Create test data
        report_data = {
            'loans': [
                {
                    'loan_number': 'LN0001',
                    'borrower_name': 'John Doe',
                    'phone_number': '0700000001',
                    'disbursement_date': date.today(),
                    'principal_amount': Decimal('10000.00'),
                    'interest_amount': Decimal('1000.00'),
                    'processing_fee': Decimal('500.00'),
                    'total_amount': Decimal('11500.00'),
                    'amount_paid': Decimal('0.00'),
                    'outstanding_amount': Decimal('11500.00'),
                }
            ]
        }
        filters = {'period': 'today'}
        
        # Generate Excel
        response = self.export_service.export_to_excel(
            report_data, 'disbursed_loans', filters
        )
        
        # Verify response
        self.assertIsInstance(response, HttpResponse)
        self.assertEqual(
            response['Content-Type'],
            'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
        )
        self.assertIn('attachment', response['Content-Disposition'])
        self.assertIn('.xlsx', response['Content-Disposition'])
        
        # Verify Excel is valid
        excel_content = BytesIO(response.content)
        workbook = openpyxl.load_workbook(excel_content)
        self.assertIsNotNone(workbook.active)
    
    def test_export_includes_all_filtered_data(self):
        """
        Test that export includes all filtered data.
        
        Validates: Requirement 8.7, 8.8
        """
        # Create test data with multiple loans
        loans_data = []
        for i in range(5):
            loans_data.append({
                'loan_number': f'LN{i:04d}',
                'borrower_name': f'Borrower {i}',
                'phone_number': f'07000000{i:02d}',
                'disbursement_date': date.today() - timedelta(days=i),
                'principal_amount': Decimal('10000.00'),
                'interest_amount': Decimal('1000.00'),
                'processing_fee': Decimal('500.00'),
                'total_amount': Decimal('11500.00'),
                'amount_paid': Decimal('0.00'),
                'outstanding_amount': Decimal('11500.00'),
            })
        
        report_data = {'loans': loans_data}
        filters = {'period': 'total'}
        
        # Test PDF export
        pdf_response = self.export_service.export_to_pdf(
            report_data, 'disbursed_loans', filters
        )
        pdf_content = BytesIO(pdf_response.content)
        pdf_reader = PdfReader(pdf_content)
        pdf_text = ''
        for page in pdf_reader.pages:
            pdf_text += page.extract_text()
        
        # Verify all loan numbers appear in PDF
        for loan in loans_data:
            self.assertIn(loan['loan_number'], pdf_text)
        
        # Test Excel export
        excel_response = self.export_service.export_to_excel(
            report_data, 'disbursed_loans', filters
        )
        excel_content = BytesIO(excel_response.content)
        workbook = openpyxl.load_workbook(excel_content)
        sheet = workbook.active
        
        # Extract all text from Excel
        excel_text = ''
        for row in sheet.iter_rows():
            for cell in row:
                if cell.value:
                    excel_text += str(cell.value) + ' '
        
        # Verify all loan numbers appear in Excel
        for loan in loans_data:
            self.assertIn(loan['loan_number'], excel_text)
    
    def test_export_formatting_currency(self):
        """
        Test that export formats currency correctly.
        
        Validates: Requirement 8.7, 8.8
        """
        # Test various currency amounts
        test_amounts = [
            Decimal('0.00'),
            Decimal('100.50'),
            Decimal('1000.00'),
            Decimal('10000.99'),
            Decimal('100000.00'),
            Decimal('1000000.50'),
        ]
        
        for amount in test_amounts:
            formatted = self.export_service.format_currency(amount)
            
            # Should have KES prefix
            self.assertIn('KES', formatted)
            
            # Should have exactly 2 decimal places
            self.assertRegex(formatted, r'KES\s+[\d,]+\.\d{2}$')
            
            # Should have thousand separators for large amounts
            if amount >= 1000:
                self.assertIn(',', formatted)
    
    def test_export_formatting_dates(self):
        """
        Test that export formats dates correctly.
        
        Validates: Requirement 8.7, 8.8
        """
        # Test various dates
        test_dates = [
            date(2024, 1, 1),
            date(2024, 12, 31),
            date.today(),
            date.today() - timedelta(days=365),
        ]
        
        for test_date in test_dates:
            formatted = self.export_service.format_date(test_date)
            
            # Should match YYYY-MM-DD format
            self.assertRegex(formatted, r'\d{4}-\d{2}-\d{2}')
            
            # Should be parseable back to a date
            from datetime import datetime
            parsed_date = datetime.strptime(formatted, '%Y-%m-%d').date()
            self.assertEqual(parsed_date, test_date)
    
    def test_export_with_large_dataset(self):
        """
        Test that export handles large datasets correctly.
        
        Validates: Requirement 8.7, 8.8
        """
        # Create large dataset (100 loans)
        loans_data = []
        for i in range(100):
            loans_data.append({
                'loan_number': f'LN{i:04d}',
                'borrower_name': f'Borrower {i}',
                'phone_number': f'07000000{i:02d}',
                'disbursement_date': date.today() - timedelta(days=i % 30),
                'principal_amount': Decimal('10000.00') + Decimal(i),
                'interest_amount': Decimal('1000.00'),
                'processing_fee': Decimal('500.00'),
                'total_amount': Decimal('11500.00') + Decimal(i),
                'amount_paid': Decimal('0.00'),
                'outstanding_amount': Decimal('11500.00') + Decimal(i),
            })
        
        report_data = {'loans': loans_data}
        filters = {'period': 'total'}
        
        # Test PDF export with large dataset
        pdf_response = self.export_service.export_to_pdf(
            report_data, 'disbursed_loans', filters
        )
        self.assertIsInstance(pdf_response, HttpResponse)
        self.assertEqual(pdf_response['Content-Type'], 'application/pdf')
        
        # Test Excel export with large dataset
        excel_response = self.export_service.export_to_excel(
            report_data, 'disbursed_loans', filters
        )
        self.assertIsInstance(excel_response, HttpResponse)
        self.assertEqual(
            excel_response['Content-Type'],
            'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
        )
    
    def test_export_error_handling_empty_data(self):
        """
        Test that export handles empty data gracefully.
        
        Validates: Requirement 8.7, 8.8
        """
        # Test with empty loans list
        report_data = {'loans': []}
        filters = {'period': 'total'}
        
        # PDF should still generate
        pdf_response = self.export_service.export_to_pdf(
            report_data, 'disbursed_loans', filters
        )
        self.assertIsInstance(pdf_response, HttpResponse)
        self.assertEqual(pdf_response['Content-Type'], 'application/pdf')
        
        # Excel should still generate
        excel_response = self.export_service.export_to_excel(
            report_data, 'disbursed_loans', filters
        )
        self.assertIsInstance(excel_response, HttpResponse)
        self.assertEqual(
            excel_response['Content-Type'],
            'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
        )
    
    def test_export_error_handling_missing_loans_key(self):
        """
        Test that export handles missing 'loans' key gracefully.
        
        Validates: Requirement 8.7, 8.8
        """
        # Test with missing 'loans' key
        report_data = {}
        filters = {'period': 'total'}
        
        # PDF should still generate (with warning logged)
        pdf_response = self.export_service.export_to_pdf(
            report_data, 'disbursed_loans', filters
        )
        self.assertIsInstance(pdf_response, HttpResponse)
        
        # Excel should still generate (with warning logged)
        excel_response = self.export_service.export_to_excel(
            report_data, 'disbursed_loans', filters
        )
        self.assertIsInstance(excel_response, HttpResponse)
    
    def test_export_error_handling_none_data(self):
        """
        Test that export handles None data gracefully.
        
        Validates: Requirement 8.7, 8.8
        """
        # Test with None report_data
        report_data = None
        filters = {'period': 'total'}
        
        # PDF should still generate (with warning logged)
        pdf_response = self.export_service.export_to_pdf(
            report_data, 'disbursed_loans', filters
        )
        self.assertIsInstance(pdf_response, HttpResponse)
        
        # Excel should still generate (with warning logged)
        excel_response = self.export_service.export_to_excel(
            report_data, 'disbursed_loans', filters
        )
        self.assertIsInstance(excel_response, HttpResponse)
    
    def test_currency_formatting_edge_cases(self):
        """
        Test currency formatting with edge cases.
        
        Validates: Requirement 8.7, 8.8
        """
        # Test None
        formatted = self.export_service.format_currency(None)
        self.assertEqual(formatted, 'KES 0.00')
        
        # Test zero
        formatted = self.export_service.format_currency(Decimal('0.00'))
        self.assertEqual(formatted, 'KES 0.00')
        
        # Test negative (should still format)
        formatted = self.export_service.format_currency(Decimal('-100.00'))
        self.assertIn('KES', formatted)
        self.assertIn('-', formatted)
        
        # Test very large number
        formatted = self.export_service.format_currency(Decimal('999999999.99'))
        self.assertIn('KES', formatted)
        self.assertIn(',', formatted)
    
    def test_date_formatting_edge_cases(self):
        """
        Test date formatting with edge cases.
        
        Validates: Requirement 8.7, 8.8
        """
        # Test None
        formatted = self.export_service.format_date(None)
        self.assertEqual(formatted, 'N/A')
        
        # Test datetime object
        from datetime import datetime
        dt = datetime(2024, 6, 15, 14, 30, 0)
        formatted = self.export_service.format_date(dt)
        self.assertIn('2024-06-15', formatted)
        
        # Test string date
        formatted = self.export_service.format_date('2024-06-15')
        self.assertEqual(formatted, '2024-06-15')
    
    def test_export_with_special_characters(self):
        """
        Test that export handles special characters correctly.
        
        Validates: Requirement 8.7, 8.8, 10.10
        """
        # Create test data with special characters
        report_data = {
            'loans': [
                {
                    'loan_number': 'LN0001',
                    'borrower_name': "O'Brien & Sons",
                    'phone_number': '0700000001',
                    'disbursement_date': date.today(),
                    'principal_amount': Decimal('10000.00'),
                    'interest_amount': Decimal('1000.00'),
                    'processing_fee': Decimal('500.00'),
                    'total_amount': Decimal('11500.00'),
                    'amount_paid': Decimal('0.00'),
                    'outstanding_amount': Decimal('11500.00'),
                }
            ]
        }
        filters = {'period': 'today'}
        
        # PDF should handle special characters
        pdf_response = self.export_service.export_to_pdf(
            report_data, 'disbursed_loans', filters
        )
        self.assertIsInstance(pdf_response, HttpResponse)
        
        # Excel should handle special characters
        excel_response = self.export_service.export_to_excel(
            report_data, 'disbursed_loans', filters
        )
        self.assertIsInstance(excel_response, HttpResponse)
        
        # Verify special characters are preserved in Excel
        excel_content = BytesIO(excel_response.content)
        workbook = openpyxl.load_workbook(excel_content)
        sheet = workbook.active
        
        # Find the borrower name in Excel
        found_name = False
        for row in sheet.iter_rows():
            for cell in row:
                if cell.value and "O'Brien" in str(cell.value):
                    found_name = True
                    break
        
        self.assertTrue(found_name, "Special characters should be preserved in Excel")
    
    def test_export_with_different_report_types(self):
        """
        Test that export works with different report types.
        
        Validates: Requirement 8.7, 8.8, 7.12
        """
        report_types = ['disbursed_loans', 'loans_due', 'processing_fees', 'interest_income']
        
        report_data = {
            'loans': [
                {
                    'loan_number': 'LN0001',
                    'borrower_name': 'John Doe',
                    'phone_number': '0700000001',
                    'disbursement_date': date.today(),
                    'principal_amount': Decimal('10000.00'),
                    'interest_amount': Decimal('1000.00'),
                    'processing_fee': Decimal('500.00'),
                    'total_amount': Decimal('11500.00'),
                    'amount_paid': Decimal('0.00'),
                    'outstanding_amount': Decimal('11500.00'),
                    'due_date': date.today() + timedelta(days=30),
                }
            ]
        }
        filters = {'period': 'total'}
        
        for report_type in report_types:
            # PDF export should work for all report types
            pdf_response = self.export_service.export_to_pdf(
                report_data, report_type, filters
            )
            self.assertIsInstance(pdf_response, HttpResponse)
            self.assertEqual(pdf_response['Content-Type'], 'application/pdf')
            
            # Excel export should work for all report types
            excel_response = self.export_service.export_to_excel(
                report_data, report_type, filters
            )
            self.assertIsInstance(excel_response, HttpResponse)
            self.assertEqual(
                excel_response['Content-Type'],
                'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
            )
