"""
Django management command to manually process unprocessed M-Pesa callbacks
Creates transactions from callbacks that failed to process automatically
"""
from django.core.management.base import BaseCommand, CommandError
from payments.models import MpesaCallback
from loans.models import MpesaTransaction


class Command(BaseCommand):
    help = 'Manually process unprocessed M-Pesa callbacks'

    def add_arguments(self, parser):
        parser.add_argument(
            'callback_id',
            type=str,
            nargs='?',
            help='Specific callback ID to process',
        )
        parser.add_argument(
            '--all-unprocessed',
            action='store_true',
            help='Process all unprocessed callbacks',
        )

    def handle(self, *args, **options):
        if options['all_unprocessed']:
            self.process_all_unprocessed()
        elif options['callback_id']:
            self.process_single(options['callback_id'])
        else:
            raise CommandError('Please provide either a callback_id or use --all-unprocessed flag')
    
    def process_single(self, callback_id):
        """Process a single callback"""
        self.stdout.write(self.style.SUCCESS('='*70))
        self.stdout.write(self.style.SUCCESS('Process M-Pesa Callback'))
        self.stdout.write(self.style.SUCCESS('='*70))
        
        try:
            callback = MpesaCallback.objects.get(id=callback_id)
        except MpesaCallback.DoesNotExist:
            raise CommandError(f'Callback not found: {callback_id}')
        
        self.stdout.write(f'\nCallback ID: {callback.id}')
        self.stdout.write(f'Type: {callback.callback_type}')
        self.stdout.write(f'Date: {callback.created_at}')
        
        if callback.transaction:
            self.stdout.write(self.style.WARNING('\n⚠ This callback is already linked to a transaction'))
            self.stdout.write(f'Transaction ID: {callback.transaction.id}')
            
            confirm = input('\nProcess anyway? (yes/no): ')
            if confirm.lower() != 'yes':
                self.stdout.write('Cancelled.')
                return
        
        # Extract data from callback
        data = callback.raw_data
        trans_id = data.get('TransID')
        amount = data.get('TransAmount', 0)
        phone = data.get('MSISDN', '')
        
        self.stdout.write(f'\nTransaction Data:')
        self.stdout.write(f'  Trans ID: {trans_id}')
        self.stdout.write(f'  Amount: KES {amount}')
        self.stdout.write(f'  Phone: {phone}')
        self.stdout.write(f'  Bill Ref: {data.get("BillRefNumber", "N/A")}')
        self.stdout.write(f'  First Name: {data.get("FirstName", "N/A")}')
        self.stdout.write(f'  Last Name: {data.get("LastName", "N/A")}')
        
        self.stdout.write('\n' + '-'*70)
        self.stdout.write('Creating transaction...')
        self.stdout.write('-'*70)
        
        try:
            # Check if transaction already exists
            existing = MpesaTransaction.objects.filter(trans_id=trans_id).first()
            if existing:
                self.stdout.write(self.style.WARNING(f'\n⚠ Transaction already exists: {existing.id}'))
                transaction = existing
                
                # Link callback to existing transaction
                callback.transaction = transaction
                callback.processed = True
                callback.save()
                
                self.stdout.write('✓ Callback linked to existing transaction')
            else:
                # Truncate phone if it's too long (hashed)
                # Database column is VARCHAR(17), but M-Pesa sent a 64-char hash
                phone_truncated = phone[:17] if phone and len(phone) > 17 else phone
                
                if len(phone) > 17:
                    self.stdout.write(self.style.WARNING(
                        f'\n⚠ Phone number is hashed/encrypted ({len(phone)} chars)'
                    ))
                    self.stdout.write(f'  Truncating to: {phone_truncated}')
                    self.stdout.write('  Will try to match by Bill Ref or Name instead')
                
                # Create new transaction
                transaction = MpesaTransaction.objects.create(
                    trans_id=trans_id,
                    transaction_type=data.get('TransactionType', 'Pay Bill'),
                    amount=amount,
                    phone_number=phone_truncated,
                    msisdn=phone_truncated,
                    trans_time=data.get('TransTime'),
                    business_short_code=data.get('BusinessShortCode'),
                    bill_ref_number=data.get('BillRefNumber'),
                    invoice_number=data.get('InvoiceNumber'),
                    org_account_balance=data.get('OrgAccountBalance'),
                    third_party_trans_id=data.get('ThirdPartyTransID'),
                    first_name=data.get('FirstName'),
                    middle_name=data.get('MiddleName'),
                    last_name=data.get('LastName'),
                    raw_confirmation_data=data,
                    status='confirmed'
                )
                
                # Link callback to transaction
                callback.transaction = transaction
                callback.processed = True
                callback.save()
                
                self.stdout.write(self.style.SUCCESS(f'\n✓ Transaction created: {transaction.id}'))
            
            # Now process the payment
            self.stdout.write('\n' + '-'*70)
            self.stdout.write('Processing payment...')
            self.stdout.write('-'*70 + '\n')
            
            success = transaction.process_payment()
            transaction.refresh_from_db()
            
            self.stdout.write('\n' + '='*70)
            if success:
                self.stdout.write(self.style.SUCCESS('✓ Payment Processed Successfully!'))
            else:
                self.stdout.write(self.style.WARNING('⚠ Payment Processing Completed with Issues'))
            self.stdout.write('='*70)
            
            self.stdout.write(f'\nTransaction Status: {transaction.status}')
            self.stdout.write(f'Matched Borrower: {transaction.borrower or "Not matched"}')
            self.stdout.write(f'Matched Loan: {transaction.loan or "Not matched"}')
            self.stdout.write(f'Repayment Created: {"Yes" if transaction.repayment else "No"}')
            
            if transaction.repayment:
                self.stdout.write(f'Receipt Number: {transaction.repayment.receipt_number}')
            
            if transaction.processing_notes:
                self.stdout.write(f'\nProcessing Notes: {transaction.processing_notes}')
            
            if success and transaction.repayment:
                self.stdout.write(self.style.SUCCESS('\n✓ Payment successfully applied to loan!'))
                self.stdout.write('\nYou can now view it at:')
                self.stdout.write('  • /loans/repayments/ - Repayment record')
                self.stdout.write('  • /payments/transactions/ - Transaction details')
                self.stdout.write('  • /loans/ - Updated loan balance')
            else:
                self.stdout.write(self.style.ERROR('\n✗ Payment could not be fully processed'))
                self.stdout.write('\nPossible reasons:')
                self.stdout.write('  - Phone number not registered in system')
                self.stdout.write('  - Phone number format mismatch')
                self.stdout.write('  - No active loans for this borrower')
            
            self.stdout.write('\n')
            
        except Exception as e:
            self.stdout.write(self.style.ERROR(f'\n✗ Error: {str(e)}'))
            import traceback
            traceback.print_exc()
            raise
    
    def process_all_unprocessed(self):
        """Process all unprocessed callbacks"""
        self.stdout.write(self.style.SUCCESS('='*70))
        self.stdout.write(self.style.SUCCESS('Process All Unprocessed Callbacks'))
        self.stdout.write(self.style.SUCCESS('='*70))
        
        # Find unprocessed callbacks
        unprocessed = MpesaCallback.objects.filter(
            processed=False
        ).order_by('-created_at')
        
        count = unprocessed.count()
        self.stdout.write(f'\nFound {count} unprocessed callback(s)\n')
        
        if count == 0:
            self.stdout.write(self.style.SUCCESS('No unprocessed callbacks found.'))
            return
        
        success_count = 0
        failed_count = 0
        
        for callback in unprocessed:
            self.stdout.write('='*70)
            self.stdout.write(f'Processing Callback: {callback.id}')
            self.stdout.write(f'Type: {callback.callback_type}')
            self.stdout.write(f'Date: {callback.created_at}')
            
            data = callback.raw_data
            trans_id = data.get('TransID', 'N/A')
            amount = data.get('TransAmount', 0)
            phone = data.get('MSISDN', 'N/A')
            
            self.stdout.write(f'Trans ID: {trans_id}')
            self.stdout.write(f'Amount: KES {amount}')
            self.stdout.write(f'Phone: {phone}')
            
            try:
                # Check if transaction exists
                transaction = MpesaTransaction.objects.filter(trans_id=trans_id).first()
                
                if not transaction:
                    # Truncate phone if too long
                    phone_truncated = phone[:17] if phone and len(phone) > 17 else phone
                    
                    # Create transaction
                    transaction = MpesaTransaction.objects.create(
                        trans_id=trans_id,
                        transaction_type=data.get('TransactionType', 'Pay Bill'),
                        amount=amount,
                        phone_number=phone_truncated,
                        msisdn=phone_truncated,
                        trans_time=data.get('TransTime'),
                        business_short_code=data.get('BusinessShortCode'),
                        bill_ref_number=data.get('BillRefNumber'),
                        invoice_number=data.get('InvoiceNumber'),
                        org_account_balance=data.get('OrgAccountBalance'),
                        third_party_trans_id=data.get('ThirdPartyTransID'),
                        first_name=data.get('FirstName'),
                        middle_name=data.get('MiddleName'),
                        last_name=data.get('LastName'),
                        raw_confirmation_data=data,
                        status='confirmed'
                    )
                    self.stdout.write(f'  ✓ Transaction created: {transaction.id}')
                
                # Link callback
                callback.transaction = transaction
                callback.processed = True
                callback.save()
                
                # Process payment
                success = transaction.process_payment()
                transaction.refresh_from_db()
                
                if success and transaction.repayment:
                    self.stdout.write(self.style.SUCCESS(
                        f'  ✓ SUCCESS: {transaction.borrower.get_full_name()} - {transaction.repayment.receipt_number}'
                    ))
                    success_count += 1
                else:
                    self.stdout.write(self.style.WARNING(
                        f'  ⚠ PARTIAL: {transaction.processing_notes}'
                    ))
                    failed_count += 1
                    
            except Exception as e:
                self.stdout.write(self.style.ERROR(f'  ✗ ERROR: {str(e)}'))
                failed_count += 1
        
        # Summary
        self.stdout.write('\n' + '='*70)
        self.stdout.write('SUMMARY')
        self.stdout.write('='*70)
        self.stdout.write(f'Total Callbacks: {count}')
        self.stdout.write(self.style.SUCCESS(f'Successfully Processed: {success_count}'))
        if failed_count > 0:
            self.stdout.write(self.style.WARNING(f'Failed/Partial: {failed_count}'))
        
        self.stdout.write('\n')
