"""Resolve the user-visible file system for web and agent callers."""

from __future__ import annotations

from dataclasses import dataclass, field
import os
from pathlib import Path
from typing import Any

import httpx

from beaver.foundation.config.schema import BeaverConfig

from .user_files import (
    LocalUserFileStorage,
    MinIOStorageConfig,
    MinIOUserFileStorage,
    USER_FILE_ROOTS,
    UserFileError,
    UserFileService,
)


class UserFileConfigurationError(UserFileError):
    """Raised when user file storage is not configured for this backend."""


@dataclass(slots=True)
class FileAuthContext:
    """Authenticated identity used by the personal file system boundary."""

    username: str
    backend_id: str
    storage_namespace: str
    user_id: str | None = None
    scopes: tuple[str, ...] = field(default_factory=tuple)
    auth_source: str = "beaver-web-token"


@dataclass(slots=True)
class UserFileStorageStatus:
    configured: bool
    storage_mode: str
    roots: list[str]
    workspace_visible: bool = False
    detail: str | None = None

    def to_dict(self) -> dict[str, Any]:
        payload: dict[str, Any] = {
            "configured": self.configured,
            "storage_mode": self.storage_mode,
            "roots": self.roots,
            "workspace_visible": self.workspace_visible,
        }
        if self.detail:
            payload["detail"] = self.detail
        return payload


class UserFileStorageResolver:
    """Build `UserFileService` from the current Beaver identity and config."""

    def __init__(
        self,
        *,
        config: BeaverConfig,
        workspace: Path,
        auth_context: FileAuthContext,
    ) -> None:
        self.config = config
        self.workspace = Path(workspace)
        self.auth_context = auth_context

    async def service(self) -> UserFileService:
        mode = _storage_mode(self.config)
        if mode == "local":
            return UserFileService(LocalUserFileStorage(self.workspace / "user_files"))
        settings = await self._load_minio_settings()
        return UserFileService(
            MinIOUserFileStorage(
                MinIOStorageConfig(
                    endpoint=str(settings.get("endpoint") or ""),
                    access_key=str(settings.get("access_key") or ""),
                    secret_key=str(settings.get("secret_key") or ""),
                    bucket=str(settings.get("bucket") or ""),
                    secure=bool(settings.get("secure", False)),
                    region=_clean_optional(settings.get("region")),
                    namespace=str(settings.get("namespace") or self.auth_context.storage_namespace),
                )
            )
        )

    async def status(self) -> UserFileStorageStatus:
        mode = _storage_mode(self.config)
        if mode == "local":
            return UserFileStorageStatus(
                configured=True,
                storage_mode="local",
                roots=list(USER_FILE_ROOTS),
                workspace_visible=False,
            )
        try:
            await self._load_minio_settings()
        except UserFileConfigurationError as exc:
            return UserFileStorageStatus(
                configured=False,
                storage_mode="object",
                roots=list(USER_FILE_ROOTS),
                workspace_visible=False,
                detail=str(exc),
            )
        return UserFileStorageStatus(
            configured=True,
            storage_mode="object",
            roots=list(USER_FILE_ROOTS),
            workspace_visible=False,
        )

    async def _load_minio_settings(self) -> dict[str, Any]:
        backend_id = self.auth_context.backend_id.strip()
        if not backend_id:
            raise UserFileConfigurationError("User file storage backend identity is not configured")
        base_url = self.config.authz.base_url.strip()
        if not (self.config.authz.enabled and base_url):
            raise UserFileConfigurationError("AuthZ is required for deployed user file storage")
        token = (
            os.getenv("BEAVER_AUTHZ_INTERNAL_TOKEN", "").strip()
            or os.getenv("AUTHZ_INTERNAL_TOKEN", "").strip()
        )
        if not token:
            raise UserFileConfigurationError("AuthZ internal token is not configured for user file storage")
        try:
            async with httpx.AsyncClient(
                timeout=self.config.authz.request_timeout_seconds,
                follow_redirects=True,
                trust_env=False,
            ) as client:
                response = await client.get(
                    f"{base_url.rstrip('/')}/internal/backends/{backend_id}/settings/minio",
                    headers={"Authorization": f"Bearer {token}"},
                )
        except httpx.HTTPError as exc:
            raise UserFileConfigurationError(f"Unable to load user file storage settings: {exc}") from exc
        if response.status_code == 404:
            raise UserFileConfigurationError("MinIO user file storage is not configured")
        if response.is_error:
            raise UserFileConfigurationError(
                f"Unable to load user file storage settings: HTTP {response.status_code}"
            )
        payload = response.json()
        if not isinstance(payload, dict):
            raise UserFileConfigurationError("Invalid MinIO settings response")
        if not all(str(payload.get(key) or "").strip() for key in ("endpoint", "access_key", "secret_key", "bucket")):
            raise UserFileConfigurationError("MinIO user file storage settings are incomplete")
        payload.setdefault("namespace", self.auth_context.storage_namespace)
        return payload


def build_file_auth_context(
    *,
    username: str,
    config: BeaverConfig,
    user_id: str | None = None,
    scopes: tuple[str, ...] = (),
    auth_source: str = "beaver-web-token",
) -> FileAuthContext:
    backend_id = (
        config.backend_identity.backend_id.strip()
        or os.getenv("BEAVER_BACKEND_IDENTITY__BACKEND_ID", "").strip()
        or username.strip()
    )
    namespace = default_user_file_namespace(backend_id)
    return FileAuthContext(
        username=username.strip(),
        backend_id=backend_id,
        storage_namespace=namespace,
        user_id=user_id,
        scopes=scopes,
        auth_source=auth_source,
    )


def default_user_file_namespace(backend_id: str) -> str:
    cleaned = backend_id.strip().strip("/")
    return f"users/{cleaned}" if cleaned else "users/unconfigured"


def _storage_mode(config: BeaverConfig) -> str:
    raw = os.getenv("BEAVER_USER_FILES_STORAGE_MODE", "").strip().lower()
    if raw in {"local", "dev-local", "development"}:
        return "local"
    if raw in {"minio", "object", "object-storage"}:
        return "minio"
    if config.authz.enabled and config.authz.base_url.strip() and config.backend_identity.backend_id.strip():
        return "minio"
    return "local"


def _clean_optional(value: Any) -> str | None:
    text = str(value or "").strip()
    return text or None
