"""
SignNow API Configuration

Configuration settings for the SignNow API client.
"""

from pydantic import AnyHttpUrl, Field, ValidationError, field_validator, model_validator
from pydantic_settings import BaseSettings, SettingsConfigDict


class SignNowConfig(BaseSettings):
    """Configuration for SignNow API client

    Required environment variables follow oneOf rule:
    - Option A (Password grant): SIGNNOW_USER_EMAIL, SIGNNOW_PASSWORD, SIGNNOW_API_BASIC_TOKEN
    - Option B (Client credentials): SIGNNOW_CLIENT_ID, SIGNNOW_CLIENT_SECRET
    """

    model_config = SettingsConfigDict(env_file=".env", extra="ignore")

    # API endpoints
    api_base: AnyHttpUrl = Field(default_factory=lambda: AnyHttpUrl("https://api.signnow.com"), description="SignNow API base URL", alias="SIGNNOW_API_BASE")
    app_base: AnyHttpUrl = Field(default_factory=lambda: AnyHttpUrl("https://app.signnow.com"), description="SignNow app base URL", alias="SIGNNOW_APP_BASE")

    # OAuth2 credentials (made optional; validated by oneOf rule below)
    client_id: str | None = Field(default=None, description="SignNow client ID", alias="SIGNNOW_CLIENT_ID")
    client_secret: str | None = Field(default=None, description="SignNow client secret", alias="SIGNNOW_CLIENT_SECRET")
    basic_token: str | None = Field(default=None, description="SignNow API basic token", alias="SIGNNOW_API_BASIC_TOKEN")

    # User credentials (for password grant) - optional; validated by oneOf rule
    user_email: str | None = Field(default=None, description="SignNow user email", alias="SIGNNOW_USER_EMAIL")
    password: str | None = Field(default=None, description="SignNow user password", alias="SIGNNOW_PASSWORD")

    # Default scope
    default_scope: str = Field(default="*", description="Default OAuth scope")

    @field_validator("api_base", mode="before")
    @classmethod
    def validate_api_base(cls: type["SignNowConfig"], v: str | None) -> AnyHttpUrl:
        """Handle empty string for api_base"""
        if v == "" or v is None:
            return AnyHttpUrl("https://api.signnow.com")
        return v

    @field_validator("app_base", mode="before")
    @classmethod
    def validate_app_base(cls: type["SignNowConfig"], v: str | None) -> AnyHttpUrl:
        """Handle empty string for app_base"""
        if v == "" or v is None:
            return AnyHttpUrl("https://app.signnow.com")
        return v

    @field_validator("client_id", mode="before")
    @classmethod
    def validate_client_id(cls: type["SignNowConfig"], v: str | None) -> str | None:
        """Handle empty string for client_id"""
        if v == "":
            return None
        return v

    @field_validator("client_secret", mode="before")
    @classmethod
    def validate_client_secret(cls: type["SignNowConfig"], v: str | None) -> str | None:
        """Handle empty string for client_secret"""
        if v == "":
            return None
        return v

    @field_validator("basic_token", mode="before")
    @classmethod
    def validate_basic_token(cls: type["SignNowConfig"], v: str | None) -> str | None:
        """Handle empty string for basic_token"""
        if v == "":
            return None
        return v

    @field_validator("user_email", mode="before")
    @classmethod
    def validate_user_email(cls: type["SignNowConfig"], v: str | None) -> str | None:
        """Handle empty string for user_email"""
        if v == "":
            return None
        return v

    @field_validator("password", mode="before")
    @classmethod
    def validate_password(cls: type["SignNowConfig"], v: str | None) -> str | None:
        """Handle empty string for password"""
        if v == "":
            return None
        return v

    @field_validator("default_scope", mode="before")
    @classmethod
    def validate_default_scope(cls: type["SignNowConfig"], v: str | None) -> str:
        """Handle empty string for default_scope"""
        if v == "" or v is None:
            return "*"
        return v

    @model_validator(mode="after")
    def validate_one_of_credentials(self: "SignNowConfig") -> "SignNowConfig":
        """Ensure that either password grant set or client credentials set is fully provided.

        Option A (password grant): SIGNNOW_USER_EMAIL, SIGNNOW_PASSWORD, SIGNNOW_API_BASIC_TOKEN
        Option B (client credentials): SIGNNOW_CLIENT_ID, SIGNNOW_CLIENT_SECRET
        """
        has_password_grant = bool(self.user_email and self.password and self.basic_token)
        has_client_credentials = bool(self.client_id and self.client_secret)

        if not (has_password_grant or has_client_credentials):
            # Build helpful error message similar to JSON Schema oneOf
            missing_a = [
                name
                for name, val in (
                    ("SIGNNOW_USER_EMAIL", self.user_email),
                    ("SIGNNOW_PASSWORD", self.password),
                    ("SIGNNOW_API_BASIC_TOKEN", self.basic_token),
                )
                if not val
            ]
            missing_b = [
                name
                for name, val in (
                    ("SIGNNOW_CLIENT_ID", self.client_id),
                    ("SIGNNOW_CLIENT_SECRET", self.client_secret),
                )
                if not val
            ]
            detail = (
                "oneOf credential sets must be provided; "
                f"missing for Option A (password grant): {', '.join(missing_a) or 'none'}; "
                f"missing for Option B (client credentials): {', '.join(missing_b) or 'none'}"
            )
            raise ValidationError.from_exception_data(
                "SignNowConfig",
                [
                    {
                        "type": "missing",
                        "loc": ("oneOf",),
                        "msg": detail,
                        "input": None,
                    }
                ],
            )
        return self


def _mask_secret_value(value: str) -> str:
    """Mask secret values for logging"""
    if not value or len(value) < 4:
        return "***"
    return value[:2] + "*" * (len(value) - 4) + value[-2:]


def _print_config_values(config: SignNowConfig) -> None:
    """Print all config values that were loaded from environment variables"""
    print("=" * 80)
    print("Configuration values loaded from environment (SignNow):")
    print("=" * 80)

    # Fields that contain secrets and should be masked
    secret_fields = {
        "SIGNNOW_CLIENT_SECRET",
        "SIGNNOW_API_BASIC_TOKEN",
        "SIGNNOW_PASSWORD",
    }

    # Use model_dump with by_alias=True to get values by their env var names
    config_dict = config.model_dump(by_alias=True)

    # Print all config values using their aliases (env var names)
    for env_var_name, value in sorted(config_dict.items()):
        if value is None:
            print(f"  {env_var_name}=<not set>")
        else:
            if env_var_name in secret_fields:
                str_value = str(value)
                masked = _mask_secret_value(str_value)
                print(f"  {env_var_name}={masked}")
            else:
                print(f"  {env_var_name}={value}")

    print("=" * 80)


def load_signnow_config() -> SignNowConfig:
    """Load SignNow configuration from environment variables"""
    config = SignNowConfig()
    _print_config_values(config)
    return config
