"""
TrustMemConfig: Centralized configuration from environment variables.
"""
from __future__ import annotations

import logging
import os
from dataclasses import dataclass, field

logger = logging.getLogger(__name__)


@dataclass
class ConfigIssue:
    """A single configuration validation issue."""

    level: str  # "error" or "warning"
    field: str  # config field name (e.g. "openai_api_key")
    message: str  # human-readable description

    def __str__(self) -> str:
        return f"{self.level.upper()}: {self.message}"


def _safe_int(value: str, name: str, default: int) -> int:
    """Parse an integer from string, returning default on failure."""
    try:
        return int(value)
    except (ValueError, TypeError):
        logger.warning("Invalid integer for %s: %r, using default %d", name, value, default)
        return default


@dataclass
class TrustMemConfig:
    """All TrustMem configuration in one place."""

    # Handler mode
    handler_mode: str = "persistent"

    # VectorStore
    vector_store: str = "sqlite"  # "sqlite" | "qdrant"
    qdrant_url: str = "http://localhost:6333"
    qdrant_api_key: str = ""
    qdrant_collection: str = "trustmem"
    embed_dim: int = 1536  # Must match embedding model output dimension

    # AI Client
    embed_provider: str = "openai"  # "openai" | "azure" | "openai_compat"
    llm_provider: str = "openai"
    openai_api_key: str = ""
    openai_base_url: str = ""
    embed_model: str = "text-embedding-3-small"
    llm_model: str = "gpt-4o-mini"
    azure_openai_endpoint: str = ""
    azure_openai_api_key: str = ""
    azure_api_version: str = "2024-02-01"

    # GraphStore
    graph_store: str = "sqlite"  # "sqlite" | "neo4j"
    neo4j_uri: str = "bolt://localhost:7687"
    neo4j_user: str = "neo4j"
    neo4j_password: str = ""
    neo4j_database: str = "neo4j"

    # Database
    database_url: str = ""

    # Read Audit
    read_audit: bool = False  # TRUSTMEM_READ_AUDIT — opt-in access logging

    # Server
    host: str = "0.0.0.0"
    port: int = 8080
    api_key: str = ""
    cors_origins: list[str] = field(default_factory=lambda: [
        "http://localhost:8080", "http://localhost:3000",
        "http://127.0.0.1:8080", "http://127.0.0.1:3000",
    ])

    _SECRET_FIELDS = frozenset({
        "openai_api_key", "azure_openai_api_key", "qdrant_api_key",
        "neo4j_password", "api_key",
    })

    def __repr__(self) -> str:
        fields = []
        for f in self.__dataclass_fields__:
            val = getattr(self, f)
            if f in self._SECRET_FIELDS and val:
                val = val[:4] + "***"
            fields.append(f"{f}={val!r}")
        return f"TrustMemConfig({', '.join(fields)})"

    def validate(self) -> list[ConfigIssue]:
        """Return list of validation issues. Empty means OK.

        Each issue is a ``ConfigIssue`` with ``level`` ('error'/'warning'),
        ``field`` (config field name), and ``message`` (human-readable).
        ``str(issue)`` returns ``"ERROR: ..."`` or ``"WARNING: ..."`` for
        backward compatibility with string-based parsing.
        """
        issues: list[ConfigIssue] = []
        if self.handler_mode != "persistent":
            issues.append(ConfigIssue(
                level="error", field="handler_mode",
                message=f"Invalid handler_mode: {self.handler_mode}. Only 'persistent' is supported.",
            ))
        if self.embed_provider not in ("openai", "azure", "openai_compat"):
            issues.append(ConfigIssue(
                level="error", field="embed_provider",
                message=f"Invalid embed_provider: {self.embed_provider}. Valid: openai, azure, openai_compat",
            ))
        if self.llm_provider not in ("openai", "azure", "openai_compat"):
            issues.append(ConfigIssue(
                level="error", field="llm_provider",
                message=f"Invalid llm_provider: {self.llm_provider}. Valid: openai, azure, openai_compat",
            ))
        if self.embed_provider == "openai" and not self.openai_api_key:
            issues.append(ConfigIssue(
                level="error", field="openai_api_key",
                message="OPENAI_API_KEY not set. Required for embeddings and LLM operations.",
            ))
        if self.embed_provider == "azure":
            if not self.azure_openai_api_key:
                issues.append(ConfigIssue(
                    level="error", field="azure_openai_api_key",
                    message="AZURE_OPENAI_API_KEY not set. Required for Azure provider.",
                ))
            if not self.azure_openai_endpoint:
                issues.append(ConfigIssue(
                    level="error", field="azure_openai_endpoint",
                    message="AZURE_OPENAI_ENDPOINT not set. Required for Azure provider.",
                ))
        if self.embed_provider == "openai_compat" and not self.openai_base_url:
            issues.append(ConfigIssue(
                level="error", field="openai_base_url",
                message="OPENAI_BASE_URL not set. Required for openai_compat provider.",
            ))
        if self.vector_store == "qdrant" and not self.qdrant_url:
            issues.append(ConfigIssue(
                level="error", field="qdrant_url",
                message="QDRANT_URL required for qdrant vector store.",
            ))
        if self.graph_store == "neo4j" and not self.neo4j_uri:
            issues.append(ConfigIssue(
                level="error", field="neo4j_uri",
                message="NEO4J_URI required for neo4j graph store.",
            ))
        if self.vector_store not in ("sqlite", "qdrant"):
            issues.append(ConfigIssue(
                level="error", field="vector_store",
                message=f"Invalid vector_store: {self.vector_store}",
            ))
        if self.graph_store not in ("sqlite", "neo4j"):
            issues.append(ConfigIssue(
                level="error", field="graph_store",
                message=f"Invalid graph_store: {self.graph_store}",
            ))
        return issues

    @classmethod
    def from_env(cls) -> "TrustMemConfig":
        """Build config from environment variables."""
        try:
            from dotenv import load_dotenv
            load_dotenv(override=False)
        except ImportError:
            pass

        handler_mode = os.getenv("HANDLER_MODE", "persistent")

        return cls(
            handler_mode=handler_mode,
            vector_store=os.getenv("VECTOR_STORE", "sqlite"),
            qdrant_url=os.getenv("QDRANT_URL", "http://localhost:6333"),
            qdrant_api_key=os.getenv("QDRANT_API_KEY", ""),
            qdrant_collection=os.getenv("QDRANT_COLLECTION", "trustmem"),
            embed_dim=_safe_int(os.getenv("EMBED_DIM", "1536"), "EMBED_DIM", 1536),
            embed_provider=os.getenv("EMBED_PROVIDER", "openai"),
            llm_provider=os.getenv("LLM_PROVIDER", "openai"),
            openai_api_key=os.getenv("OPENAI_API_KEY", ""),
            openai_base_url=os.getenv("OPENAI_BASE_URL", ""),
            embed_model=os.getenv("EMBED_MODEL", "text-embedding-3-small"),
            llm_model=os.getenv("LLM_MODEL", "gpt-4o-mini"),
            azure_openai_endpoint=os.getenv("AZURE_OPENAI_ENDPOINT", ""),
            azure_openai_api_key=os.getenv("AZURE_OPENAI_API_KEY", ""),
            azure_api_version=os.getenv("AZURE_API_VERSION", "2024-02-01"),
            graph_store=os.getenv("GRAPH_STORE", "sqlite"),
            neo4j_uri=os.getenv("NEO4J_URI", "bolt://localhost:7687"),
            neo4j_user=os.getenv("NEO4J_USER", "neo4j"),
            neo4j_password=os.getenv("NEO4J_PASSWORD", ""),
            neo4j_database=os.getenv("NEO4J_DATABASE", "neo4j"),
            database_url=os.getenv("DATABASE_URL", ""),
            read_audit=os.getenv("TRUSTMEM_READ_AUDIT", "").lower() in ("true", "1", "yes"),
            host=os.getenv("HOST", "0.0.0.0"),
            port=_safe_int(os.getenv("PORT", "8080"), "PORT", 8080),
            api_key=os.getenv("TRUSTMEM_API_KEY", ""),
            cors_origins=[
                o.strip() for o in
                os.getenv("CORS_ORIGINS", "http://localhost:8080,http://localhost:3000,http://127.0.0.1:8080,http://127.0.0.1:3000").split(",")
            ],
        )
