"""
Test Database Configuration.

Dataclasses for PostgreSQL and Neo4j test container configuration.
"""

import hashlib
import json
import re
from dataclasses import dataclass, field
from typing import Dict, List


@dataclass
class TestDatabaseConfig:
    """Configuration for PostgreSQL test database containers."""

    name: str
    memory_limit: str = "512m"
    max_connections: int = 100
    shared_buffers: str = "128MB"
    enable_query_logging: bool = False
    port: int = 0  # 0 = auto-assign
    postgres_version: str = "17"
    pgvector_version: str = "0.8.1"
    connection_timeout: int = 30

    def validate(self) -> List[str]:
        """Validate configuration. Returns list of error messages."""
        errors = []
        if not self.name or not re.match(r"^[a-zA-Z0-9_-]+$", self.name):
            errors.append(f"Invalid name: {self.name!r} — must be alphanumeric/dash/underscore.")
        if self.port != 0 and not (1024 <= self.port <= 65535):
            errors.append(f"Port must be 0 (auto) or 1024-65535, got {self.port}.")
        if self.max_connections < 1:
            errors.append("max_connections must be >= 1.")
        return errors

    def get_postgres_image(self) -> str:
        return f"pgvector/pgvector:{self.pgvector_version}-pg{self.postgres_version}-trixie"

    def get_container_name(self) -> str:
        return f"test-postgres-{self.name}"

    def get_database_name(self) -> str:
        return f"test_{self.name}"

    def get_cache_key(self) -> str:
        data = json.dumps(
            {"name": self.name, "pg": self.postgres_version, "pgv": self.pgvector_version},
            sort_keys=True,
        )
        return hashlib.sha256(data.encode()).hexdigest()[:16]

    def get_postgres_command_args(self) -> List[str]:
        """Return PostgreSQL command args optimised for testing (fsync=off etc.).

        Each `-c key=value` must be passed as two separate exec args; the
        Postgres image's entrypoint passes argv straight through, so a glued
        ``"-c key=value"`` arg is parsed by postgres as the parameter name
        ``" key"`` (with a leading space) and rejected.
        """
        args = ["postgres"]
        params = [
            f"max_connections={self.max_connections}",
            f"shared_buffers={self.shared_buffers}",
            "fsync=off",
            "synchronous_commit=off",
            "full_page_writes=off",
        ]
        if self.enable_query_logging:
            params += [
                "log_min_duration_statement=0",
                "log_statement=all",
            ]
        for p in params:
            args += ["-c", p]
        return args


@dataclass
class TestNeo4jConfig:
    """Configuration for Neo4j test containers."""

    name: str
    memory_limit: str = "1g"
    port_bolt: int = 0  # 0 = auto-assign
    port_http: int = 0
    neo4j_version: str = "5"
    auth_enabled: bool = False
    connection_timeout: int = 60
    apoc_enabled: bool = True
    plugins: List[str] = field(default_factory=lambda: ["apoc"])

    def validate(self) -> List[str]:
        errors = []
        if not self.name or not re.match(r"^[a-zA-Z0-9_-]+$", self.name):
            errors.append(f"Invalid name: {self.name!r}")
        for label, port in [("bolt", self.port_bolt), ("http", self.port_http)]:
            if port != 0 and not (1024 <= port <= 65535):
                errors.append(f"{label} port must be 0 (auto) or 1024-65535, got {port}.")
        return errors

    def get_neo4j_image(self) -> str:
        return f"neo4j:{self.neo4j_version}"

    def get_container_name(self) -> str:
        return f"test-neo4j-{self.name}"

    def get_env_vars(self) -> Dict[str, str]:
        env = {
            "NEO4J_AUTH": "none" if not self.auth_enabled else "neo4j/testpassword",
            "NEO4J_dbms_memory_heap_initial__size": "256m",
            "NEO4J_dbms_memory_heap_max__size": "512m",
            "NEO4J_dbms_security_procedures_unrestricted": "apoc.*",
        }
        if self.plugins:
            env["NEO4J_PLUGINS"] = json.dumps(self.plugins)
        return env
