# [DEF:backend.src.core.database:Module]
#
# @SEMANTICS: database, sqlite, sqlalchemy, session, persistence
# @PURPOSE:   Configures the SQLite database connection and session management.
# @LAYER:     Core
# @RELATION:  DEPENDS_ON -> sqlalchemy
# @RELATION:  USES -> backend.src.models.mapping
# @RELATION:  USES -> backend.src.core.auth.config
#
# @INVARIANT: A single engine instance is used for the entire application.

# [SECTION: IMPORTS]
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, Session
from ..models.mapping import Base
# Import models to ensure they're registered with Base
from ..models.task import TaskRecord
from ..models.connection import ConnectionConfig
from ..models.git import GitServerConfig, GitRepository, DeploymentEnvironment
from ..models.auth import User, Role, Permission, ADGroupMapping
from ..models.llm import LLMProvider, ValidationRecord
from .logger import belief_scope
from .auth.config import auth_config
import os
from pathlib import Path
# [/SECTION]

# [DEF:BASE_DIR:Variable]
# @PURPOSE: Base directory for the backend (where .db files should reside).
BASE_DIR = Path(__file__).resolve().parent.parent.parent
# [/DEF:BASE_DIR:Variable]

# [DEF:DATABASE_URL:Constant]
# @PURPOSE: URL for the main mappings database.
DATABASE_URL = os.getenv("DATABASE_URL", f"sqlite:///{BASE_DIR}/mappings.db")
# [/DEF:DATABASE_URL:Constant]

# [DEF:TASKS_DATABASE_URL:Constant]
# @PURPOSE: URL for the tasks execution database.
TASKS_DATABASE_URL = os.getenv("TASKS_DATABASE_URL", f"sqlite:///{BASE_DIR}/tasks.db")
# [/DEF:TASKS_DATABASE_URL:Constant]

# [DEF:AUTH_DATABASE_URL:Constant]
# @PURPOSE: URL for the authentication database.
AUTH_DATABASE_URL = os.getenv("AUTH_DATABASE_URL", auth_config.AUTH_DATABASE_URL)
# If it's a relative sqlite path starting with ./backend/, fix it to be absolute or relative to BASE_DIR
if AUTH_DATABASE_URL.startswith("sqlite:///./backend/"):
    AUTH_DATABASE_URL = AUTH_DATABASE_URL.replace("sqlite:///./backend/", f"sqlite:///{BASE_DIR}/")
elif AUTH_DATABASE_URL.startswith("sqlite:///./") and not AUTH_DATABASE_URL.startswith("sqlite:///./backend/"):
    # If it's just ./ but we are in backend, it's fine, but let's make it absolute for robustness
    AUTH_DATABASE_URL = AUTH_DATABASE_URL.replace("sqlite:///./", f"sqlite:///{BASE_DIR}/")
# [/DEF:AUTH_DATABASE_URL:Constant]

# [DEF:engine:Variable]
# @PURPOSE: SQLAlchemy engine for mappings database.
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
# [/DEF:engine:Variable]

# [DEF:tasks_engine:Variable]
# @PURPOSE: SQLAlchemy engine for tasks database.
tasks_engine = create_engine(TASKS_DATABASE_URL, connect_args={"check_same_thread": False})
# [/DEF:tasks_engine:Variable]

# [DEF:auth_engine:Variable]
# @PURPOSE: SQLAlchemy engine for authentication database.
auth_engine = create_engine(AUTH_DATABASE_URL, connect_args={"check_same_thread": False})
# [/DEF:auth_engine:Variable]

# [DEF:SessionLocal:Class]
# @PURPOSE: A session factory for the main mappings database.
# @PRE: engine is initialized.
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
# [/DEF:SessionLocal:Class]

# [DEF:TasksSessionLocal:Class]
# @PURPOSE: A session factory for the tasks execution database.
# @PRE: tasks_engine is initialized.
TasksSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=tasks_engine)
# [/DEF:TasksSessionLocal:Class]

# [DEF:AuthSessionLocal:Class]
# @PURPOSE: A session factory for the authentication database.
# @PRE: auth_engine is initialized.
AuthSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=auth_engine)
# [/DEF:AuthSessionLocal:Class]

# [DEF:init_db:Function]
# @PURPOSE: Initializes the database by creating all tables.
# @PRE: engine, tasks_engine and auth_engine are initialized.
# @POST: Database tables created in all databases.
# @SIDE_EFFECT: Creates physical database files if they don't exist.
def init_db():
    with belief_scope("init_db"):
        Base.metadata.create_all(bind=engine)
        Base.metadata.create_all(bind=tasks_engine)
        Base.metadata.create_all(bind=auth_engine)
# [/DEF:init_db:Function]

# [DEF:get_db:Function]
# @PURPOSE: Dependency for getting a database session.
# @PRE: SessionLocal is initialized.
# @POST:    Session is closed after use.
# @RETURN:  Generator[Session, None, None]
def get_db():
    with belief_scope("get_db"):
        db = SessionLocal()
        try:
            yield db
        finally:
            db.close()
# [/DEF:get_db:Function]

# [DEF:get_tasks_db:Function]
# @PURPOSE: Dependency for getting a tasks database session.
# @PRE: TasksSessionLocal is initialized.
# @POST:    Session is closed after use.
# @RETURN:  Generator[Session, None, None]
def get_tasks_db():
    with belief_scope("get_tasks_db"):
        db = TasksSessionLocal()
        try:
            yield db
        finally:
            db.close()
# [/DEF:get_tasks_db:Function]

# [DEF:get_auth_db:Function]
# @PURPOSE: Dependency for getting an authentication database session.
# @PRE: AuthSessionLocal is initialized.
# @POST:    Session is closed after use.
# @RETURN:  Generator[Session, None, None]
def get_auth_db():
    with belief_scope("get_auth_db"):
        db = AuthSessionLocal()
        try:
            yield db
        finally:
            db.close()
# [/DEF:get_auth_db:Function]

# [/DEF:backend.src.core.database:Module]
