"""
Makes the per-company databases (created by core.tenant_db) actually get
used. Without this file, Django has no way to know that Member,
Expense, Material, etc. should be written to a *different* physical
database per company — it would silently keep using 'default' for
everything, even though a dedicated database + MySQL user were created
for each company.

CONTROL data (accounts, core.Project/ProjectApplication/ProjectMembership/
AuditLog, Django's own admin/auth/sessions/contenttypes) always lives in
'default' — this is shared platform data the Application Owner and the
login system need to reach in one place.

TENANT data (members, contributions, expenses, materials, labor,
contractors, flats, sales, finance) is routed to the current company's
own database, based on a thread-local set by TenantRoutingMiddleware.
"""
import threading

from django.conf import settings
from django.db import connections

CONTROL_APPS = {
    "accounts", "core", "admin", "auth", "contenttypes", "sessions",
    "reports", "dashboard",
}
TENANT_APPS = {
    "members", "contributions", "expenses", "materials",
    "labor", "contractors", "flats", "sales", "finance",
}

_thread_locals = threading.local()


def get_current_tenant_alias():
    return getattr(_thread_locals, "tenant_alias", None)


def set_current_tenant_alias(alias):
    _thread_locals.tenant_alias = alias


def clear_current_tenant_alias():
    _thread_locals.tenant_alias = None


def ensure_tenant_connection(project):
    """Register (if not already registered) a live Django database
    connection for this company, using ITS OWN dedicated MySQL
    user/password/database (decrypted on the fly — never stored or
    logged in plaintext). Cheap to call every request: Django doesn't
    actually open the socket until a query runs against this alias."""
    alias = project.database_key
    if not alias:
        return None
    if alias in connections.databases:
        return alias

    if settings.DJANGO_DB_ENGINE == "sqlite":
        # Local dev/testing fallback with no real MySQL — one file per
        # company, same schema semantics, so the whole flow can still be
        # exercised without a MySQL server.
        connections.databases[alias] = {
            "ENGINE": "django.db.backends.sqlite3",
            "NAME": str(settings.BASE_DIR / "tenant_dbs" / f"{project.database_name or alias}.sqlite3"),
        }
    else:
        if project.database_status != "provisioned" or not project.database_name:
            return None

        connections.databases[alias] = {
            "ENGINE": "django.db.backends.mysql",
            "NAME": project.database_name,
            "USER": project.database_user,
            "PASSWORD": project.database_password,
            "HOST": project.database_host,
            "PORT": str(project.database_port),
            "OPTIONS": {
                "charset": "utf8mb4",
                "init_command": "SET sql_mode='STRICT_TRANS_TABLES'",
            },
        }

    # Manually-inserted connection dicts skip Django's normal settings
    # processing, which silently leaves out keys the DB backend requires
    # at connect time. Fill in the same defaults Django applies to every
    # alias in DATABASES (see django.db.utils.ConnectionHandler) — done
    # by hand rather than via a private API, since that API isn't public
    # across all supported Django versions.
    conn = connections.databases[alias]
    conn.setdefault("ATOMIC_REQUESTS", False)
    conn.setdefault("AUTOCOMMIT", True)
    conn.setdefault("CONN_MAX_AGE", 0)
    conn.setdefault("CONN_HEALTH_CHECKS", False)
    conn.setdefault("OPTIONS", {})
    conn.setdefault("TIME_ZONE", None)
    for key in ("NAME", "USER", "PASSWORD", "HOST", "PORT"):
        conn.setdefault(key, "")
    test_settings = conn.setdefault("TEST", {})
    for key, default in (("CHARSET", None), ("COLLATION", None), ("MIGRATE", True), ("MIRROR", None), ("NAME", None)):
        test_settings.setdefault(key, default)
    return alias


class TenantRouter:
    """Routes tenant-app models to the current company's database, and
    keeps platform/control data on 'default'."""

    def db_for_read(self, model, **hints):
        if model._meta.app_label in TENANT_APPS:
            return get_current_tenant_alias()
        return None

    def db_for_write(self, model, **hints):
        if model._meta.app_label in TENANT_APPS:
            return get_current_tenant_alias()
        return None

    def allow_relation(self, obj1, obj2, **hints):
        # Tenant models reference control models (Project, CustomUser) by
        # id only (db_constraint=False) since there's no real cross-db FK
        # for Django to validate — always allow.
        return True

    def allow_migrate(self, db, app_label, model_name=None, **hints):
        if app_label in TENANT_APPS:
            return db != "default"
        return db == "default"
