#!/usr/bin/env python3
"""
Production Deployment Script for Loan System Fixes
==================================================

This script safely deploys the loan calculation fixes to production:
- Interest rate fix (20% instead of 15%)
- Processing fee fix (2% instead of 8%, one-time only)
- Disbursement date handling improvements
- Rollover disbursement date support

Usage:
    python deploy_loan_fixes.py

Safety Features:
- Creates backup before changes
- Validates settings before applying
- Provides rollback instructions
- Shows detailed progress
"""

import os
import sys
import django
from datetime import datetime
import subprocess
import json

# Set up Django environment
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'branch_system.settings')
django.setup()

from django.db import transaction
from utils.models import SystemSetting
from loans.models import LoanProduct
from decimal import Decimal

class ProductionDeployer:
    def __init__(self):
        self.backup_data = {}
        self.start_time = datetime.now()
        
    def log(self, message, level="INFO"):
        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        print(f"[{timestamp}] [{level}] {message}")
        
    def create_backup(self):
        """Create backup of current settings"""
        self.log("Creating backup of current settings...")
        
        try:
            # Backup current system settings
            settings_to_backup = [
                'boost_interest_rate',
                'boost_processing_fee',
                'boost_plus_interest_rate', 
                'boost_plus_processing_fee',
                'mwamba_interest_rate',
                'mwamba_processing_fee',
                'imara_interest_rate',
                'imara_processing_fee'
            ]
            
            for key in settings_to_backup:
                try:
                    setting = SystemSetting.objects.get(key=key)
                    self.backup_data[key] = {
                        'value': setting.value,
                        'category': setting.category,
                        'description': setting.description,
                        'is_public': setting.is_public
                    }
                except SystemSetting.DoesNotExist:
                    self.backup_data[key] = None
                    
            # Save backup to file
            backup_filename = f"settings_backup_{self.start_time.strftime('%Y%m%d_%H%M%S')}.json"
            with open(backup_filename, 'w') as f:
                json.dump(self.backup_data, f, indent=2, default=str)
                
            self.log(f"Backup saved to: {backup_filename}")
            return True
            
        except Exception as e:
            self.log(f"Error creating backup: {str(e)}", "ERROR")
            return False
    
    def validate_environment(self):
        """Validate production environment"""
        self.log("Validating production environment...")
        
        try:
            # Check if we're in production
            from django.conf import settings
            if settings.DEBUG:
                self.log("WARNING: DEBUG mode is enabled. This might not be production.", "WARNING")
            
            # Check database connectivity
            from django.db import connection
            with connection.cursor() as cursor:
                cursor.execute("SELECT 1")
                
            self.log("Database connection: OK")
            return True
            
        except Exception as e:
            self.log(f"Environment validation failed: {str(e)}", "ERROR")
            return False
    
    def run_migrations(self):
        """Run Django migrations"""
        self.log("Running database migrations...")
        
        try:
            # Check for pending migrations
            result = subprocess.run([
                'python', 'manage.py', 'showmigrations', '--plan'
            ], capture_output=True, text=True, check=True)
            
            if '[ ]' in result.stdout:
                self.log("Pending migrations found. Running migrations...")
                
                # Run migrations
                result = subprocess.run([
                    'python', 'manage.py', 'migrate', '--noinput'
                ], capture_output=True, text=True, check=True)
                
                self.log("Migrations completed successfully")
            else:
                self.log("No pending migrations found")
                
            return True
            
        except subprocess.CalledProcessError as e:
            self.log(f"Migration failed: {e.stderr}", "ERROR")
            return False
        except Exception as e:
            self.log(f"Error running migrations: {str(e)}", "ERROR")
            return False
    
    def update_system_settings(self):
        """Update system settings with correct values"""
        self.log("Updating system settings...")
        
        try:
            with transaction.atomic():
                # Define the correct settings
                settings_to_update = [
                    {
                        'key': 'boost_interest_rate',
                        'value': '20.0',
                        'category': 'loan',
                        'description': 'Monthly interest rate for Boost loans (%)',
                        'is_public': True
                    },
                    {
                        'key': 'boost_processing_fee',
                        'value': '2.0',
                        'category': 'loan', 
                        'description': 'Processing fee for Boost loans (%) - one-time only',
                        'is_public': True
                    },
                    {
                        'key': 'boost_plus_interest_rate',
                        'value': '20.0',
                        'category': 'loan',
                        'description': 'Monthly interest rate for Boost Plus loans (%)',
                        'is_public': True
                    },
                    {
                        'key': 'boost_plus_processing_fee',
                        'value': '2.0',
                        'category': 'loan',
                        'description': 'Processing fee for Boost Plus loans (%) - one-time only',
                        'is_public': True
                    },
                    {
                        'key': 'mwamba_interest_rate',
                        'value': '20.0',
                        'category': 'loan',
                        'description': 'Monthly interest rate for Mwamba loans (%)',
                        'is_public': True
                    },
                    {
                        'key': 'mwamba_processing_fee',
                        'value': '2.0',
                        'category': 'loan',
                        'description': 'Processing fee for Mwamba loans (%) - one-time only',
                        'is_public': True
                    },
                    {
                        'key': 'imara_interest_rate',
                        'value': '10.0',
                        'category': 'loan',
                        'description': 'Monthly interest rate for Imara loans (%)',
                        'is_public': True
                    },
                    {
                        'key': 'imara_processing_fee',
                        'value': '2.0',
                        'category': 'loan',
                        'description': 'Processing fee for Imara loans (%) - one-time only',
                        'is_public': True
                    }
                ]
                
                for setting_data in settings_to_update:
                    setting, created = SystemSetting.objects.update_or_create(
                        key=setting_data['key'],
                        defaults=setting_data
                    )
                    status = "Created" if created else "Updated"
                    self.log(f"✓ {status} {setting_data['key']}: {setting_data['value']}%")
                
                self.log("System settings updated successfully")
                return True
                
        except Exception as e:
            self.log(f"Error updating settings: {str(e)}", "ERROR")
            return False
    
    def verify_changes(self):
        """Verify that changes were applied correctly"""
        self.log("Verifying changes...")
        
        try:
            # Test Boost product calculations
            boost = LoanProduct.objects.filter(product_type='boost').first()
            if boost:
                interest_rate = boost.get_interest_rate()
                processing_fee = boost.get_processing_fee()
                
                self.log(f"Boost Interest Rate: {interest_rate}%")
                self.log(f"Boost Processing Fee: {processing_fee}%")
                
                # Test calculations
                test_amount = Decimal('5000')
                interest = boost.calculate_interest(test_amount, 1)
                processing = boost.calculate_processing_fee(test_amount, 1)
                
                self.log(f"Test calculation for KES 5,000:")
                self.log(f"  Interest (1 month): KES {interest}")
                self.log(f"  Processing Fee: KES {processing}")
                self.log(f"  Total: KES {test_amount + interest + processing}")
                
                # Verify expected values
                expected_interest = test_amount * Decimal('0.20')  # 20%
                expected_processing = test_amount * Decimal('0.02')  # 2%
                
                if interest == expected_interest and processing == expected_processing:
                    self.log("✓ Calculations verified successfully")
                    return True
                else:
                    self.log("✗ Calculations do not match expected values", "ERROR")
                    return False
            else:
                self.log("No Boost product found", "ERROR")
                return False
                
        except Exception as e:
            self.log(f"Error verifying changes: {str(e)}", "ERROR")
            return False
    
    def create_rollback_script(self):
        """Create rollback script"""
        self.log("Creating rollback script...")
        
        rollback_script = f"""#!/usr/bin/env python3
'''
Rollback Script for Loan System Fixes
Generated: {self.start_time.strftime('%Y-%m-%d %H:%M:%S')}
'''

import os
import django
import json

# Set up Django environment
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'branch_system.settings')
django.setup()

from utils.models import SystemSetting

# Restore settings from backup
backup_data = {json.dumps(self.backup_data, indent=2)}

def rollback():
    print("Rolling back system settings...")
    
    for key, data in backup_data.items():
        if data is None:
            # Setting didn't exist before, delete it
            try:
                SystemSetting.objects.get(key=key).delete()
                print(f"Deleted {key}")
            except SystemSetting.DoesNotExist:
                pass
        else:
            # Restore original setting
            SystemSetting.objects.update_or_create(
                key=key,
                defaults=data
            )
            print(f"Restored {key}: {data['value']}")
    
    print("Rollback completed!")

if __name__ == "__main__":
    rollback()
"""
        
        rollback_filename = f"rollback_{self.start_time.strftime('%Y%m%d_%H%M%S')}.py"
        with open(rollback_filename, 'w') as f:
            f.write(rollback_script)
            
        self.log(f"Rollback script created: {rollback_filename}")
        return rollback_filename
    
    def deploy(self):
        """Main deployment process"""
        self.log("Starting production deployment...")
        self.log("=" * 60)
        
        try:
            # Step 1: Validate environment
            if not self.validate_environment():
                self.log("Environment validation failed. Aborting deployment.", "ERROR")
                return False
            
            # Step 2: Create backup
            if not self.create_backup():
                self.log("Backup creation failed. Aborting deployment.", "ERROR")
                return False
            
            # Step 3: Run migrations
            if not self.run_migrations():
                self.log("Migration failed. Aborting deployment.", "ERROR")
                return False
            
            # Step 4: Update settings
            if not self.update_system_settings():
                self.log("Settings update failed. Aborting deployment.", "ERROR")
                return False
            
            # Step 5: Verify changes
            if not self.verify_changes():
                self.log("Verification failed. Please check the changes.", "ERROR")
                return False
            
            # Step 6: Create rollback script
            rollback_script = self.create_rollback_script()
            
            # Success
            self.log("=" * 60)
            self.log("🎉 DEPLOYMENT COMPLETED SUCCESSFULLY!")
            self.log("=" * 60)
            self.log("Changes applied:")
            self.log("  ✓ Interest rates set to correct values (20% for most products)")
            self.log("  ✓ Processing fees set to 2% and one-time only")
            self.log("  ✓ Disbursement date handling improved")
            self.log("  ✓ Rollover disbursement date support added")
            self.log("  ✓ Templates fixed to use system settings instead of hardcoded values")
            self.log("")
            self.log(f"Rollback script: {rollback_script}")
            self.log("To rollback if needed: python " + rollback_script)
            self.log("")
            self.log("Next steps:")
            self.log("  1. Test the system with a new loan application")
            self.log("  2. Verify calculations in the admin panel")
            self.log("  3. Monitor for any issues")
            
            return True
            
        except Exception as e:
            self.log(f"Deployment failed with error: {str(e)}", "ERROR")
            return False

def main():
    """Main function"""
    print("🚀 Loan System Production Deployment")
    print("=" * 60)
    print("This script will deploy the loan calculation fixes to production.")
    print("")
    
    # Confirm deployment
    response = input("Are you sure you want to proceed? (yes/no): ").lower().strip()
    if response != 'yes':
        print("Deployment cancelled.")
        return
    
    # Run deployment
    deployer = ProductionDeployer()
    success = deployer.deploy()
    
    if success:
        print("\n✅ Deployment completed successfully!")
        sys.exit(0)
    else:
        print("\n❌ Deployment failed!")
        sys.exit(1)

if __name__ == "__main__":
    main()
