#!/usr/bin/env python
"""
Comprehensive Deployment Script for Expenses Management System
HAVEN GRAZURI INVESTMENT LIMITED- Expenses Module

This script handles:
1. Database migrations
2. Static files collection
3. Sample data creation (optional)
4. System verification
5. Cache clearing
6. Deployment validation

Usage:
    python deploy_expenses_system.py [--skip-sample-data] [--production]
"""

import os
import sys
import django
import subprocess
from datetime import datetime

# Color codes for terminal output
class Colors:
    HEADER = '\033[95m'
    OKBLUE = '\033[94m'
    OKCYAN = '\033[96m'
    OKGREEN = '\033[92m'
    WARNING = '\033[93m'
    FAIL = '\033[91m'
    ENDC = '\033[0m'
    BOLD = '\033[1m'
    UNDERLINE = '\033[4m'

def print_header(message):
    """Print a formatted header"""
    print(f"\n{Colors.HEADER}{Colors.BOLD}{'='*80}{Colors.ENDC}")
    print(f"{Colors.HEADER}{Colors.BOLD}{message.center(80)}{Colors.ENDC}")
    print(f"{Colors.HEADER}{Colors.BOLD}{'='*80}{Colors.ENDC}\n")

def print_success(message):
    """Print a success message"""
    print(f"{Colors.OKGREEN}✓ {message}{Colors.ENDC}")

def print_error(message):
    """Print an error message"""
    print(f"{Colors.FAIL}✗ {message}{Colors.ENDC}")

def print_warning(message):
    """Print a warning message"""
    print(f"{Colors.WARNING}⚠ {message}{Colors.ENDC}")

def print_info(message):
    """Print an info message"""
    print(f"{Colors.OKCYAN}ℹ {message}{Colors.ENDC}")

def run_command(command, description, critical=True):
    """Run a shell command and handle errors"""
    print_info(f"Running: {description}...")
    try:
        result = subprocess.run(
            command,
            shell=True,
            check=True,
            capture_output=True,
            text=True
        )
        print_success(f"{description} completed successfully")
        if result.stdout:
            print(f"  Output: {result.stdout.strip()}")
        return True
    except subprocess.CalledProcessError as e:
        if critical:
            print_error(f"{description} failed!")
            print(f"  Error: {e.stderr}")
            return False
        else:
            print_warning(f"{description} had warnings (non-critical)")
            return True

def check_django_setup():
    """Check if Django is properly set up"""
    print_header("CHECKING DJANGO SETUP")
    try:
        os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'branch_system.settings')
        django.setup()
        print_success("Django setup successful")
        return True
    except Exception as e:
        print_error(f"Django setup failed: {str(e)}")
        return False

def check_database_connection():
    """Check database connection"""
    print_header("CHECKING DATABASE CONNECTION")
    try:
        from django.db import connection
        with connection.cursor() as cursor:
            cursor.execute("SELECT 1")
        print_success("Database connection successful")
        return True
    except Exception as e:
        print_error(f"Database connection failed: {str(e)}")
        return False

def run_migrations():
    """Run database migrations"""
    print_header("RUNNING DATABASE MIGRATIONS")
    
    # Check for pending migrations
    print_info("Checking for pending migrations...")
    if not run_command(
        "python manage.py showmigrations expenses",
        "Checking expenses migrations",
        critical=False
    ):
        print_warning("Could not check migrations status")
    
    # Make migrations for expenses app (skip if no changes)
    print_info("Checking for new migrations...")
    try:
        result = subprocess.run(
            "python manage.py makemigrations expenses --dry-run",
            shell=True,
            capture_output=True,
            text=True,
            timeout=10
        )
        if "No changes detected" in result.stdout:
            print_success("No new migrations needed")
        else:
            print_info("Creating migrations for expenses app...")
            run_command(
                "python manage.py makemigrations expenses --noinput",
                "Creating expenses migrations",
                critical=False
            )
    except Exception as e:
        print_warning(f"Could not check for migrations: {str(e)}")
    
    # Run all migrations
    print_info("Applying all migrations...")
    if run_command(
        "python manage.py migrate",
        "Applying database migrations",
        critical=True
    ):
        print_success("All migrations applied successfully")
        return True
    else:
        print_error("Migration failed!")
        return False

def collect_static_files(production=False):
    """Collect static files"""
    print_header("COLLECTING STATIC FILES")
    
    if production:
        command = "python manage.py collectstatic --noinput"
    else:
        print_warning("Skipping static files collection (development mode)")
        return True
    
    return run_command(
        command,
        "Collecting static files",
        critical=False
    )

def clear_cache():
    """Clear Django cache"""
    print_header("CLEARING CACHE")
    
    try:
        from django.core.cache import cache
        cache.clear()
        print_success("Cache cleared successfully")
        return True
    except Exception as e:
        print_warning(f"Could not clear cache: {str(e)}")
        return True  # Non-critical

def create_sample_data(skip_if_exists=True):
    """Create sample expense data"""
    print_header("CREATING SAMPLE DATA")
    
    try:
        from expenses.models import Expense
        
        # Check if sample data already exists
        existing_count = Expense.objects.count()
        if existing_count > 0:
            print_warning(f"Found {existing_count} existing expenses")
            if skip_if_exists:
                print_info("Skipping sample data creation (data already exists)")
                return True
        
        # Run the sample data creation script
        print_info("Creating 15 sample expenses...")
        if run_command(
            "python create_sample_expenses.py",
            "Creating sample expenses",
            critical=False
        ):
            print_success("Sample data created successfully")
            return True
        else:
            print_warning("Sample data creation had issues (non-critical)")
            return True
            
    except Exception as e:
        print_warning(f"Could not create sample data: {str(e)}")
        return True  # Non-critical

def verify_installation():
    """Verify the expenses system installation"""
    print_header("VERIFYING INSTALLATION")
    
    try:
        from expenses.models import Expense
        from django.db.models import Sum, Count
        
        # Check if expenses table exists
        total_expenses = Expense.objects.count()
        approved_expenses = Expense.objects.filter(status='approved').count()
        pending_expenses = Expense.objects.filter(status='pending').count()
        
        total_amount = Expense.objects.filter(status='approved').aggregate(
            total=Sum('amount')
        )['total'] or 0
        
        print_success("Expenses table exists and is accessible")
        print_info(f"  Total Expenses: {total_expenses}")
        print_info(f"  Approved: {approved_expenses}")
        print_info(f"  Pending: {pending_expenses}")
        print_info(f"  Total Amount: KES {total_amount:,.2f}")
        
        # Check if URLs are configured
        from django.urls import reverse
        try:
            expenses_url = reverse('expenses:expenses_list')
            print_success(f"Expenses URLs configured correctly: {expenses_url}")
        except Exception as e:
            print_error(f"URL configuration issue: {str(e)}")
            return False
        
        # Check if templates exist
        import os
        template_path = 'templates/expenses/expenses_list.html'
        if os.path.exists(template_path):
            print_success("Templates are in place")
        else:
            print_error("Templates not found!")
            return False
        
        return True
        
    except Exception as e:
        print_error(f"Verification failed: {str(e)}")
        return False

def run_system_check():
    """Run Django system check"""
    print_header("RUNNING SYSTEM CHECK")
    
    return run_command(
        "python manage.py check",
        "Django system check",
        critical=False
    )

def create_superuser_prompt():
    """Check for superuser (non-interactive)"""
    print_header("SUPERUSER CHECK")
    
    try:
        from django.contrib.auth import get_user_model
        User = get_user_model()
        
        if User.objects.filter(is_superuser=True).exists():
            print_success("Superuser already exists")
            return True
        else:
            print_warning("No superuser found")
            print_info("You can create one later with: python manage.py createsuperuser")
            return True
                
    except Exception as e:
        print_warning(f"Could not check for superuser: {str(e)}")
        return True

def print_deployment_summary():
    """Print deployment summary and next steps"""
    print_header("DEPLOYMENT SUMMARY")
    
    print(f"{Colors.OKGREEN}{Colors.BOLD}")
    print("✓ Expenses Management System deployed successfully!")
    print(f"{Colors.ENDC}")
    
    print(f"\n{Colors.OKCYAN}Next Steps:{Colors.ENDC}")
    print("1. Start the development server: python manage.py runserver")
    print("2. Navigate to: http://localhost:8000/expenses/")
    print("3. Login with your credentials")
    print("4. Test the expenses features")
    
    print(f"\n{Colors.OKCYAN}Available URLs:{Colors.ENDC}")
    print("  • Expenses List:      /expenses/")
    print("  • Add Expense:        /expenses/add/")
    print("  • Pending Approvals:  /expenses/approvals/pending/")
    print("  • Analytics:          /expenses/analytics/")
    
    print(f"\n{Colors.OKCYAN}Documentation:{Colors.ENDC}")
    print("  • User Guide:         EXPENSES_USER_GUIDE.md")
    print("  • Technical Docs:     EXPENSES_FEATURE_DOCUMENTATION.md")
    print("  • Quick Start:        EXPENSES_README.md")
    
    print(f"\n{Colors.OKCYAN}Verification:{Colors.ENDC}")
    print("  • Run: python verify_expenses.py")
    print("  • Run: python demo_expenses_features.py")
    
    print(f"\n{Colors.OKGREEN}{'='*80}{Colors.ENDC}\n")

def main():
    """Main deployment function"""
    # Parse command line arguments
    skip_sample_data = '--skip-sample-data' in sys.argv
    production = '--production' in sys.argv
    
    print(f"\n{Colors.BOLD}{Colors.HEADER}")
    print("╔" + "═" * 78 + "╗")
    print("║" + " " * 15 + "EXPENSES MANAGEMENT SYSTEM DEPLOYMENT" + " " * 26 + "║")
    print("║" + " " * 25 + "HAVEN GRAZURI Advance" + " " * 30 + "║")
    print("╚" + "═" * 78 + "╝")
    print(f"{Colors.ENDC}")
    
    print(f"{Colors.OKCYAN}Deployment started at: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}{Colors.ENDC}")
    
    if production:
        print_warning("Running in PRODUCTION mode")
    else:
        print_info("Running in DEVELOPMENT mode")
    
    if skip_sample_data:
        print_info("Sample data creation will be skipped")
    
    # Deployment steps
    steps = [
        ("Django Setup", check_django_setup, True),
        ("Database Connection", check_database_connection, True),
        ("Database Migrations", run_migrations, True),
        ("Static Files", lambda: collect_static_files(production), False),
        ("Cache Clearing", clear_cache, False),
        ("System Check", run_system_check, False),
        ("Installation Verification", verify_installation, True),
    ]
    
    # Add sample data step if not skipped
    if not skip_sample_data:
        steps.append(("Sample Data Creation", create_sample_data, False))
    
    # Add superuser check
    steps.append(("Superuser Check", create_superuser_prompt, False))
    
    # Execute deployment steps
    failed_steps = []
    for step_name, step_func, critical in steps:
        try:
            if not step_func():
                if critical:
                    print_error(f"Critical step '{step_name}' failed!")
                    failed_steps.append(step_name)
                else:
                    print_warning(f"Non-critical step '{step_name}' had issues")
        except Exception as e:
            print_error(f"Step '{step_name}' raised an exception: {str(e)}")
            if critical:
                failed_steps.append(step_name)
    
    # Check if deployment was successful
    if failed_steps:
        print_header("DEPLOYMENT FAILED")
        print_error("The following critical steps failed:")
        for step in failed_steps:
            print(f"  • {step}")
        print(f"\n{Colors.FAIL}Please fix the errors and try again.{Colors.ENDC}\n")
        sys.exit(1)
    else:
        print_deployment_summary()
        sys.exit(0)

if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt:
        print(f"\n\n{Colors.WARNING}Deployment interrupted by user{Colors.ENDC}\n")
        sys.exit(1)
    except Exception as e:
        print(f"\n\n{Colors.FAIL}Unexpected error: {str(e)}{Colors.ENDC}\n")
        sys.exit(1)
