"""Configuration for one TaxPilot AI deployment.

Per ADR-0001 this process serves exactly one TaxPilot installation. There is no
customer or tenant concept anywhere in this codebase, and adding one would mean
multi-tenancy had crept in — grounds to reject the change, not a convenience.
"""

from __future__ import annotations

import os
from dataclasses import dataclass


class ConfigurationError(RuntimeError):
    """Raised when the deployment is not configured well enough to run."""


@dataclass(frozen=True, slots=True)
class Settings:
    """Everything this deployment needs to reach its CMS."""

    cms_base_url: str
    agent_api_key: str
    agent_api_secret: str
    request_timeout: float = 15.0
    verify_tls: bool = True

    def __repr__(self) -> str:
        """Masked, because a dataclass repr is how secrets reach logs.

        `logger.exception("failed with %s", settings)`, a traceback that prints
        locals, or a crash reporter all render this — and logs are shipped,
        collected and kept far longer than anybody intends. The default
        dataclass repr printed the API secret in full.
        """
        return (
            f"Settings(cms_base_url={self.cms_base_url!r}, "
            f"agent_api_key={_mask(self.agent_api_key)!r}, "
            f"agent_api_secret={_mask(self.agent_api_secret)!r}, "
            f"verify_tls={self.verify_tls!r})"
        )

    @property
    def agent_api_root(self) -> str:
        return f"{self.cms_base_url.rstrip('/')}/api/agent/v1"

    @classmethod
    def from_env(cls, env: dict[str, str] | None = None) -> Settings:
        """Load from the environment, failing loudly on anything missing.

        A deployment missing its credentials should refuse to start rather than
        run and fail on every call — the second is far harder to diagnose from
        a log full of 400s.
        """
        env = env if env is not None else dict(os.environ)

        missing = [
            name
            for name in ("TAXPILOT_CMS_URL", "TAXPILOT_AGENT_KEY", "TAXPILOT_AGENT_SECRET")
            if not env.get(name)
        ]

        if missing:
            raise ConfigurationError(
                "TaxPilot AI is not configured. Missing: " + ", ".join(missing)
            )

        base_url = env["TAXPILOT_CMS_URL"].rstrip("/")

        # TLS verification is on unless deliberately disabled for local work.
        # This connection carries client data; silently allowing a downgrade
        # would defeat ADR-0002 at the transport layer.
        verify_tls = env.get("TAXPILOT_VERIFY_TLS", "true").lower() not in {"0", "false", "no"}

        if not verify_tls and not base_url.startswith("http://"):
            raise ConfigurationError(
                "TLS verification may only be disabled for a plain-http local CMS."
            )

        return cls(
            cms_base_url=base_url,
            agent_api_key=env["TAXPILOT_AGENT_KEY"],
            agent_api_secret=env["TAXPILOT_AGENT_SECRET"],
            request_timeout=float(env.get("TAXPILOT_TIMEOUT", "15")),
            verify_tls=verify_tls,
        )


def _mask(secret: str) -> str:
    """Enough to tell two credentials apart, not enough to use one."""
    if not secret:
        return ""

    return f"…{secret[-4:]}" if len(secret) > 8 else "…"
