from flask import Flask
import os
import logging
from datetime import datetime
from sqlalchemy.exc import SQLAlchemyError, OperationalError
from sqlalchemy import inspect
from time import sleep
from .config import get_config
from .logging_config import configure_logging
from dotenv import load_dotenv
from .extensions import db, login_manager, migrate, csrf, limiter, cache, mail
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
from flask_migrate import Migrate
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
from flask_caching import Cache

# Load environment variables first
load_dotenv()

db = SQLAlchemy()
migrate = Migrate()
limiter = Limiter(key_func=get_remote_address)
cache = Cache()

def create_app(config_class=None):
    app = Flask(__name__, static_folder='static')
    
    @app.context_processor
    def inject_now():
        return {'now': datetime.utcnow()}
    
    # Load configuration
    if config_class is None:
        config_class = get_config()
    app.config.from_object(config_class)

    # Ensure SECRET_KEY is set from environment if not present
    if not hasattr(config_class, 'SECRET_KEY') or not config_class.SECRET_KEY:
        env_secret = os.environ.get('SECRET_KEY')
        if not env_secret:
            raise ValueError("SECRET_KEY environment variable is not set")
        config_class.SECRET_KEY = env_secret
    app.config['SECRET_KEY'] = config_class.SECRET_KEY.encode('utf-8')
    # Initialize configuration
    config_class.init_app(app)
    
    # Ensure instance folder exists
    try:
        os.makedirs(app.instance_path)
    except OSError:
        pass
    
    # Configure logging
    if os.environ.get('HOST_ENV') != 'AWS':
        configure_logging(app)
    
    # Initialize extensions with retry logic
    max_retries = 3
    retry_delay = 5
    
    # Initialize extensions only if they haven't been initialized
    if not hasattr(app, 'extensions') or 'sqlalchemy' not in app.extensions:
        for attempt in range(max_retries):
            try:
                # Initialize SQLAlchemy only if not already initialized
                if 'sqlalchemy' not in app.extensions:
                    db.init_app(app)
                    migrate.init_app(app, db)
                
                # Initialize other extensions
                if 'csrf' not in app.extensions:
                    csrf.init_app(app)
                if 'limiter' not in app.extensions:
                    limiter.init_app(app)
                if 'cache' not in app.extensions:
                    cache.init_app(app)
                if 'mail' not in app.extensions:
                    mail.init_app(app)
                if 'login_manager' not in app.extensions:
                    login_manager.init_app(app)
                
                # Test database connection
                with app.app_context():
                    db.engine.connect()
                break
            except OperationalError as e:
                if attempt == max_retries - 1:
                    app.logger.error(f"Failed to connect to database after {max_retries} attempts: {str(e)}")
                    raise
                app.logger.warning(f"Database connection attempt {attempt + 1} failed: {str(e)}")
                sleep(retry_delay)
    
    # Configure login manager
    login_manager.login_view = 'auth.login'
    login_manager.login_message = 'Please log in to access this page.'
    login_manager.login_message_category = 'warning'
    
    @login_manager.user_loader
    def load_user(user_id):
        from .models import User
        return User.query.get(int(user_id))
    
    # Register blueprints
    from .routes import main, auth, admin
    app.register_blueprint(main.bp)
    app.register_blueprint(auth.bp)
    app.register_blueprint(admin.bp)
    
    # Register error handlers
    from .errors import register_error_handlers
    register_error_handlers(app)
    
    # Debug: log all registered endpoints at startup (warning level and flush)
    app.logger.warning("Registered endpoints (debug):")
    for rule in app.url_map.iter_rules():
        app.logger.warning(f"{rule.endpoint} {rule.methods} {rule.rule}")
        for handler in app.logger.handlers:
            handler.flush()
    
    # Debug: write all registered endpoints to a file
    # with open('logs/endpoints.log', 'w') as f:
    #    for rule in app.url_map.iter_rules():
    #        f.write(f"{rule.endpoint} {rule.methods} {rule.rule}\n")
    
    return app 