﻿#!/usr/bin/env python
"""
Haven Grazuri Investment Limited - Complete Deployment Script
Non-interactive automated deployment for production/development servers

This script will:
1. Check Python version and requirements
2. Install all Python dependencies
3. Setup/reset database
4. Run all migrations
5. Create superuser
6. Import initial data
7. Collect static files
8. Run system checks
9. Start the server (optional)

Usage:
    python deploy.py                    # Full deployment
    python deploy.py --skip-db          # Skip database reset
    python deploy.py --no-server        # Don't start server
    python deploy.py --production       # Production mode (no debug)
"""

import os
import sys
import subprocess
import platform
import shutil
from pathlib import Path
import time

# Configuration
PROJECT_NAME = "Haven Grazuri Investment Limited"
DB_NAME = "xygbfpsg_graz"
DB_USER = "root"
DB_PASSWORD = "password"
DB_HOST = "localhost"
DB_PORT = "3306"

# Superuser credentials
SUPERUSER_USERNAME = "admin"
SUPERUSER_EMAIL = "admin@havengrazuri.co.ke"
SUPERUSER_PASSWORD = "Admin@2025"
SUPERUSER_FIRST_NAME = "System"
SUPERUSER_LAST_NAME = "Administrator"

# Colors for 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 header message"""
    print(f"\n{Colors.HEADER}{'=' * 100}{Colors.ENDC}")
    print(f"{Colors.HEADER}{Colors.BOLD}{message}{Colors.ENDC}")
    print(f"{Colors.HEADER}{'=' * 100}{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, check=True, shell=True, capture_output=False):
    """Run a shell command and handle errors"""
    print_info(f"{description}...")
    try:
        if capture_output:
            result = subprocess.run(
                command,
                shell=shell,
                check=check,
                capture_output=True,
                text=True
            )
            return result
        else:
            result = subprocess.run(command, shell=shell, check=check)
            print_success(f"{description} completed")
            return result
    except subprocess.CalledProcessError as e:
        if check:
            print_error(f"{description} failed: {str(e)}")
            if capture_output and e.stderr:
                print(e.stderr)
            sys.exit(1)
        else:
            print_warning(f"{description} had warnings (continuing)")
            return None

def check_python_version():
    """Check if Python version is compatible"""
    print_header("STEP 1: Checking Python Version")
    
    version = sys.version_info
    print_info(f"Python version: {version.major}.{version.minor}.{version.micro}")
    
    if version.major < 3 or (version.major == 3 and version.minor < 8):
        print_error("Python 3.8 or higher is required")
        sys.exit(1)
    
    print_success(f"Python version {version.major}.{version.minor}.{version.micro} is compatible")

def check_mysql():
    """Check if MySQL is installed and running"""
    print_header("STEP 2: Checking MySQL")
    
    try:
        result = run_command(
            f'mysql --version',
            "Checking MySQL installation",
            capture_output=True
        )
        print_success(f"MySQL is installed: {result.stdout.strip()}")
    except:
        print_error("MySQL is not installed or not in PATH")
        print_info("Please install MySQL and ensure it's in your system PATH")
        sys.exit(1)
    
    # Test MySQL connection
    try:
        result = run_command(
            f'mysql -u {DB_USER} -p{DB_PASSWORD} -e "SELECT 1"',
            "Testing MySQL connection",
            capture_output=True
        )
        print_success("MySQL connection successful")
    except:
        print_error(f"Cannot connect to MySQL with user '{DB_USER}'")
        print_info("Please check your MySQL credentials in the script")
        sys.exit(1)

def install_requirements():
    """Install Python requirements"""
    print_header("STEP 3: Installing Python Requirements")
    
    requirements_file = Path("requirements.txt")
    if not requirements_file.exists():
        print_warning("requirements.txt not found, skipping")
        return
    
    print_info("Installing packages from requirements.txt...")
    run_command(
        f'{sys.executable} -m pip install --upgrade pip',
        "Upgrading pip"
    )
    
    run_command(
        f'{sys.executable} -m pip install -r requirements.txt',
        "Installing requirements"
    )
    
    print_success("All requirements installed")

def setup_database(skip_db=False):
    """Setup or reset the database"""
    print_header("STEP 4: Setting Up Database")
    
    if skip_db:
        print_warning("Skipping database reset (--skip-db flag)")
        return
    
    # Drop database if exists
    print_info(f"Dropping database '{DB_NAME}' if it exists...")
    run_command(
        f'mysql -u {DB_USER} -p{DB_PASSWORD} -e "DROP DATABASE IF EXISTS {DB_NAME}"',
        "Dropping existing database",
        check=False
    )
    
    # Create database
    print_info(f"Creating database '{DB_NAME}'...")
    run_command(
        f'mysql -u {DB_USER} -p{DB_PASSWORD} -e "CREATE DATABASE {DB_NAME} CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"',
        "Creating database"
    )
    
    print_success(f"Database '{DB_NAME}' created successfully")

def run_migrations():
    """Run Django migrations"""
    print_header("STEP 5: Running Database Migrations")
    
    # Make migrations
    print_info("Creating migrations...")
    run_command(
        f'{sys.executable} manage.py makemigrations',
        "Making migrations",
        check=False
    )
    
    # Run migrations
    print_info("Applying migrations...")
    run_command(
        f'{sys.executable} manage.py migrate',
        "Running migrations"
    )
    
    print_success("All migrations applied successfully")

def create_superuser():
    """Create Django superuser"""
    print_header("STEP 6: Creating Superuser")
    
    # Check if superuser already exists
    check_script = f"""
import os
import django
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'branch_system.settings')
django.setup()
from users.models import CustomUser
exists = CustomUser.objects.filter(username='{SUPERUSER_USERNAME}').exists()
print('EXISTS' if exists else 'NOT_EXISTS')
"""
    
    with open('_check_user.py', 'w') as f:
        f.write(check_script)
    
    result = run_command(
        f'{sys.executable} _check_user.py',
        "Checking if superuser exists",
        capture_output=True
    )
    
    os.remove('_check_user.py')
    
    if 'EXISTS' in result.stdout:
        print_warning(f"Superuser '{SUPERUSER_USERNAME}' already exists, skipping")
        return
    
    # Create superuser
    create_script = f"""
import os
import django
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'branch_system.settings')
django.setup()
from users.models import CustomUser, Branch

# Get or create main branch
branch, _ = Branch.objects.get_or_create(
    code='MAIN',
    defaults={{
        'name': 'Main Branch - Thika',
        'location': 'Thika',
        'phone_number': '+254112941830',
        'email': 'main@havengrazuri.co.ke',
        'status': 'active'
    }}
)

# Create superuser
user = CustomUser.objects.create_superuser(
    username='{SUPERUSER_USERNAME}',
    email='{SUPERUSER_EMAIL}',
    password='{SUPERUSER_PASSWORD}',
    first_name='{SUPERUSER_FIRST_NAME}',
    last_name='{SUPERUSER_LAST_NAME}',
    phone_number='+254112941830',
    id_number='00000000',
    branch=branch,
    role='admin'
)

# Give access to all branches
user.accessible_branches.set(Branch.objects.all())

print(f'Superuser created: {{user.username}}')
"""
    
    with open('_create_superuser.py', 'w') as f:
        f.write(create_script)
    
    run_command(
        f'{sys.executable} _create_superuser.py',
        "Creating superuser"
    )
    
    os.remove('_create_superuser.py')
    
    print_success(f"Superuser '{SUPERUSER_USERNAME}' created successfully")
    print_info(f"Username: {SUPERUSER_USERNAME}")
    print_info(f"Password: {SUPERUSER_PASSWORD}")

def create_initial_data():
    """Create initial data (branches, loan products, etc.)"""
    print_header("STEP 7: Creating Initial Data")
    
    init_script = """
import os
import django
from decimal import Decimal
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'branch_system.settings')
django.setup()
from users.models import Branch
from loans.models import LoanProduct

# Create branches
branches_data = [
    {
        'name': 'Main Branch - Thika',
        'code': 'MAIN',
        'location': 'Thika',
        'phone_number': '+254112941830',
        'email': 'main@havengrazuri.co.ke',
        'status': 'active'
    },
    {
        'name': 'Nairobi Branch',
        'code': 'NRB',
        'location': 'Nairobi CBD',
        'phone_number': '+254112941831',
        'email': 'nairobi@havengrazuri.co.ke',
        'status': 'active'
    }
]

for branch_data in branches_data:
    branch, created = Branch.objects.get_or_create(
        code=branch_data['code'],
        defaults=branch_data
    )
    if created:
        print(f'Created branch: {branch.name}')
    else:
        print(f'Branch already exists: {branch.name}')

# Create loan products
products_data = [
    {
        'name': 'Biashara Loan',
        'product_type': 'biashara',
        'description': 'Business loan for entrepreneurs',
        'min_amount': Decimal('5000.00'),
        'max_amount': Decimal('500000.00'),
        'interest_rate': Decimal('10.00'),
        'processing_fee': Decimal('5.00'),
        'late_payment_penalty': Decimal('5.00'),
        'duration_months': 12,
        'min_duration': 30,
        'max_duration': 365,
        'available_repayment_methods': ['monthly', 'weekly'],
        'requires_guarantor': False,
        'requires_collateral': False,
        'is_active': True,
        'gl_code': '11002',
        'grazuri_account_type': 'B'
    },
    {
        'name': 'Log Book Loan',
        'product_type': 'logbook',
        'description': 'Loan secured by vehicle logbook',
        'min_amount': Decimal('10000.00'),
        'max_amount': Decimal('1000000.00'),
        'interest_rate': Decimal('8.00'),
        'processing_fee': Decimal('3.00'),
        'late_payment_penalty': Decimal('5.00'),
        'duration_months': 12,
        'min_duration': 30,
        'max_duration': 365,
        'available_repayment_methods': ['monthly'],
        'requires_guarantor': False,
        'requires_collateral': True,
        'is_active': True,
        'gl_code': '11003',
        'grazuri_account_type': 'P'
    }
]

for product_data in products_data:
    product, created = LoanProduct.objects.get_or_create(
        product_type=product_data['product_type'],
        defaults=product_data
    )
    if created:
        print(f'Created loan product: {product.name}')
    else:
        print(f'Loan product already exists: {product.name}')

print('Initial data created successfully')
"""
    
    with open('_create_initial_data.py', 'w') as f:
        f.write(init_script)
    
    run_command(
        f'{sys.executable} _create_initial_data.py',
        "Creating initial data"
    )
    
    os.remove('_create_initial_data.py')
    
    print_success("Initial data created successfully")

def import_grazuri_users():
    """Import Grazuri users if available"""
    print_header("STEP 8: Importing Grazuri Users (Optional)")
    
    if not Path('import_grazuri_users.py').exists():
        print_warning("import_grazuri_users.py not found, skipping")
        return
    
    # Check if user table exists
    check_script = """
import pymysql
try:
    conn = pymysql.connect(
        host='localhost',
        user='root',
        password='password',
        database='xygbfpsg_graz'
    )
    cursor = conn.cursor()
    cursor.execute("SHOW TABLES LIKE 'user'")
    exists = cursor.fetchone() is not None
    if exists:
        cursor.execute("SELECT COUNT(*) FROM user")
        count = cursor.fetchone()[0]
        print(f'USER_TABLE_EXISTS:{count}')
    else:
        print('USER_TABLE_NOT_EXISTS')
    conn.close()
except Exception as e:
    print(f'ERROR:{str(e)}')
"""
    
    with open('_check_user_table.py', 'w') as f:
        f.write(check_script)
    
    result = run_command(
        f'{sys.executable} _check_user_table.py',
        "Checking for Grazuri user table",
        capture_output=True,
        check=False
    )
    
    os.remove('_check_user_table.py')
    
    if 'USER_TABLE_EXISTS' in result.stdout:
        count = result.stdout.split(':')[1].strip()
        print_info(f"Found {count} users in Grazuri user table")
        
        if int(count) > 0:
            print_info("Importing Grazuri users...")
            run_command(
                f'{sys.executable} import_grazuri_users.py',
                "Importing Grazuri users",
                check=False
            )
        else:
            print_warning("No users to import from Grazuri table")
    else:
        print_warning("Grazuri user table not found, skipping import")

def collect_static_files():
    """Collect static files"""
    print_header("STEP 9: Collecting Static Files")
    
    run_command(
        f'{sys.executable} manage.py collectstatic --noinput',
        "Collecting static files",
        check=False
    )
    
    print_success("Static files collected")

def run_system_checks():
    """Run Django system checks"""
    print_header("STEP 10: Running System Checks")
    
    result = run_command(
        f'{sys.executable} manage.py check',
        "Running system checks",
        capture_output=True,
        check=False
    )
    
    if result and result.returncode == 0:
        print_success("All system checks passed")
    else:
        print_warning("Some system checks failed (this may be normal)")
        if result and result.stdout:
            print(result.stdout)

def create_env_file():
    """Create or update .env file"""
    print_header("STEP 11: Creating Environment File")
    
    env_content = f"""# Database Configuration
DB_NAME={DB_NAME}
DB_USER={DB_USER}
DB_PASSWORD={DB_PASSWORD}
DB_HOST={DB_HOST}
DB_PORT={DB_PORT}

# Haven Grazuri Investment Limited Company Information
COMPANY_NAME=Haven Grazuri Investment Limited
COMPANY_EMAIL=havenin2023@gmail.com
COMPANY_PHONE=+254112941830
COMPANY_WHATSAPP=+254112941830

# M-Pesa Configuration
MPESA_SHORTCODE=4159523

# SMS Configuration
SMS_SENDER_ID=HavGrazuri

# Django Settings
SECRET_KEY=django-insecure-change-this-in-production
DEBUG=True
ALLOWED_HOSTS=localhost,127.0.0.1

# Security (set these in production)
# SECURE_SSL_REDIRECT=True
# SESSION_COOKIE_SECURE=True
# CSRF_COOKIE_SECURE=True
"""
    
    with open('.env', 'w') as f:
        f.write(env_content)
    
    print_success(".env file created")
    print_warning("Remember to update SECRET_KEY and security settings for production!")

def print_deployment_summary():
    """Print deployment summary"""
    print_header("DEPLOYMENT COMPLETE!")
    
    print(f"{Colors.OKGREEN}{Colors.BOLD}")
    print("✓ Python requirements installed")
    print("✓ Database created and configured")
    print("✓ Migrations applied")
    print("✓ Superuser created")
    print("✓ Initial data loaded")
    print("✓ Static files collected")
    print("✓ System checks completed")
    print(f"{Colors.ENDC}")
    
    print(f"\n{Colors.OKCYAN}{Colors.BOLD}System Information:{Colors.ENDC}")
    print(f"  Database: {DB_NAME}")
    print(f"  Superuser: {SUPERUSER_USERNAME}")
    print(f"  Password: {SUPERUSER_PASSWORD}")
    print(f"  Email: {SUPERUSER_EMAIL}")
    
    print(f"\n{Colors.OKCYAN}{Colors.BOLD}Next Steps:{Colors.ENDC}")
    print(f"  1. Start the server:")
    print(f"     {Colors.BOLD}python manage.py runserver{Colors.ENDC}")
    print(f"  2. Access the system:")
    print(f"     {Colors.BOLD}http://127.0.0.1:8000/{Colors.ENDC}")
    print(f"  3. Login with:")
    print(f"     Username: {Colors.BOLD}{SUPERUSER_USERNAME}{Colors.ENDC}")
    print(f"     Password: {Colors.BOLD}{SUPERUSER_PASSWORD}{Colors.ENDC}")
    
    print(f"\n{Colors.WARNING}{Colors.BOLD}Important:{Colors.ENDC}")
    print(f"  - Change the superuser password after first login")
    print(f"  - Update .env file with production settings")
    print(f"  - Set DEBUG=False in production")
    print(f"  - Configure proper SECRET_KEY")
    print(f"  - Enable HTTPS in production")
    
    print(f"\n{Colors.HEADER}{'=' * 100}{Colors.ENDC}\n")

def start_server(no_server=False):
    """Start the Django development server"""
    if no_server:
        print_warning("Skipping server start (--no-server flag)")
        return
    
    print_header("Starting Development Server")
    print_info("Server will start at http://127.0.0.1:8000/")
    print_info("Press Ctrl+C to stop the server")
    print()
    
    try:
        subprocess.run(f'{sys.executable} manage.py runserver', shell=True)
    except KeyboardInterrupt:
        print_info("\nServer stopped")

def main():
    """Main deployment function"""
    import argparse
    
    parser = argparse.ArgumentParser(description='Deploy Haven Grazuri Investment Limited System')
    parser.add_argument('--skip-db', action='store_true', help='Skip database reset')
    parser.add_argument('--no-server', action='store_true', help='Do not start server after deployment')
    parser.add_argument('--production', action='store_true', help='Production mode (no debug)')
    
    args = parser.parse_args()
    
    print(f"\n{Colors.HEADER}{Colors.BOLD}")
    print("=" * 100)
    print(f"  {PROJECT_NAME}")
    print("  Automated Deployment Script")
    print("=" * 100)
    print(f"{Colors.ENDC}\n")
    
    start_time = time.time()
    
    try:
        # Run deployment steps
        check_python_version()
        check_mysql()
        install_requirements()
        setup_database(skip_db=args.skip_db)
        run_migrations()
        create_superuser()
        create_initial_data()
        import_grazuri_users()
        collect_static_files()
        run_system_checks()
        create_env_file()
        
        # Calculate deployment time
        elapsed_time = time.time() - start_time
        print_info(f"Deployment completed in {elapsed_time:.2f} seconds")
        
        # Print summary
        print_deployment_summary()
        
        # Start server if requested
        if not args.no_server:
            start_server(no_server=args.no_server)
        
    except KeyboardInterrupt:
        print_error("\nDeployment cancelled by user")
        sys.exit(1)
    except Exception as e:
        print_error(f"Deployment failed: {str(e)}")
        import traceback
        traceback.print_exc()
        sys.exit(1)

if __name__ == '__main__':
    main()
