# import importlib.machinery
# import importlib.util
# import os
# import sys


# sys.path.insert(0, os.path.dirname(__file__))

# def load_source(modname, filename):
#     loader = importlib.machinery.SourceFileLoader(modname, filename)
#     spec = importlib.util.spec_from_file_location(modname, filename, loader=loader)
#     module = importlib.util.module_from_spec(spec)
#     loader.exec_module(module)
#     return module

# wsgi = load_source('wsgi', 'passenger_wsgi.py')
# application = wsgi.application
"""
Entry point for cPanel's "Setup Python App" (Phusion Passenger).

Passenger expects a WSGI callable named `application` in this file.
FastAPI is ASGI-based, so we wrap it with a2wsgi's ASGIMiddleware to
bridge the two — everything else in the app (routing, background
training threads, prediction endpoint, file uploads) works unmodified.

cPanel setup:
  - Application startup file : passenger_wsgi.py
  - Application Entry point  : application
  - Make sure `a2wsgi` is in requirements.txt and installed via
    cPanel's "Run Pip Install" (or `pip install -r requirements.txt`
    inside the app's virtualenv over SSH).

Unlike Render, Passenger does NOT use a $PORT env var — it manages the
process and routes HTTP through Apache/LiteSpeed itself, so run.py is
not used in this deployment path.
"""

# import sys
# import os

# # Make sure the project root is importable regardless of Passenger's cwd.
# BASE_DIR = os.path.dirname(os.path.abspath(__file__))
# sys.path.insert(0, BASE_DIR)

# from a2wsgi import ASGIMiddleware  # noqa: E402
# from app.main import app as fastapi_app  # noqa: E402
# from app.database import init_db, SessionLocal  # noqa: E402
# from app.services import model_service  # noqa: E402

# # IMPORTANT: Passenger/WSGI has no "lifespan" protocol, so FastAPI's
# # @app.on_event("startup") hook in app/main.py (which creates DB tables
# # and auto-deploys any committed model) never fires under this deployment
# # path. Run the same startup logic explicitly, once, at import time —
# # Passenger imports this module exactly once per worker process.
# init_db()
# _db = SessionLocal()
# try:
#     model_service.sync_committed_models(_db)
# finally:
#     _db.close()

# application = ASGIMiddleware(fastapi_app)
# import os

# # Batasi thread OpenBLAS
# os.environ["OPENBLAS_NUM_THREADS"] = "1"
# os.environ["OMP_NUM_THREADS"] = "1"
# os.environ["MKL_NUM_THREADS"] = "1"
# os.environ["NUMEXPR_NUM_THREADS"] = "1"

# # Batasi thread TensorFlow
# os.environ["TF_NUM_INTRAOP_THREADS"] = "1"
# os.environ["TF_NUM_INTEROP_THREADS"] = "1"

# # Nonaktifkan GPU
# os.environ["CUDA_VISIBLE_DEVICES"] = "-1"

# # Kurangi log TensorFlow
# os.environ["TF_CPP_MIN_LOG_LEVEL"] = "2"

# import sys

# PROJECT_PATH = "/home/rayb8274/public_html/dermascan.raylab.my.id"

# if PROJECT_PATH not in sys.path:
#     sys.path.insert(0, PROJECT_PATH)

# from a2wsgi import ASGIMiddleware
# from app.main import app as fastapi_app

# application = ASGIMiddleware(fastapi_app)
"""
Entry point for cPanel's "Setup Python App" (Phusion Passenger).

Passenger expects a WSGI callable named `application` in this file.
FastAPI is ASGI-based, so we wrap it with a2wsgi's ASGIMiddleware to
bridge the two — everything else in the app (routing, background
training threads, prediction endpoint, file uploads) works unmodified.

cPanel setup:
  - Application startup file : passenger_wsgi.py
  - Application Entry point  : application
  - Make sure `a2wsgi` is in requirements.txt and installed via
    cPanel's "Run Pip Install" (or `pip install -r requirements.txt`
    inside the app's virtualenv over SSH).

Unlike Render, Passenger does NOT use a $PORT env var — it manages the
process and routes HTTP through Apache/LiteSpeed itself, so run.py is
not used in this deployment path.
"""
"""
Entry point for cPanel's "Setup Python App" (Phusion Passenger).

Passenger expects a WSGI callable named `application` in this file.
FastAPI is ASGI-based, so we wrap it with a2wsgi's ASGIMiddleware to
bridge the two — everything else in the app (routing, background
training threads, prediction endpoint, file uploads) works unmodified.

cPanel setup:
  - Application startup file : passenger_wsgi.py
  - Application Entry point  : application
  - Make sure `a2wsgi` is in requirements.txt and installed via
    cPanel's "Run Pip Install" (or `pip install -r requirements.txt`
    inside the app's virtualenv over SSH).

Unlike Render, Passenger does NOT use a $PORT env var — it manages the
process and routes HTTP through Apache/LiteSpeed itself, so run.py is
not used in this deployment path.
"""
"""
Entry point for cPanel's "Setup Python App" (Phusion Passenger).

Passenger expects a WSGI callable named `application` in this file.
FastAPI is ASGI-based, so we wrap it with a2wsgi's ASGIMiddleware to
bridge the two — everything else in the app (routing, background
training threads, prediction endpoint, file uploads) works unmodified.

cPanel setup:
  - Application startup file : passenger_wsgi.py
  - Application Entry point  : application
  - Make sure `a2wsgi` is in requirements.txt and installed via
    cPanel's "Run Pip Install" (or `pip install -r requirements.txt`
    inside the app's virtualenv over SSH).

Unlike Render, Passenger does NOT use a $PORT env var — it manages the
process and routes HTTP through Apache/LiteSpeed itself, so run.py is
not used in this deployment path.
"""

import sys
import os

# IMPORTANT: set these BEFORE numpy/tensorflow are imported anywhere down
# the chain. By default OpenBLAS (used by numpy) spawns one thread per
# detected CPU core, which can blow past a constrained shared-hosting
# account's RLIMIT_NPROC (max processes/threads for the whole account) and
# get the worker killed mid-startup. Single-threaded BLAS is slightly
# slower per-call but starts fast and reliably on tight shared hosting.
os.environ.setdefault("OPENBLAS_NUM_THREADS", "1")
os.environ.setdefault("OMP_NUM_THREADS", "1")
os.environ.setdefault("MKL_NUM_THREADS", "1")
os.environ.setdefault("NUMEXPR_NUM_THREADS", "1")
os.environ.setdefault("TF_NUM_INTRAOP_THREADS", "1")
os.environ.setdefault("TF_NUM_INTEROP_THREADS", "1")

# This host has no GPU — skip TensorFlow's CUDA/GPU probing entirely so it
# doesn't waste startup time (or hang) on a doomed cuInit() call.
os.environ.setdefault("CUDA_VISIBLE_DEVICES", "-1")
os.environ.setdefault("TF_CPP_MIN_LOG_LEVEL", "3")

# Make sure the project root is importable regardless of Passenger's cwd.
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, BASE_DIR)

from a2wsgi import ASGIMiddleware  # noqa: E402
from app.main import app as fastapi_app  # noqa: E402
from app.database import init_db, SessionLocal  # noqa: E402
from app.services import model_service  # noqa: E402

# IMPORTANT: Passenger/WSGI has no "lifespan" protocol, so FastAPI's
# @app.on_event("startup") hook in app/main.py (which creates DB tables
# and auto-deploys any committed model) never fires under this deployment
# path. Run the same startup logic explicitly, once, at import time —
# Passenger imports this module exactly once per worker process.
init_db()
_db = SessionLocal()
try:
    model_service.sync_committed_models(_db)
finally:
    _db.close()

application = ASGIMiddleware(fastapi_app)

# CRITICAL FIX for shared hosting with a tight NPROC (max processes/threads
# per account) limit: FastAPI/Starlette dispatch every *synchronous* route
# handler (def, not async def) — and every sync dependency, including our
# get_db() — onto a background threadpool managed by anyio, so the request
# doesn't block the event loop. anyio's DEFAULT threadpool cap is 40
# threads, which can be exactly (or close to) this account's whole-account
# NPROC limit. Once enough requests/threads stack up, CloudLinux's LVE
# resource enforcer kills the worker process outright with SIGTERM — no
# Python traceback, just "Child process ... was killed by signal: 15" in
# the log, which is exactly what was being seen here.
#
# a2wsgi runs ONE persistent event loop for the lifetime of this worker
# process (see ASGIMiddleware.__init__), so this only needs to be set once,
# on that same loop, right here at import time.
import asyncio  # noqa: E402


async def _limit_thread_pool():
    from anyio import to_thread
    # Small but non-zero: this app has light concurrency needs on shared
    # hosting. Raise this later if you upgrade to a plan with a higher
    # NPROC limit and need more concurrent requests handled.
    to_thread.current_default_thread_limiter().total_tokens = 4


asyncio.run_coroutine_threadsafe(_limit_thread_pool(), application.loop).result()