# #region TestSessionConfig [C:2] [TYPE Module] [SEMANTICS test, conftest, db, postgres, testcontainers]
# @BRIEF Shared pytest fixtures and session configuration for backend tests.
# @RELATION BINDS_TO -> [init_db]
# @PRE All test modules use sys.path patching to import from src/.
# @POST Database tables exist before test session executes.
# @RATIONALE
#   Architecture:
#   - Global DATABASE_URL → temp-file SQLite (for modules importing src.core.database)
#   - Per-module engines → sqlite:///:memory: (for modules with local create_engine)
#   - FK enforcement on ALL engines via PRAGMA foreign_keys=ON
#
#   Why:
#   - In-process SQLite is 5-10x faster than Docker PostgreSQL
#   - FK enforcement catches constraint violations (unlike plain SQLite)
#   - Per-module engines prevent 10GB+ shared-cache memory leak
#   - Temp file for global engine also prevents memory leak
#
#   PostgreSQL mode (TEST_DB=postgres) → testcontainers or explicit DATABASE_URL.
#
# @REJECTED
#   sqlite:///file::memory:...?cache=shared — 10GB+ memory leak (all modules share one DB).
#   Plain SQLite without FK — silently ignores FK violations, masks bugs.
#   Always-on PostgresContainer — 5-10x slower, Docker dependency.
import os
from pathlib import Path
import sys
import tempfile

from sqlalchemy import create_engine, event

sys.path.insert(0, str(Path(__file__).parent.parent / "src"))

import pytest

os.environ.setdefault("SECRET_KEY", "test-secret-key-for-testing")

# ── Global engine: temp-file SQLite (not in-memory, not shared cache) ──
# This prevents the 10GB memory leak from shared in-memory databases.

_TEST_DB_FILE = tempfile.NamedTemporaryFile(suffix="_ss_tools.db", delete=False)
_TEST_DB_PATH = _TEST_DB_FILE.name
_TEST_DB_FILE.close()

os.environ.setdefault("DATABASE_URL", f"sqlite:///{_TEST_DB_PATH}")
os.environ.setdefault("AUTH_DATABASE_URL", f"sqlite:///{_TEST_DB_PATH}")

# ── FK enforcement on the global engine ──

from src.core.database import engine

event.listen(engine, "connect", lambda c, _: c.execute("PRAGMA foreign_keys=ON"))


# ── Helper: FK-enabled SQLite engine for per-module isolation ──

def make_fk_engine():
    """Create a per-module SQLite in-memory engine with FK enforcement.

    Each test module calls this once at module level to get its own
    isolated database. No shared state → no memory leak.
    """
    eng = create_engine("sqlite:///:memory:", connect_args={"check_same_thread": False})
    event.listen(eng, "connect", lambda c, _: c.execute("PRAGMA foreign_keys=ON"))
    return eng


# ── CLI options ──

def pytest_addoption(parser):
    parser.addoption(
        "--run-integration",
        action="store_true",
        default=False,
        help="Run integration tests (require Docker, Testcontainers, Superset container)",
    )


# ── Monkey-patch pathlib.Path.mkdir for /app paths that don't exist in test env ──
# Tests run without /app directory (production path). When a test triggers
# GitService init, _ensure_base_path_exists calls mkdir('/app/storage/...')
# which fails. Intercept silently for /app paths only.
import pathlib as _pathlib
_ORIG_MKDIR = _pathlib.Path.mkdir

def _safe_mkdir(self, mode=0o777, parents=False, exist_ok=False):
    p = str(self)
    if p.startswith("/app"):
        return  # /app doesn't exist in test env — silently accept
    return _ORIG_MKDIR(self, mode, parents=parents, exist_ok=exist_ok)

_pathlib.Path.mkdir = _safe_mkdir


# ── Test session lifecycle ──

def pytest_configure(config):
    print(
        f"\n[conftest] SQLite (global: {_TEST_DB_PATH}) + FK enforcement",
        file=sys.stderr,
    )
    print(
        "  Integration tests skipped by default. Use --run-integration to enable.\n",
        file=sys.stderr,
    )


def pytest_unconfigure(config):
    try:
        from src.core.database import engine as _global_engine
        from src.core.database import tasks_engine as _tasks_engine
        from src.core.database import auth_engine as _auth_engine
        _global_engine.dispose()
        _tasks_engine.dispose()
        _auth_engine.dispose()
    except Exception:
        pass
    try:
        os.unlink(_TEST_DB_PATH)
    except Exception:
        pass


@pytest.fixture(scope="session", autouse=True)
def ensure_db_tables():
    """Create all tables on the global database."""
    try:
        from src.core.database import init_db
        init_db()
    except Exception as exc:
        print(f"\n[conftest] init_db failed: {exc}", file=sys.stderr)
        raise


@pytest.fixture(scope="session", autouse=True)
def ensure_git_storage_dirs(ensure_db_tables):
    """Patch StorageConfig default to a writable temp dir for tests.
    
    The global pathlib.Path.mkdir monkey-patch (above) handles /app paths
    that don't exist in the test environment. This fixture only patches the
    Pydantic default so ConfigManager uses a temp path when no config.json exists.
    """
    import os
    import tempfile
    from src.models.storage import StorageConfig
    
    test_root = tempfile.mkdtemp(prefix="ss_tools_test_storage_")
    test_repos = os.path.join(test_root, "repositories")
    os.makedirs(test_repos, exist_ok=True)
    
    StorageConfig.model_fields["root_path"].default = test_root
    StorageConfig.model_fields["repo_path"].default = "repositories"
    
    print(f"\n[conftest] Storage root_path default={test_root}", file=sys.stderr)


# #endregion TestSessionConfig
