"""
Tests for export functionality (PDF and Excel).
"""
from io import BytesIO
from decimal import Decimal
from datetime import date, timedelta

from django.test import TestCase
from django.contrib.auth import get_user_model

from openpyxl import load_workbook
from PyPDF2 import PdfReader

from accounting.services.export_service import ExportService
from accounting.models import Account


User = get_user_model()


class ExportServiceTestCase(TestCase):
    """Test cases for ExportService PDF and Excel generation."""
    
    def setUp(self):
        """Set up test data."""
        self.export_service = ExportService()
        self.user = User.objects.create_user(
            username='testuser',
            password='testpass123',
            phone_number='0799999999',
            email='exporttest@example.com'
        )
        
        # Create sample report data
        self.trial_balance_data = {
            'report_date': date.today(),
            'branch': 'Main Branch',
            'accounts': [
                {
                    'code': '202001',
                    'name': 'Loan Portfolio',
                    'type': 'asset',
                    'debit_balance': Decimal('1000000.00'),
                    'credit_balance': Decimal('0.00')
                },
                {
                    'code': '202003',
                    'name': 'Cash',
                    'type': 'asset',
                    'debit_balance': Decimal('500000.00'),
                    'credit_balance': Decimal('0.00')
                },
                {
                    'code': '301001',
                    'name': 'Share Capital',
                    'type': 'equity',
                    'debit_balance': Decimal('0.00'),
                    'credit_balance': Decimal('1500000.00')
                },
            ],
            'totals': {
                'total_debits': Decimal('1500000.00'),
                'total_credits': Decimal('1500000.00'),
                'balanced': True
            }
        }
        
        self.income_statement_data = {
            'period': {'start': date.today() - timedelta(days=30), 'end': date.today()},
            'branch': 'Main Branch',
            'income': {
                'Interest Income': Decimal('50000.00'),
                'Fee Income': Decimal('10000.00'),
            },
            'gross_income': Decimal('60000.00'),
            'expenses': {
                'Operating Expenses': Decimal('20000.00'),
                'Staff Costs': Decimal('15000.00'),
            },
            'total_expenses': Decimal('35000.00'),
            'net_income': Decimal('25000.00')
        }
        
        self.balance_sheet_data = {
            'as_of_date': date.today(),
            'branch': 'Main Branch',
            'assets': {
                'Current Assets': Decimal('500000.00'),
                'Loan Portfolio': Decimal('1000000.00'),
            },
            'total_assets': Decimal('1500000.00'),
            'liabilities': {
                'Current Liabilities': Decimal('200000.00'),
            },
            'total_liabilities': Decimal('200000.00'),
            'equity': {
                'Share Capital': Decimal('1000000.00'),
                'Retained Earnings': Decimal('300000.00'),
            },
            'total_equity': Decimal('1300000.00')
        }
        
        self.cash_flow_data = {
            'period': {'start': date.today() - timedelta(days=30), 'end': date.today()},
            'branch': 'Main Branch',
            'net_income': Decimal('25000.00'),
            'operating_adjustments': {
                'Depreciation': Decimal('5000.00'),
            },
            'net_operating': Decimal('30000.00'),
            'investing': {
                'Equipment Purchase': Decimal('-10000.00'),
            },
            'net_investing': Decimal('-10000.00'),
            'financing': {
                'Loan Proceeds': Decimal('50000.00'),
            },
            'net_financing': Decimal('50000.00'),
            'net_change': Decimal('70000.00'),
            'beginning_cash': Decimal('430000.00'),
            'ending_cash': Decimal('500000.00')
        }
        
        self.account_analysis_data = {
            'account': {
                'code': '202001',
                'name': 'Loan Portfolio',
                'type': 'asset'
            },
            'period': {'start': date.today() - timedelta(days=30), 'end': date.today()},
            'opening_balance': Decimal('900000.00'),
            'transactions': [
                {
                    'date': date.today() - timedelta(days=10),
                    'description': 'Loan disbursement',
                    'reference': 'LOAN-DISB-001',
                    'debit': Decimal('100000.00'),
                    'credit': Decimal('0.00'),
                    'balance': Decimal('1000000.00')
                }
            ],
            'total_debits': Decimal('100000.00'),
            'total_credits': Decimal('0.00'),
            'closing_balance': Decimal('1000000.00')
        }
    
    # PDF Export Tests
    
    def test_export_trial_balance_pdf(self):
        """Test that trial balance PDF export generates valid PDF."""
        buffer = self.export_service.export_trial_balance_pdf(
            self.trial_balance_data, 
            self.user.username
        )
        
        self.assertIsInstance(buffer, BytesIO)
        self.assertGreater(buffer.getbuffer().nbytes, 0)
        
        # Verify it's a valid PDF
        pdf = PdfReader(buffer)
        self.assertGreater(len(pdf.pages), 0)
    
    def test_export_income_statement_pdf(self):
        """Test that income statement PDF export generates valid PDF."""
        buffer = self.export_service.export_income_statement_pdf(
            self.income_statement_data,
            self.user.username
        )
        
        self.assertIsInstance(buffer, BytesIO)
        self.assertGreater(buffer.getbuffer().nbytes, 0)
        
        # Verify it's a valid PDF
        pdf = PdfReader(buffer)
        self.assertGreater(len(pdf.pages), 0)
    
    def test_export_balance_sheet_pdf(self):
        """Test that balance sheet PDF export generates valid PDF."""
        buffer = self.export_service.export_balance_sheet_pdf(
            self.balance_sheet_data,
            self.user.username
        )
        
        self.assertIsInstance(buffer, BytesIO)
        self.assertGreater(buffer.getbuffer().nbytes, 0)
        
        # Verify it's a valid PDF
        pdf = PdfReader(buffer)
        self.assertGreater(len(pdf.pages), 0)
    
    def test_export_cash_flow_pdf(self):
        """Test that cash flow statement PDF export generates valid PDF."""
        buffer = self.export_service.export_cash_flow_pdf(
            self.cash_flow_data,
            self.user.username
        )
        
        self.assertIsInstance(buffer, BytesIO)
        self.assertGreater(buffer.getbuffer().nbytes, 0)
        
        # Verify it's a valid PDF
        pdf = PdfReader(buffer)
        self.assertGreater(len(pdf.pages), 0)
    
    def test_export_account_analysis_pdf(self):
        """Test that account analysis PDF export generates valid PDF."""
        buffer = self.export_service.export_account_analysis_pdf(
            self.account_analysis_data,
            self.user.username
        )
        
        self.assertIsInstance(buffer, BytesIO)
        self.assertGreater(buffer.getbuffer().nbytes, 0)
        
        # Verify it's a valid PDF
        pdf = PdfReader(buffer)
        self.assertGreater(len(pdf.pages), 0)
    
    # Excel Export Tests
    
    def test_export_trial_balance_excel(self):
        """Test that trial balance Excel export generates valid Excel file."""
        buffer = self.export_service.export_trial_balance_excel(self.trial_balance_data)
        
        self.assertIsInstance(buffer, BytesIO)
        self.assertGreater(buffer.getbuffer().nbytes, 0)
        
        # Verify it's a valid Excel file
        wb = load_workbook(buffer)
        self.assertIn('Trial Balance', wb.sheetnames)
        
        ws = wb['Trial Balance']
        # Check headers exist
        self.assertEqual(ws.cell(6, 1).value, 'Account Code')
        self.assertEqual(ws.cell(6, 2).value, 'Account Name')
    
    def test_export_income_statement_excel(self):
        """Test that income statement Excel export generates valid Excel file."""
        buffer = self.export_service.export_income_statement_excel(self.income_statement_data)
        
        self.assertIsInstance(buffer, BytesIO)
        self.assertGreater(buffer.getbuffer().nbytes, 0)
        
        # Verify it's a valid Excel file
        wb = load_workbook(buffer)
        self.assertIn('Income Statement', wb.sheetnames)
    
    def test_export_balance_sheet_excel(self):
        """Test that balance sheet Excel export generates valid Excel file."""
        buffer = self.export_service.export_balance_sheet_excel(self.balance_sheet_data)
        
        self.assertIsInstance(buffer, BytesIO)
        self.assertGreater(buffer.getbuffer().nbytes, 0)
        
        # Verify it's a valid Excel file
        wb = load_workbook(buffer)
        self.assertIn('Balance Sheet', wb.sheetnames)
    
    def test_export_cash_flow_excel(self):
        """Test that cash flow Excel export generates valid Excel file."""
        buffer = self.export_service.export_cash_flow_excel(self.cash_flow_data)
        
        self.assertIsInstance(buffer, BytesIO)
        self.assertGreater(buffer.getbuffer().nbytes, 0)
        
        # Verify it's a valid Excel file
        wb = load_workbook(buffer)
        self.assertIn('Cash Flow Statement', wb.sheetnames)
    
    def test_export_account_analysis_excel(self):
        """Test that account analysis Excel export generates valid Excel file."""
        buffer = self.export_service.export_account_analysis_excel(self.account_analysis_data)
        
        self.assertIsInstance(buffer, BytesIO)
        self.assertGreater(buffer.getbuffer().nbytes, 0)
        
        # Verify it's a valid Excel file
        wb = load_workbook(buffer)
        self.assertIn('Account Analysis', wb.sheetnames)
        
        ws = wb['Account Analysis']
        # Check headers exist
        self.assertEqual(ws.cell(6, 1).value, 'Date')
        self.assertEqual(ws.cell(6, 2).value, 'Description')
    
    def test_currency_formatting(self):
        """Test that currency values are formatted correctly."""
        formatted = self.export_service._format_currency(Decimal('1234567.89'))
        self.assertEqual(formatted, '1,234,567.89')
        
        formatted_zero = self.export_service._format_currency(Decimal('0.00'))
        self.assertEqual(formatted_zero, '0.00')
        
        formatted_none = self.export_service._format_currency(None)
        self.assertEqual(formatted_none, '0.00')



class ExportViewTestCase(TestCase):
    """Test cases for export views."""
    
    def setUp(self):
        """Set up test data."""
        self.user = User.objects.create_user(
            username='testuser',
            password='testpass123',
            is_staff=True,
            phone_number='0700000000',  # Provide unique phone number
            email='testuser@example.com'  # Provide unique email
        )
        # Set role for staff_required decorator
        self.user.role = 'admin'
        self.user.save()
        
        # Create sample accounts
        Account.objects.create(
            code='202001',
            name='Loan Portfolio',
            account_type='asset',
            description='Loan Portfolio',
            created_by=self.user
        )
        Account.objects.create(
            code='301001',
            name='Share Capital',
            account_type='equity',
            description='Share Capital',
            created_by=self.user
        )
        
        self.client.login(username='testuser', password='testpass123')
    
    def test_trial_balance_export_pdf(self):
        """Test trial balance PDF export view returns correct content type."""
        response = self.client.get('/accounting/reports/trial-balance/export/', {
            'as_of_date': date.today().isoformat(),
            'format': 'pdf'
        })
        
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response['Content-Type'], 'application/pdf')
        self.assertIn('Content-Disposition', response)
        self.assertIn('attachment', response['Content-Disposition'])
        self.assertIn('.pdf', response['Content-Disposition'])
    
    def test_trial_balance_export_excel(self):
        """Test trial balance Excel export view returns correct content type."""
        response = self.client.get('/accounting/reports/trial-balance/export/', {
            'as_of_date': date.today().isoformat(),
            'format': 'excel'
        })
        
        self.assertEqual(response.status_code, 200)
        self.assertEqual(
            response['Content-Type'], 
            'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
        )
        self.assertIn('Content-Disposition', response)
        self.assertIn('attachment', response['Content-Disposition'])
        self.assertIn('.xlsx', response['Content-Disposition'])
    
    def test_income_statement_export_pdf(self):
        """Test income statement PDF export view."""
        response = self.client.get('/accounting/reports/income-statement/export/', {
            'start_date': (date.today() - timedelta(days=30)).isoformat(),
            'end_date': date.today().isoformat(),
            'format': 'pdf'
        })
        
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response['Content-Type'], 'application/pdf')
    
    def test_balance_sheet_export_pdf(self):
        """Test balance sheet PDF export view."""
        response = self.client.get('/accounting/reports/balance-sheet/export/', {
            'as_of_date': date.today().isoformat(),
            'format': 'pdf'
        })
        
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response['Content-Type'], 'application/pdf')
    
    def test_cash_flow_export_excel(self):
        """Test cash flow Excel export view."""
        response = self.client.get('/accounting/reports/cash-flow/export/', {
            'start_date': (date.today() - timedelta(days=30)).isoformat(),
            'end_date': date.today().isoformat(),
            'format': 'excel'
        })
        
        self.assertEqual(response.status_code, 200)
        self.assertEqual(
            response['Content-Type'],
            'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
        )
    
    def test_account_analysis_export_pdf(self):
        """Test account analysis PDF export view."""
        response = self.client.get('/accounting/reports/account-analysis/export/', {
            'account_code': '202001',
            'start_date': (date.today() - timedelta(days=30)).isoformat(),
            'end_date': date.today().isoformat(),
            'format': 'pdf'
        })
        
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response['Content-Type'], 'application/pdf')
    
    def test_export_requires_authentication(self):
        """Test that export views require authentication."""
        self.client.logout()
        
        response = self.client.get('/accounting/reports/trial-balance/export/', {
            'as_of_date': date.today().isoformat(),
            'format': 'pdf'
        })
        
        # Should redirect to login
        self.assertEqual(response.status_code, 302)
        self.assertIn('/login/', response.url)
    
    def test_export_requires_staff_permission(self):
        """Test that export views require staff permission."""
        # Create non-staff user (borrower role)
        non_staff_user = User.objects.create_user(
            username='nonstaff',
            password='testpass123',
            is_staff=False,
            phone_number='0712345678',  # Provide unique phone number
            email='nonstaff@example.com'  # Provide unique email
        )
        non_staff_user.role = 'borrower'  # Role without staff permission
        non_staff_user.save()
        
        self.client.logout()
        self.client.login(username='nonstaff', password='testpass123')
        
        response = self.client.get('/accounting/reports/trial-balance/export/', {
            'as_of_date': date.today().isoformat(),
            'format': 'pdf'
        })
        
        # Should redirect or return 403
        self.assertIn(response.status_code, [302, 403])
