import json
from functools import lru_cache

from pydantic import AnyUrl, Field, TypeAdapter
from pydantic_settings import BaseSettings, SettingsConfigDict


class Settings(BaseSettings):
    """Application settings loaded from environment variables.

    Attributes:
        project_name: Human-readable application name.
        frontend_urls_raw: Raw frontend origins for CORS configuration.
        supabase_jwt_secret: Supabase JWT signing secret.
        supabase_jwt_audience: Expected JWT audience claim.
        supabase_jwt_issuer: Expected JWT issuer claim.
        neo4j_uri: Neo4j connection URI.
        neo4j_username: Neo4j username.
        neo4j_password: Neo4j password.
    """

    model_config = SettingsConfigDict(
        env_file=".env",
        env_file_encoding="utf-8",
        case_sensitive=True,
        extra="ignore",
    )

    project_name: str = Field(
        default="backend-sre",
        validation_alias="PROJECT_NAME",
    )
    frontend_urls_raw: str = Field(validation_alias="FRONTEND_URLS")
    supabase_jwt_secret: str = Field(validation_alias="SUPABASE_JWT_SECRET")
    supabase_jwt_audience: str = Field(
        default="authenticated",
        validation_alias="SUPABASE_JWT_AUDIENCE",
    )
    supabase_jwt_issuer: str = Field(validation_alias="SUPABASE_JWT_ISSUER")
    neo4j_uri: str = Field(validation_alias="NEO4J_URI")
    neo4j_username: str = Field(validation_alias="NEO4J_USERNAME")
    neo4j_password: str = Field(validation_alias="NEO4J_PASSWORD")
    dashscope_llm_keys_raw: str = Field(default="", validation_alias="DASHSCOPE_LLM_KEYS")
    dashscope_embedding_keys_raw: str = Field(default="", validation_alias="DASHSCOPE_EMBEDDING_KEYS")
    openai_embedding_keys_raw: str = Field(
        default="",
        validation_alias="OPENAI_EMBEDDING_KEYS",
    )
    openai_llm_keys_raw: str = Field(
        default="",
        validation_alias="OPENAI_LLM_KEYS",
    )
    project_status: str = Field(
        default="development",
        validation_alias="PROJECT_STATUS",
    )
    supabase_url: str = Field(default="", validation_alias="SUPABASE_URL")
    supabase_service_role_key: str = Field(
        default="",
        validation_alias="SUPABASE_SERVICE_ROLE_KEY",
    )
    supabase_bucket: str = Field(default="pdfs", validation_alias="SUPABASE_BUCKET")

    @staticmethod
    def _parse_frontend_urls(value: str) -> list[str]:
        """Parse frontend origins from JSON array, CSV, or single URL string.

        Args:
            value: Raw frontend URLs string.

        Returns:
            list[str]: Parsed list of URL strings.

        Raises:
            ValueError: When the JSON format is invalid.
        """
        stripped = value.strip()
        if stripped.startswith("["):
            try:
                parsed = json.loads(stripped)
            except json.JSONDecodeError as exc:
                raise ValueError(
                    "FRONTEND_URLS must be a JSON array, CSV, or single URL."
                ) from exc
            if isinstance(parsed, list):
                return [str(item).strip() for item in parsed if str(item).strip()]
            raise ValueError("FRONTEND_URLS JSON value must be a list.")
        if "," in stripped:
            return [item.strip() for item in stripped.split(",") if item.strip()]
        return [stripped]

    @staticmethod
    def _parse_key_pool(value: str) -> tuple[str, ...]:
        """Normalize a comma-separated API key pool.

        Args:
            value: Raw comma-separated key string.

        Returns:
            tuple[str, ...]: Ordered non-empty keys.
        """
        return tuple(item.strip() for item in value.split(",") if item.strip())

    @property
    def frontend_urls(self) -> list[str]:
        """Return validated frontend origin URLs."""
        parsed = self._parse_frontend_urls(self.frontend_urls_raw)
        adapter = TypeAdapter(list[AnyUrl])
        return [str(url).rstrip("/") for url in adapter.validate_python(parsed)]

    @property
    def openai_embedding_keys(self) -> tuple[str, ...]:
        """Return normalized embedding keys from the target-state contract."""
        return self._parse_key_pool(self.openai_embedding_keys_raw)

    @property
    def openai_llm_keys(self) -> tuple[str, ...]:
        """Return normalized OpenAI LLM keys from the target-state contract."""
        return self._parse_key_pool(self.openai_llm_keys_raw)

    @property
    def dashscope_llm_keys(self) -> tuple[str, ...]:
        """Return normalized DashScope LLM keys from the target-state contract."""
        return self._parse_key_pool(self.dashscope_llm_keys_raw)

    @property
    def dashscope_embedding_keys(self) -> tuple[str, ...]:
        """Return normalized DashScope Embedding keys from the target-state contract."""
        return self._parse_key_pool(self.dashscope_embedding_keys_raw)


@lru_cache
def get_settings() -> Settings:
    """Return cached application settings.

    Returns:
        Settings: Settings instance loaded from environment variables.
    """
    return Settings()
