# ============================================================================
# Oderith Dental — Aplicación Principal (Flask)
# ============================================================================
# Punto de entrada de la aplicación. Crea la instancia de Flask,
# registra blueprints, inicializa extensiones y define error handlers.
#
# Incluye soporte para:
#   - SuperAdmin: gestión de clínicas y planes.
#   - Middleware de verificación de clínica activa.
#   - Inicialización automática del superadmin desde variables de entorno.
# ============================================================================

import os
from flask import Flask, jsonify, render_template, send_from_directory, request
from flask_cors import CORS

from config import get_config
from models import db, SuperAdmin, Doctor, Assistant, Clinic
from routes.auth import auth_bp, login_manager


def create_app(config_class=None):
    """
    Factory de la aplicación Flask.
    Usa el patrón Application Factory para permitir múltiples instancias
    y facilitar las pruebas.
    """
    app = Flask(
        __name__,
        static_folder='static',
        static_url_path='/static',
        template_folder='templates'
    )

    # --- Cargar configuración ---
    if config_class is None:
        config_class = get_config()
    app.config.from_object(config_class)

    # --- Inicializar extensiones ---
    _init_extensions(app)

    # --- Registrar blueprints (rutas) ---
    _register_blueprints(app)

    # --- Registrar error handlers ---
    _register_error_handlers(app)

    # --- Crear tablas en la base de datos ---
    _create_tables(app)

    # --- Inicializar SuperAdmin desde variables de entorno ---
    _init_superadmin(app)

    # --- Registrar middleware de verificación de clínica ---
    _register_clinic_middleware(app)

    # --- Ruta principal ---
    _register_main_routes(app)

    return app


def _init_extensions(app):
    """Inicializa todas las extensiones de Flask."""

    # Base de datos (SQLAlchemy)
    db.init_app(app)

    # Autenticación (Flask-Login)
    login_manager.init_app(app)
    login_manager.session_protection = 'strong'

    # CORS — Permitir peticiones desde el frontend en desarrollo
    cors_origins = '*' if app.debug else os.environ.get('CORS_ORIGINS', '*')
    CORS(app, supports_credentials=True, origins=cors_origins)


def _register_blueprints(app):
    """Registra todos los blueprints (módulos de rutas) de la aplicación."""

    # Autenticación
    app.register_blueprint(auth_bp)

    # Super Administración
    from routes.superadmin import admin_bp
    app.register_blueprint(admin_bp)

    # Pacientes
    from routes.patients import patients_bp
    app.register_blueprint(patients_bp)

    # Citas
    from routes.appointments import appointments_bp
    app.register_blueprint(appointments_bp)

    # Presupuestos
    from routes.budgets import budgets_bp
    app.register_blueprint(budgets_bp)

    # Cuentas por cobrar
    from routes.accounts import accounts_bp
    app.register_blueprint(accounts_bp)

    # Odontodiagrama
    from routes.odontogram import odontogram_bp
    app.register_blueprint(odontogram_bp)

    # Asistentes
    from routes.assistants import assistants_bp
    app.register_blueprint(assistants_bp)

    # WhatsApp
    from routes.whatsapp import whatsapp_bp
    app.register_blueprint(whatsapp_bp)

    # Dashboard
    from routes.dashboard import dashboard_bp
    app.register_blueprint(dashboard_bp)

    # Radiografías / Imágenes de pacientes
    from routes.patient_images import images_bp
    app.register_blueprint(images_bp)


def _register_error_handlers(app):
    """Registra manejadores de errores globales que retornan JSON."""

    @app.errorhandler(400)
    def bad_request(error):
        return jsonify({
            'success': False,
            'message': 'Solicitud incorrecta.',
            'error': str(error)
        }), 400

    @app.errorhandler(404)
    def not_found(error):
        return jsonify({
            'success': False,
            'message': 'Recurso no encontrado.',
            'error': 'La ruta o recurso solicitado no existe.'
        }), 404

    @app.errorhandler(405)
    def method_not_allowed(error):
        return jsonify({
            'success': False,
            'message': 'Método HTTP no permitido.',
            'error': str(error)
        }), 405

    @app.errorhandler(500)
    def internal_error(error):
        db.session.rollback()
        return jsonify({
            'success': False,
            'message': 'Error interno del servidor.',
            'error': 'Ocurrió un error inesperado. Intenta de nuevo más tarde.'
        }), 500


def _create_tables(app):
    """Crea todas las tablas en la base de datos si no existen."""
    with app.app_context():
        db.create_all()


def _init_superadmin(app):
    """
    Inicializa el SuperAdmin desde variables de entorno.
    Lee SUPERADMIN_USERNAME y SUPERADMIN_PASSWORD del .env.
    Si no existe el superadmin en la BD, lo crea con las credenciales hasheadas.
    Se ejecuta automáticamente al crear la aplicación.
    """
    with app.app_context():
        username = os.environ.get('SUPERADMIN_USERNAME')
        password = os.environ.get('SUPERADMIN_PASSWORD')

        if not username or not password:
            # No se configuraron credenciales — saltar silenciosamente
            return

        existing = SuperAdmin.query.filter_by(username=username).first()
        if not existing:
            admin = SuperAdmin(
                username=username,
                name='Super Administrador',
                email=os.environ.get('SUPERADMIN_EMAIL', ''),
            )
            admin.set_password(password)

            try:
                db.session.add(admin)
                db.session.commit()
                print(f'  ✓ SuperAdmin "{username}" creado exitosamente.')
            except Exception as e:
                db.session.rollback()
                print(f'  ✗ Error al crear SuperAdmin: {e}')


def _register_clinic_middleware(app):
    """
    Middleware que verifica la clínica activa para rutas /api/*.
    - Para rutas /admin/* → no aplica (son del superadmin).
    - Para rutas /api/* → verifica que la clínica del doctor/asistente esté activa.
    - Si la clínica está vencida → auto-desactiva y retorna 403.
    """
    from flask_login import current_user

    @app.before_request
    def check_clinic_status():
        path = request.path

        # Solo verificar rutas /api/* que no sean /admin/*
        if not path.startswith('/api/'):
            return None

        # No bloquear rutas de autenticación (login, register, health)
        if path.startswith('/api/auth/login') or path.startswith('/api/auth/register'):
            return None
        if path.startswith('/api/health'):
            return None

        # Solo verificar usuarios autenticados
        if not current_user.is_authenticated:
            return None

        # Los superadmins no tienen restricción de clínica
        if isinstance(current_user, SuperAdmin):
            return None

        # Obtener la clínica del usuario
        clinic = None

        if isinstance(current_user, Doctor):
            clinic = current_user.clinic
        elif isinstance(current_user, Assistant):
            if current_user.doctor:
                clinic = current_user.doctor.clinic

        # Si el doctor/asistente no tiene clínica asignada, permitir acceso
        # (compatibilidad con doctores existentes antes de la migración)
        if clinic is None:
            return None

        # Verificar si la clínica está activa y no vencida
        if not clinic.active:
            return jsonify({
                'success': False,
                'message': 'Su clínica está desactivada. Contacte al administrador.',
                'error_code': 'CLINIC_INACTIVE'
            }), 403

        if clinic.is_expired():
            # Auto-desactivar la clínica vencida
            clinic.active = False
            try:
                db.session.commit()
            except Exception:
                db.session.rollback()

            return jsonify({
                'success': False,
                'message': 'El plan de su clínica ha vencido. Contacte al administrador para renovar.',
                'error_code': 'CLINIC_EXPIRED'
            }), 403

        return None


def _register_main_routes(app):
    """Registra las rutas principales de la aplicación."""

    @app.route('/')
    def index():
        """Ruta principal — sirve el frontend SPA."""
        return render_template('index.html')

    @app.route('/admin')
    def admin_panel():
        """Ruta del panel de administración — sirve admin.html."""
        return render_template('admin.html')

    @app.route('/uploads/<filename>')
    def uploaded_file(filename):
        upload_folder = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'uploads')
        return send_from_directory(upload_folder, filename)

    @app.route('/admin/api/uploads/<filename>')
    def admin_uploaded_file(filename):
        """Sirve archivos subidos (logos de clínicas, etc.) desde el panel admin."""
        upload_folder = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'uploads')
        return send_from_directory(upload_folder, filename)

    @app.route('/api/health')
    def health_check():
        """Endpoint de salud para verificar que el servidor está activo."""
        return jsonify({
            'success': True,
            'status': 'ok',
            'app': app.config.get('APP_NAME', 'Oderith Dental'),
            'version': app.config.get('APP_VERSION', '1.0.0'),
        }), 200


# ============================================================================
# Ejecución directa (desarrollo local)
# ============================================================================

if __name__ == '__main__':
    app = create_app()
    print("""
    ╔══════════════════════════════════════════════╗
    ║   🦷  ODERITH DENTAL — Servidor Activo       ║
    ║                                              ║
    ║   Abre en tu navegador:                      ║
    ║   http://localhost:5000                       ║
    ║   http://localhost:5000/admin  (Admin Panel)  ║
    ║                                              ║
    ║   Presiona Ctrl+C para detener               ║
    ╚══════════════════════════════════════════════╝
    """)
    app.run(
        host='0.0.0.0',
        port=5000,
        debug=True
    )
