import os
from datetime import datetime
from flask import Flask, render_template
from flask_login import LoginManager
from flask_bcrypt import Bcrypt
from flask_session import Session
from flask_caching import Cache
from flask_wtf.csrf import CSRFProtect

from config import config

# Inicialización de extensiones
login_manager = LoginManager()
login_manager.login_view = "auth.login"
login_manager.login_message = "Por favor inicie sesión para acceder a esta página."
login_manager.login_message_category = "warning"

bcrypt = Bcrypt()
csrf = CSRFProtect()
session = Session()
cache = Cache()


def create_app(config_name=None):
    """Fábrica de aplicación Flask."""
    if config_name is None:
        config_name = os.environ.get("FLASK_ENV", "default")

    app = Flask(__name__)
    app.config.from_object(config[config_name])
    config[config_name].init_app(app)

    # Inicializar extensiones
    login_manager.init_app(app)
    bcrypt.init_app(app)
    csrf.init_app(app)
    session.init_app(app)
    cache.init_app(app)

    # Importar y registrar blueprints
    from routes.main_routes import main_bp
    from routes.auth_routes import auth_bp
    from routes.user_routes import users_bp
    from routes.project_routes import projects_bp
    from routes.document_routes import documents_bp
    from routes.schema_routes import schemas_bp
    from routes.admin_routes import admin_bp

    app.register_blueprint(main_bp)
    app.register_blueprint(auth_bp)
    app.register_blueprint(users_bp)
    app.register_blueprint(projects_bp)
    app.register_blueprint(documents_bp)
    app.register_blueprint(schemas_bp)
    app.register_blueprint(admin_bp)

    # Configurar cargador de usuario para Flask-Login
    from services.user_service import get_user_by_id

    @login_manager.user_loader
    def load_user(user_id):
        return get_user_by_id(user_id)

    # Registrar handlers para errores
    register_error_handlers(app)

    # Context processor para variables globales en templates
    @app.context_processor
    def inject_globals():
        return {"now": datetime.now()}

    # Asegurar que existen los directorios de almacenamiento
    initialize_storage_structure(app)

    # Configurar sistema de logging
    from utils.logger import setup_logger

    setup_logger(app)

    # Inicializar datos por defecto
    with app.app_context():
        # Inicializar usuario administrador si no existe
        from services.auth_service import initialize_admin_user

        initialize_admin_user()

        # Inicializar esquemas predeterminados si no existen
        from services.schema_service import initialize_default_schemas

        initialize_default_schemas()

    return app


def register_error_handlers(app):
    """Registrar manejadores de errores para la aplicación."""

    @app.errorhandler(404)
    def page_not_found(e):
        return (
            render_template(
                "error.html", error_code=404, error_message="Página no encontrada"
            ),
            404,
        )

    @app.errorhandler(403)
    def forbidden(e):
        return (
            render_template(
                "error.html", error_code=403, error_message="Acceso denegado"
            ),
            403,
        )

    @app.errorhandler(500)
    def internal_error(e):
        return (
            render_template(
                "error.html", error_code=500, error_message="Error interno del servidor"
            ),
            500,
        )


def initialize_storage_structure(app):
    """Crear estructura de almacenamiento si no existe."""
    storage_path = app.config["STORAGE_PATH"]

    # Crear directorios principales
    os.makedirs(os.path.join(storage_path, "logs"), exist_ok=True)
    os.makedirs(os.path.join(storage_path, "schemas"), exist_ok=True)
    os.makedirs(os.path.join(storage_path, "users"), exist_ok=True)
    os.makedirs(os.path.join(storage_path, "filetypes"), exist_ok=True)
    os.makedirs(os.path.join(storage_path, "projects"), exist_ok=True)
    os.makedirs(os.path.join(storage_path, "exports"), exist_ok=True)

    # Inicializar archivos JSON si no existen
    init_json_file(os.path.join(storage_path, "schemas", "schema.json"), {})
    init_json_file(os.path.join(storage_path, "users", "users.json"), {})
    init_json_file(os.path.join(storage_path, "filetypes", "filetypes.json"), {})
    init_json_file(
        os.path.join(storage_path, "indices.json"),
        {"max_project_id": 0, "max_document_id": 0},
    )


def init_json_file(file_path, default_content):
    """Inicializar un archivo JSON si no existe."""
    if not os.path.exists(file_path):
        import json

        with open(file_path, "w", encoding="utf-8") as f:
            json.dump(default_content, f, ensure_ascii=False, indent=2)


if __name__ == "__main__":
    app = create_app()
    app.run(debug=True)