from pydantic import BaseModel, Field, field_validator
from typing import Dict, Optional, Literal


# Maximum number of credential key-value pairs a merchant may submit in a single
# onboarding call.  Prevents unbounded mass-assignment of arbitrary keys.
_MAX_CREDENTIAL_KEYS = 20
# Maximum length for a single credential value (e.g. API keys rarely exceed 512 chars).
_MAX_CREDENTIAL_VALUE_LEN = 512
# Maximum length for a single credential key name.
_MAX_CREDENTIAL_KEY_LEN = 100


class OnboardingSubmitRequest(BaseModel):
    credentials: Dict[str, str] = Field(
        ...,
        description="Key-value map of credential fields to submit",
    )

    @field_validator("credentials")
    @classmethod
    def validate_credentials(cls, v: Dict[str, str]) -> Dict[str, str]:
        if len(v) > _MAX_CREDENTIAL_KEYS:
            raise ValueError(
                f"Too many credential fields: maximum {_MAX_CREDENTIAL_KEYS} allowed."
            )
        for key, value in v.items():
            if len(key) > _MAX_CREDENTIAL_KEY_LEN:
                raise ValueError(
                    f"Credential key '{key[:32]}...' exceeds maximum length of {_MAX_CREDENTIAL_KEY_LEN} characters."
                )
            if len(value) > _MAX_CREDENTIAL_VALUE_LEN:
                raise ValueError(
                    f"Credential value for key '{key}' exceeds maximum length of {_MAX_CREDENTIAL_VALUE_LEN} characters."
                )
        return v


class AdminCreateProviderRequest(BaseModel):
    name: str = Field(..., max_length=100)
    slug: str = Field(..., max_length=50, pattern=r'^[a-z][a-z0-9_]*$')
    description: Optional[str] = None
    logo_url: Optional[str] = None
    is_active: bool = True
    is_default: bool = False
    supported_payment_methods: Optional[Dict[str, bool]] = None
    onboarding_schema: Optional[dict] = None


class AdminUpdateProviderRequest(BaseModel):
    name: Optional[str] = Field(None, max_length=100)
    description: Optional[str] = None
    logo_url: Optional[str] = None
    is_active: Optional[bool] = None
    is_default: Optional[bool] = None
    supported_payment_methods: Optional[Dict[str, bool]] = None
    onboarding_schema: Optional[dict] = None


class AdminAssignProviderRequest(BaseModel):
    is_enabled: bool = True


# Only statuses that an admin is allowed to set explicitly are permitted.
# This prevents promotion to "active" via a simple PATCH without real onboarding
# and stops injection of arbitrary string values into the onboarding_status column.
_ALLOWED_ADMIN_ONBOARDING_STATUSES = Literal[
    "not_started", "in_progress", "pending_review", "active", "suspended", "rejected"
]


class AdminUpdateMerchantProviderRequest(BaseModel):
    is_enabled: Optional[bool] = None
    onboarding_status: Optional[_ALLOWED_ADMIN_ONBOARDING_STATUSES] = None


class IframeConfigRequest(BaseModel):
    amount: float = 0.0
    currency: str = "USD"
