"""Fail-closed configuration for test and production sidecar migrations.

Alembic reads only purpose-specific variables. Generic database variables are
ignored so a deployment cannot accidentally migrate NocoDB or another service.
"""

from __future__ import annotations

import os
from dataclasses import dataclass, field
from typing import Final, Mapping

from sqlalchemy.engine import URL, make_url
from sqlalchemy.exc import ArgumentError

MIGRATION_ENVIRONMENT_VARIABLE: Final = "OSM_LEAD_SOURCE_MIGRATION_ENV"
TEST_DATABASE_URL_VARIABLE: Final = "OSM_LEAD_SOURCE_TEST_DATABASE_URL"
PRODUCTION_DATABASE_URL_VARIABLE: Final = "OSM_LEAD_SOURCE_PRODUCTION_DATABASE_URL"
PRODUCTION_APPROVAL_VARIABLE: Final = "OSM_LEAD_SOURCE_PRODUCTION_MIGRATION_APPROVAL"
PRODUCTION_APPROVAL_VALUE: Final = "apply-osm-lead-source-sidecar-v2"
PRODUCTION_DATABASE_NAME: Final = "osm_lead_source"
ALLOWED_TEST_HOSTS: Final = frozenset({"localhost", "127.0.0.1", "::1", "postgis"})


class MigrationSafetyError(ValueError):
    """Raised when a migration request does not target an approved sidecar."""


@dataclass(frozen=True, slots=True)
class MigrationSettings:
    """Validated, non-secret metadata plus a hidden SQLAlchemy URL."""

    database_url: str = field(repr=False)
    database_name: str
    host: str
    port: int
    environment: str


TestMigrationSettings = MigrationSettings
ProductionMigrationSettings = MigrationSettings


def _safe_error(environment: str, reason: str) -> MigrationSafetyError:
    return MigrationSafetyError(f"{environment}-sidecar migration rejected: {reason}")


def _parse_url(database_url: str, *, environment: str) -> URL:
    if not database_url.strip():
        raise _safe_error(environment, "database URL is missing")
    try:
        return make_url(database_url)
    except (ArgumentError, TypeError, ValueError):
        raise _safe_error(environment, "database URL is malformed") from None


def validate_test_migration_url(
    database_url: str, *, allow_passwordless: bool = False
) -> TestMigrationSettings:
    """Validate the disposable test database without exposing its URL."""

    parsed = _parse_url(database_url, environment="test")
    if parsed.drivername != "postgresql+psycopg":
        raise _safe_error("test", "URL scheme must be postgresql+psycopg")
    if parsed.host is None or parsed.host not in ALLOWED_TEST_HOSTS:
        raise _safe_error("test", "database host is not on the explicit test allowlist")
    if parsed.database is None or "test" not in parsed.database.lower():
        raise _safe_error("test", "database name must identify a test database")
    if parsed.username is None:
        raise _safe_error("test", "test database username is required")
    if parsed.password is None and not allow_passwordless:
        raise _safe_error("test", "test database password is required")
    if parsed.port is None:
        raise _safe_error("test", "test database port is required")
    return MigrationSettings(
        database_url=database_url,
        database_name=parsed.database,
        host=parsed.host,
        port=parsed.port,
        environment="test",
    )


def validate_production_migration_url(database_url: str) -> ProductionMigrationSettings:
    """Validate an isolated production sidecar, never an arbitrary database."""

    parsed = _parse_url(database_url, environment="production")
    if parsed.drivername != "postgresql+psycopg":
        raise _safe_error("production", "URL scheme must be postgresql+psycopg")
    if parsed.host is None or parsed.host in ALLOWED_TEST_HOSTS:
        raise _safe_error("production", "database host must be a non-test service host")
    if parsed.database != PRODUCTION_DATABASE_NAME:
        raise _safe_error(
            "production",
            f"database name must be exactly {PRODUCTION_DATABASE_NAME}",
        )
    if parsed.username is None:
        raise _safe_error("production", "production database username is required")
    if parsed.password is None:
        raise _safe_error("production", "production database password is required")
    if parsed.port is None:
        raise _safe_error("production", "production database port is required")
    return MigrationSettings(
        database_url=database_url,
        database_name=parsed.database,
        host=parsed.host,
        port=parsed.port,
        environment="production",
    )


def load_test_migration_settings(
    environ: Mapping[str, str] | None = None,
) -> TestMigrationSettings:
    """Load only explicit disposable test-sidecar variables."""

    source = os.environ if environ is None else environ
    if source.get(MIGRATION_ENVIRONMENT_VARIABLE) != "test":
        raise _safe_error("test", "migration environment must be exactly test")
    raw_url = source.get(TEST_DATABASE_URL_VARIABLE)
    if raw_url is None:
        raise _safe_error("test", "explicit test database URL is missing")
    return validate_test_migration_url(raw_url, allow_passwordless=True)


def load_production_migration_settings(
    environ: Mapping[str, str] | None = None,
) -> ProductionMigrationSettings:
    """Load an explicitly approved isolated production-sidecar target."""

    source = os.environ if environ is None else environ
    if source.get(MIGRATION_ENVIRONMENT_VARIABLE) != "production":
        raise _safe_error("production", "migration environment must be exactly production")
    if source.get(PRODUCTION_APPROVAL_VARIABLE) != PRODUCTION_APPROVAL_VALUE:
        raise _safe_error("production", "explicit migration approval is missing or invalid")
    raw_url = source.get(PRODUCTION_DATABASE_URL_VARIABLE)
    if raw_url is None:
        raise _safe_error("production", "explicit production database URL is missing")
    return validate_production_migration_url(raw_url)


def load_migration_settings(
    environ: Mapping[str, str] | None = None,
) -> MigrationSettings:
    """Dispatch only the exact test or production migration environment."""

    source = os.environ if environ is None else environ
    environment = source.get(MIGRATION_ENVIRONMENT_VARIABLE)
    if environment == "test":
        return load_test_migration_settings(source)
    if environment == "production":
        return load_production_migration_settings(source)
    raise _safe_error("sidecar", "migration environment must be exactly test or production")


__all__ = [
    "ALLOWED_TEST_HOSTS",
    "MIGRATION_ENVIRONMENT_VARIABLE",
    "MigrationSafetyError",
    "MigrationSettings",
    "PRODUCTION_APPROVAL_VALUE",
    "PRODUCTION_APPROVAL_VARIABLE",
    "PRODUCTION_DATABASE_NAME",
    "PRODUCTION_DATABASE_URL_VARIABLE",
    "ProductionMigrationSettings",
    "TEST_DATABASE_URL_VARIABLE",
    "TestMigrationSettings",
    "load_migration_settings",
    "load_production_migration_settings",
    "load_test_migration_settings",
    "validate_production_migration_url",
    "validate_test_migration_url",
]
