import os
from functools import lru_cache
from typing import Optional
from urllib.parse import parse_qsl, urlencode, urlsplit, urlunsplit

from sqlalchemy import create_engine
from sqlalchemy.engine import Engine


def _default_db_path() -> str:
    env_path = os.getenv("SYNTHNEURA_DB_PATH")
    if env_path:
        return env_path
    repo_root = os.path.abspath(
        os.path.join(os.path.dirname(__file__), "..", "..", "..")
    )
    default_path = os.path.join(repo_root, "trials.db")
    if os.access(repo_root, os.W_OK):
        return default_path
    # Render and similar platforms may run app code from a read-only directory.
    return "/tmp/trials.db"


def resolve_db_url(db_url: Optional[str] = None) -> str:
    """
    Resolve a database URL for SQLAlchemy.

    If SYNTHNEURA_DB_URL is set, use it. Otherwise, fall back to a local
    SQLite file path (SYNTHNEURA_DB_PATH or trials.db).
    """

    env_url = os.getenv("SYNTHNEURA_DB_URL")
    if env_url:
        return _normalize_db_url(env_url)
    if db_url and "://" in db_url:
        return _normalize_db_url(db_url)
    path = db_url or _default_db_path()
    if "://" in path:
        return _normalize_db_url(path)
    abs_path = os.path.abspath(path)
    return f"sqlite:///{abs_path}"


def _normalize_db_url(db_url: str) -> str:
    normalized = db_url.strip()
    if normalized.startswith("postgres://"):
        normalized = "postgresql://" + normalized[len("postgres://") :]
    if not normalized.startswith("postgresql://"):
        return normalized
    if "sslmode=" in normalized.lower():
        return normalized

    # Render-hosted Postgres endpoints usually require TLS.
    render_env = os.getenv("RENDER") or os.getenv("RENDER_SERVICE_ID")
    if not render_env:
        return normalized

    parts = urlsplit(normalized)
    query = dict(parse_qsl(parts.query, keep_blank_values=True))
    query["sslmode"] = "require"
    return urlunsplit(
        (parts.scheme, parts.netloc, parts.path, urlencode(query), parts.fragment)
    )


@lru_cache(maxsize=8)
def _engine_for(db_url: str) -> Engine:
    connect_args = {}
    if db_url.startswith("sqlite"):
        connect_args = {"check_same_thread": False}
    return create_engine(
        db_url, future=True, pool_pre_ping=True, connect_args=connect_args
    )


def get_engine(db_url: Optional[str] = None) -> Engine:
    """
    Return a cached SQLAlchemy engine for the resolved database URL.
    """

    resolved = resolve_db_url(db_url)
    return _engine_for(resolved)
