from __future__ import annotations

from datetime import datetime
from typing import Any, Dict, List, Optional

from pydantic import BaseModel, Field, ConfigDict, model_validator


class MiniAppBranding(BaseModel):
    service_name: Dict[str, Optional[str]] = Field(default_factory=dict)
    service_description: Dict[str, Optional[str]] = Field(default_factory=dict)


class MiniAppSubscriptionRequest(BaseModel):
    init_data: str = Field(..., alias="initData")


class MiniAppSubscriptionUser(BaseModel):
    telegram_id: int
    username: Optional[str] = None
    first_name: Optional[str] = None
    last_name: Optional[str] = None
    display_name: str
    language: Optional[str] = None
    status: str
    subscription_status: str
    subscription_actual_status: str
    status_label: str
    expires_at: Optional[datetime] = None
    device_limit: Optional[int] = None
    traffic_used_gb: float = 0.0
    traffic_used_label: str
    traffic_limit_gb: Optional[int] = None
    traffic_limit_label: str
    lifetime_used_traffic_gb: float = 0.0
    has_active_subscription: bool = False
    promo_offer_discount_percent: int = 0
    promo_offer_discount_expires_at: Optional[datetime] = None
    promo_offer_discount_source: Optional[str] = None


class MiniAppPromoGroup(BaseModel):
    id: int
    name: str
    server_discount_percent: int = 0
    traffic_discount_percent: int = 0
    device_discount_percent: int = 0
    period_discounts: Dict[int, int] = Field(default_factory=dict)
    apply_discounts_to_addons: bool = True


class MiniAppAutoPromoGroupLevel(BaseModel):
    id: int
    name: str
    threshold_kopeks: int
    threshold_rubles: float
    threshold_label: str
    is_reached: bool = False
    is_current: bool = False
    server_discount_percent: int = 0
    traffic_discount_percent: int = 0
    device_discount_percent: int = 0
    period_discounts: Dict[int, int] = Field(default_factory=dict)
    apply_discounts_to_addons: bool = True


class MiniAppConnectedServer(BaseModel):
    uuid: str
    name: str


class MiniAppDevice(BaseModel):
    hwid: Optional[str] = None
    platform: Optional[str] = None
    device_model: Optional[str] = None
    app_version: Optional[str] = None
    last_seen: Optional[str] = None
    last_ip: Optional[str] = None


class MiniAppDeviceRemovalRequest(BaseModel):
    init_data: str = Field(..., alias="initData")
    hwid: str


class MiniAppDeviceRemovalResponse(BaseModel):
    success: bool = True
    message: Optional[str] = None


class MiniAppTransaction(BaseModel):
    id: int
    type: str
    amount_kopeks: int
    amount_rubles: float
    description: Optional[str] = None
    payment_method: Optional[str] = None
    external_id: Optional[str] = None
    is_completed: bool
    created_at: datetime
    completed_at: Optional[datetime] = None


class MiniAppPromoOffer(BaseModel):
    id: int
    status: str
    notification_type: Optional[str] = None
    offer_type: Optional[str] = None
    effect_type: Optional[str] = None
    discount_percent: int = 0
    bonus_amount_kopeks: int = 0
    bonus_amount_label: Optional[str] = None
    expires_at: Optional[datetime] = None
    claimed_at: Optional[datetime] = None
    is_active: bool = False
    template_id: Optional[int] = None
    template_name: Optional[str] = None
    button_text: Optional[str] = None
    title: Optional[str] = None
    message_text: Optional[str] = None
    icon: Optional[str] = None
    test_squads: List[MiniAppConnectedServer] = Field(default_factory=list)
    active_discount_expires_at: Optional[datetime] = None
    active_discount_started_at: Optional[datetime] = None
    active_discount_duration_seconds: Optional[int] = None


class MiniAppPromoOfferClaimRequest(BaseModel):
    init_data: str = Field(..., alias="initData")


class MiniAppPromoOfferClaimResponse(BaseModel):
    success: bool = True
    code: Optional[str] = None


class MiniAppPromoCode(BaseModel):
    code: str
    type: Optional[str] = None
    balance_bonus_kopeks: int = 0
    subscription_days: int = 0
    max_uses: Optional[int] = None
    current_uses: Optional[int] = None
    valid_until: Optional[datetime] = None


class MiniAppPromoCodeActivationRequest(BaseModel):
    init_data: str = Field(..., alias="initData")
    code: str


class MiniAppPromoCodeActivationResponse(BaseModel):
    success: bool = True
    description: Optional[str] = None
    promocode: Optional[MiniAppPromoCode] = None


class MiniAppFaqItem(BaseModel):
    id: int
    title: Optional[str] = None
    content: Optional[str] = None
    display_order: Optional[int] = None


class MiniAppFaq(BaseModel):
    requested_language: str
    language: str
    is_enabled: bool = True
    total: int = 0
    items: List[MiniAppFaqItem] = Field(default_factory=list)


class MiniAppRichTextDocument(BaseModel):
    requested_language: str
    language: str
    title: Optional[str] = None
    is_enabled: bool = True
    content: str = ""
    created_at: Optional[datetime] = None
    updated_at: Optional[datetime] = None


class MiniAppLegalDocuments(BaseModel):
    public_offer: Optional[MiniAppRichTextDocument] = None
    service_rules: Optional[MiniAppRichTextDocument] = None
    privacy_policy: Optional[MiniAppRichTextDocument] = None


class MiniAppReferralTerms(BaseModel):
    minimum_topup_kopeks: int = 0
    minimum_topup_label: Optional[str] = None
    first_topup_bonus_kopeks: int = 0
    first_topup_bonus_label: Optional[str] = None
    inviter_bonus_kopeks: int = 0
    inviter_bonus_label: Optional[str] = None
    commission_percent: float = 0.0
    referred_user_reward_kopeks: int = 0
    referred_user_reward_label: Optional[str] = None


class MiniAppReferralStats(BaseModel):
    invited_count: int = 0
    paid_referrals_count: int = 0
    active_referrals_count: int = 0
    total_earned_kopeks: int = 0
    total_earned_label: Optional[str] = None
    month_earned_kopeks: int = 0
    month_earned_label: Optional[str] = None
    conversion_rate: float = 0.0


class MiniAppReferralRecentEarning(BaseModel):
    amount_kopeks: int = 0
    amount_label: Optional[str] = None
    reason: Optional[str] = None
    referral_name: Optional[str] = None
    created_at: Optional[datetime] = None


class MiniAppReferralItem(BaseModel):
    id: int
    telegram_id: Optional[int] = None
    full_name: Optional[str] = None
    username: Optional[str] = None
    created_at: Optional[datetime] = None
    last_activity: Optional[datetime] = None
    has_made_first_topup: bool = False
    balance_kopeks: int = 0
    balance_label: Optional[str] = None
    total_earned_kopeks: int = 0
    total_earned_label: Optional[str] = None
    topups_count: int = 0
    days_since_registration: Optional[int] = None
    days_since_activity: Optional[int] = None
    status: Optional[str] = None


class MiniAppReferralList(BaseModel):
    total_count: int = 0
    has_next: bool = False
    has_prev: bool = False
    current_page: int = 1
    total_pages: int = 1
    items: List[MiniAppReferralItem] = Field(default_factory=list)


class MiniAppReferralInfo(BaseModel):
    referral_code: Optional[str] = None
    referral_link: Optional[str] = None
    terms: Optional[MiniAppReferralTerms] = None
    stats: Optional[MiniAppReferralStats] = None
    recent_earnings: List[MiniAppReferralRecentEarning] = Field(default_factory=list)
    referrals: Optional[MiniAppReferralList] = None


class MiniAppPaymentMethodsRequest(BaseModel):
    init_data: str = Field(..., alias="initData")


class MiniAppPaymentMethod(BaseModel):
    id: str
    icon: Optional[str] = None
    requires_amount: bool = False
    currency: str = "RUB"
    min_amount_kopeks: Optional[int] = None
    max_amount_kopeks: Optional[int] = None
    amount_step_kopeks: Optional[int] = None


class MiniAppPaymentMethodsResponse(BaseModel):
    methods: List[MiniAppPaymentMethod] = Field(default_factory=list)


class MiniAppPaymentCreateRequest(BaseModel):
    init_data: str = Field(..., alias="initData")
    method: str
    amount_rubles: Optional[float] = Field(default=None, alias="amountRubles")
    amount_kopeks: Optional[int] = Field(default=None, alias="amountKopeks")
    payment_option: Optional[str] = Field(default=None, alias="option")


class MiniAppPaymentCreateResponse(BaseModel):
    success: bool = True
    method: str
    payment_url: Optional[str] = None
    amount_kopeks: Optional[int] = None
    extra: Dict[str, Any] = Field(default_factory=dict)


class MiniAppPaymentStatusQuery(BaseModel):
    method: str
    local_payment_id: Optional[int] = Field(default=None, alias="localPaymentId")
    invoice_id: Optional[str] = Field(default=None, alias="invoiceId")
    payment_id: Optional[str] = Field(default=None, alias="paymentId")
    payload: Optional[str] = None
    amount_kopeks: Optional[int] = Field(default=None, alias="amountKopeks")
    started_at: Optional[str] = Field(default=None, alias="startedAt")


class MiniAppPaymentStatusRequest(BaseModel):
    init_data: str = Field(..., alias="initData")
    payments: List[MiniAppPaymentStatusQuery] = Field(default_factory=list)


class MiniAppPaymentStatusResult(BaseModel):
    method: str
    status: str
    is_paid: bool = False
    amount_kopeks: Optional[int] = None
    currency: Optional[str] = None
    completed_at: Optional[datetime] = None
    transaction_id: Optional[int] = None
    external_id: Optional[str] = None
    message: Optional[str] = None
    extra: Dict[str, Any] = Field(default_factory=dict)


class MiniAppPaymentStatusResponse(BaseModel):
    results: List[MiniAppPaymentStatusResult] = Field(default_factory=list)


class MiniAppSubscriptionResponse(BaseModel):
    success: bool = True
    subscription_id: int
    remnawave_short_uuid: Optional[str] = None
    user: MiniAppSubscriptionUser
    subscription_url: Optional[str] = None
    subscription_crypto_link: Optional[str] = None
    subscription_purchase_url: Optional[str] = None
    links: List[str] = Field(default_factory=list)
    ss_conf_links: Dict[str, str] = Field(default_factory=dict)
    connected_squads: List[str] = Field(default_factory=list)
    connected_servers: List[MiniAppConnectedServer] = Field(default_factory=list)
    connected_devices_count: int = 0
    connected_devices: List[MiniAppDevice] = Field(default_factory=list)
    happ: Optional[Dict[str, Any]] = None
    happ_link: Optional[str] = None
    happ_crypto_link: Optional[str] = None
    happ_cryptolink_redirect_link: Optional[str] = None
    balance_kopeks: int = 0
    balance_rubles: float = 0.0
    balance_currency: Optional[str] = None
    transactions: List[MiniAppTransaction] = Field(default_factory=list)
    promo_offers: List[MiniAppPromoOffer] = Field(default_factory=list)
    promo_group: Optional[MiniAppPromoGroup] = None
    auto_assign_promo_groups: List[MiniAppAutoPromoGroupLevel] = Field(default_factory=list)
    total_spent_kopeks: int = 0
    total_spent_rubles: float = 0.0
    total_spent_label: Optional[str] = None
    subscription_type: str
    autopay_enabled: bool = False
    branding: Optional[MiniAppBranding] = None
    faq: Optional[MiniAppFaq] = None
    legal_documents: Optional[MiniAppLegalDocuments] = None
    referral: Optional[MiniAppReferralInfo] = None


class MiniAppSubscriptionServerOption(BaseModel):
    uuid: str
    name: Optional[str] = None
    price_kopeks: Optional[int] = None
    price_label: Optional[str] = None
    discount_percent: Optional[int] = None
    is_connected: bool = False
    is_available: bool = True
    disabled_reason: Optional[str] = None


class MiniAppSubscriptionTrafficOption(BaseModel):
    value: Optional[int] = None
    label: Optional[str] = None
    price_kopeks: Optional[int] = None
    price_label: Optional[str] = None
    is_current: bool = False
    is_available: bool = True
    description: Optional[str] = None


class MiniAppSubscriptionDeviceOption(BaseModel):
    value: int
    label: Optional[str] = None
    price_kopeks: Optional[int] = None
    price_label: Optional[str] = None


class MiniAppSubscriptionCurrentSettings(BaseModel):
    servers: List[MiniAppConnectedServer] = Field(default_factory=list)
    traffic_limit_gb: Optional[int] = None
    traffic_limit_label: Optional[str] = None
    device_limit: int = 0


class MiniAppSubscriptionServersSettings(BaseModel):
    available: List[MiniAppSubscriptionServerOption] = Field(default_factory=list)
    min: int = 0
    max: int = 0
    can_update: bool = True
    hint: Optional[str] = None


class MiniAppSubscriptionTrafficSettings(BaseModel):
    options: List[MiniAppSubscriptionTrafficOption] = Field(default_factory=list)
    can_update: bool = True
    current_value: Optional[int] = None


class MiniAppSubscriptionDevicesSettings(BaseModel):
    options: List[MiniAppSubscriptionDeviceOption] = Field(default_factory=list)
    can_update: bool = True
    min: int = 0
    max: int = 0
    step: int = 1
    current: int = 0
    price_kopeks: Optional[int] = None
    price_label: Optional[str] = None


class MiniAppSubscriptionBillingContext(BaseModel):
    months_remaining: int = 1
    period_hint_days: Optional[int] = None
    renews_at: Optional[datetime] = None


class MiniAppSubscriptionSettings(BaseModel):
    subscription_id: int
    currency: str = "RUB"
    current: MiniAppSubscriptionCurrentSettings
    servers: MiniAppSubscriptionServersSettings
    traffic: MiniAppSubscriptionTrafficSettings
    devices: MiniAppSubscriptionDevicesSettings
    billing: Optional[MiniAppSubscriptionBillingContext] = None


class MiniAppSubscriptionSettingsResponse(BaseModel):
    success: bool = True
    settings: MiniAppSubscriptionSettings


class MiniAppSubscriptionPurchasePeriod(BaseModel):
    id: str
    days: int
    months: int
    price_kopeks: int = Field(..., alias="priceKopeks")
    price_label: Optional[str] = Field(default=None, alias="priceLabel")
    original_price_kopeks: Optional[int] = Field(default=None, alias="originalPriceKopeks")
    original_price_label: Optional[str] = Field(default=None, alias="originalPriceLabel")
    per_month_price_kopeks: Optional[int] = Field(default=None, alias="perMonthPriceKopeks")
    per_month_price_label: Optional[str] = Field(default=None, alias="perMonthPriceLabel")
    discount_percent: Optional[int] = Field(default=None, alias="discountPercent")
    description: Optional[str] = None
    traffic: Optional[Dict[str, Any]] = None
    servers: Optional[Dict[str, Any]] = None
    devices: Optional[Dict[str, Any]] = None

    model_config = ConfigDict(populate_by_name=True)


class MiniAppSubscriptionPurchaseOptions(BaseModel):
    currency: str = "RUB"
    balance_kopeks: int = Field(..., alias="balanceKopeks")
    balance_label: Optional[str] = Field(default=None, alias="balanceLabel")
    subscription_id: Optional[int] = Field(default=None, alias="subscriptionId")
    periods: List[Dict[str, Any]] = Field(default_factory=list)
    traffic: Dict[str, Any] = Field(default_factory=dict)
    servers: Dict[str, Any] = Field(default_factory=dict)
    devices: Dict[str, Any] = Field(default_factory=dict)
    selection: Dict[str, Any] = Field(default_factory=dict)
    summary: Optional[Dict[str, Any]] = None
    promo: Optional[Dict[str, Any]] = None

    model_config = ConfigDict(populate_by_name=True)


class MiniAppSubscriptionPurchaseOptionsResponse(BaseModel):
    success: bool = True
    data: MiniAppSubscriptionPurchaseOptions


class MiniAppSubscriptionPurchasePreview(BaseModel):
    total_price_kopeks: int = Field(..., alias="totalPriceKopeks")
    total_price_label: str = Field(..., alias="totalPriceLabel")
    original_price_kopeks: Optional[int] = Field(default=None, alias="originalPriceKopeks")
    original_price_label: Optional[str] = Field(default=None, alias="originalPriceLabel")
    per_month_price_kopeks: Optional[int] = Field(default=None, alias="perMonthPriceKopeks")
    per_month_price_label: Optional[str] = Field(default=None, alias="perMonthPriceLabel")
    discount_percent: Optional[int] = Field(default=None, alias="discountPercent")
    discount_label: Optional[str] = Field(default=None, alias="discountLabel")
    discount_lines: List[str] = Field(default_factory=list, alias="discountLines")
    breakdown: List[Dict[str, Any]] = Field(default_factory=list)
    balance_label: Optional[str] = Field(default=None, alias="balanceLabel")
    balance_kopeks: Optional[int] = Field(default=None, alias="balanceKopeks")
    missing_amount_kopeks: Optional[int] = Field(default=None, alias="missingAmountKopeks")
    missing_amount_label: Optional[str] = Field(default=None, alias="missingAmountLabel")
    status_message: Optional[str] = Field(default=None, alias="statusMessage")
    can_purchase: bool = Field(default=True, alias="canPurchase")

    model_config = ConfigDict(populate_by_name=True)


class MiniAppSubscriptionPurchasePreviewResponse(BaseModel):
    success: bool = True
    preview: MiniAppSubscriptionPurchasePreview


class MiniAppSubscriptionPurchaseOptionsRequest(BaseModel):
    init_data: str = Field(..., alias="initData")


class MiniAppSubscriptionPurchaseBaseRequest(BaseModel):
    init_data: str = Field(..., alias="initData")
    subscription_id: Optional[int] = Field(default=None, alias="subscriptionId")
    selection: Optional[Dict[str, Any]] = None
    period_id: Optional[str] = Field(default=None, alias="periodId")
    period_key: Optional[str] = Field(default=None, alias="periodKey")
    period_code: Optional[str] = Field(default=None, alias="periodCode")
    period: Optional[str] = None
    period_days: Optional[int] = Field(default=None, alias="periodDays")
    period_months: Optional[int] = Field(default=None, alias="periodMonths")
    months: Optional[int] = None
    traffic: Optional[int] = None
    traffic_value: Optional[int] = Field(default=None, alias="trafficValue")
    traffic_gb: Optional[int] = Field(default=None, alias="trafficGb")
    limit: Optional[int] = None
    servers: Optional[List[str]] = None
    countries: Optional[List[str]] = None
    server_uuids: Optional[List[str]] = Field(default=None, alias="serverUuids")
    squad_uuids: Optional[List[str]] = Field(default=None, alias="squadUuids")
    devices: Optional[int] = None
    device_limit: Optional[int] = Field(default=None, alias="deviceLimit")

    model_config = ConfigDict(populate_by_name=True, extra="allow")

    @model_validator(mode="before")
    @classmethod
    def _merge_selection(cls, values: Any) -> Any:
        if isinstance(values, dict):
            selection = values.get("selection")
            if isinstance(selection, dict):
                merged = dict(selection)
                merged.update(values)
                return merged
        return values


class MiniAppSubscriptionPurchasePreviewRequest(MiniAppSubscriptionPurchaseBaseRequest):
    pass


class MiniAppSubscriptionPurchaseSubmitRequest(MiniAppSubscriptionPurchaseBaseRequest):
    pass


class MiniAppSubscriptionPurchaseSubmitResponse(BaseModel):
    success: bool = True
    message: Optional[str] = None
    subscription_id: Optional[int] = Field(default=None, alias="subscriptionId")
    balance_kopeks: Optional[int] = Field(default=None, alias="balanceKopeks")
    balance_label: Optional[str] = Field(default=None, alias="balanceLabel")


class MiniAppSubscriptionSettingsRequest(BaseModel):
    init_data: str = Field(..., alias="initData")
    subscription_id: Optional[int] = None

    model_config = ConfigDict(populate_by_name=True)

    @model_validator(mode="before")
    @classmethod
    def _populate_aliases(cls, values: Any) -> Any:
        if isinstance(values, dict):
            if "subscriptionId" in values and "subscription_id" not in values:
                values["subscription_id"] = values["subscriptionId"]
        return values


class MiniAppSubscriptionServersUpdateRequest(BaseModel):
    init_data: str = Field(..., alias="initData")
    subscription_id: Optional[int] = None
    servers: Optional[List[str]] = None
    squads: Optional[List[str]] = None
    server_uuids: Optional[List[str]] = None
    squad_uuids: Optional[List[str]] = None

    model_config = ConfigDict(populate_by_name=True)

    @model_validator(mode="before")
    @classmethod
    def _populate_aliases(cls, values: Any) -> Any:
        if isinstance(values, dict):
            alias_map = {
                "subscriptionId": "subscription_id",
                "serverUuids": "server_uuids",
                "squadUuids": "squad_uuids",
            }
            for alias, target in alias_map.items():
                if alias in values and target not in values:
                    values[target] = values[alias]
        return values


class MiniAppSubscriptionTrafficUpdateRequest(BaseModel):
    init_data: str = Field(..., alias="initData")
    subscription_id: Optional[int] = None
    traffic: Optional[int] = None
    traffic_gb: Optional[int] = None

    model_config = ConfigDict(populate_by_name=True)

    @model_validator(mode="before")
    @classmethod
    def _populate_aliases(cls, values: Any) -> Any:
        if isinstance(values, dict):
            alias_map = {
                "subscriptionId": "subscription_id",
                "trafficGb": "traffic_gb",
            }
            for alias, target in alias_map.items():
                if alias in values and target not in values:
                    values[target] = values[alias]
        return values


class MiniAppSubscriptionDevicesUpdateRequest(BaseModel):
    init_data: str = Field(..., alias="initData")
    subscription_id: Optional[int] = None
    devices: Optional[int] = None
    device_limit: Optional[int] = None

    model_config = ConfigDict(populate_by_name=True)

    @model_validator(mode="before")
    @classmethod
    def _populate_aliases(cls, values: Any) -> Any:
        if isinstance(values, dict):
            alias_map = {
                "subscriptionId": "subscription_id",
                "deviceLimit": "device_limit",
            }
            for alias, target in alias_map.items():
                if alias in values and target not in values:
                    values[target] = values[alias]
        return values


class MiniAppSubscriptionUpdateResponse(BaseModel):
    success: bool = True
    message: Optional[str] = None

