from enum import Enum
from functools import wraps
from logging import Logger
from typing import Any, Callable, ClassVar, Dict, List, Literal, Optional, Set, Union

from nostr_sdk import Event, Keys, Metadata, ProductData, StallData
from pydantic import BaseModel, ConfigDict, Field

def deprecated(
    reason: str, version: str = "2.0.0", alternative: Optional[str] = None
) -> Callable[[Callable[..., Any]], Callable[..., Any]]: ...

class KeyEncoding(str, Enum):
    """
    Enum representing the valid encoding formats for public or private keys.

    Attributes:
        BECH32 (str): Encodes the key using Bech32 format.
        HEX (str): Encodes the key using hexadecimal format.
    """

    BECH32 = "bech32"
    HEX = "hex"

    @classmethod
    def from_str(cls, value: str) -> "KeyEncoding": ...

class Namespace(str, Enum):
    """
    Represents a namespace.
    """

    MERCHANT = "com.synvya.merchant"
    GAMER = "com.synvya.gamer"
    OTHER = "com.synvya.other"
    BUSINESS_TYPE = "business.type"

    """Configuration for Pydantic models to use enum values directly."""
    model_config = ConfigDict(use_enum_values=True)

class ProfileType(str, Enum):
    """
    Represents a profile type.
    """

    RETAIL = "retail"
    RESTAURANT = "restaurant"
    SERVICE = "service"
    BUSINESS = "business"
    ENTERTAINMENT = "entertainment"
    OTHER = "other"
    GAMER_DADJOKE = "dad-joke-game"
    OTHER_OTHER = "other"

    """Configuration for Pydantic models to use enum values directly."""
    model_config = ConfigDict(use_enum_values=True)

class ProfileFilter(BaseModel):
    """
    Represents a profile filter.
    """

    namespace: str
    profile_type: ProfileType
    hashtags: List[str]

    def __init__(
        self,
        namespace: str,
        profile_type: ProfileType,
        hashtags: Optional[List[str]] = None,
    ) -> None: ...
    def to_json(self) -> str: ...
    @classmethod
    def from_json(cls, json_str: str) -> "ProfileFilter": ...
    @staticmethod
    def _normalize_hashtag(tag: str) -> str: ...

class Profile(BaseModel):
    """
    Nostr Profile class.
    Contains public key only.
    """

    PROFILE_URL_PREFIX: ClassVar[str]
    logger: ClassVar[Logger]

    public_key: str  # stored in hex format
    about: str = ""
    banner: str = ""
    bot: bool = False
    city: str = ""
    country: str = ""
    created_at: int = 0
    display_name: str = ""
    environment: Literal["production", "demo"] = "production"
    email: str = ""
    hashtags: List[str] = []
    locations: Set[str] = Field(default_factory=set)
    name: str = ""
    namespace: str = ""
    nip05: str = ""
    nip05_validated: bool = False
    picture: str = ""
    phone: str = ""
    profile_type: ProfileType = ProfileType.OTHER_OTHER
    profile_url: str = ""
    state: str = ""
    street: str = ""
    website: str = ""
    zip_code: str = ""
    geohash: str = ""

    def __init__(self, public_key: str, **data: Any) -> None: ...
    def add_hashtag(self, hashtag: str) -> None: ...
    def add_location(self, location: str) -> None: ...
    def get_about(self) -> str: ...
    def get_banner(self) -> str: ...
    def get_city(self) -> str: ...
    def get_country(self) -> str: ...
    def get_created_at(self) -> int: ...
    def get_display_name(self) -> str: ...
    def get_environment(self) -> Literal["production", "demo"]: ...
    def get_email(self) -> str: ...
    def get_hashtags(self) -> List[str]: ...
    def get_locations(self) -> set[str]: ...
    def get_name(self) -> str: ...
    def get_namespace(self) -> str: ...
    def get_nip05(self) -> str: ...
    def get_phone(self) -> str: ...
    def get_picture(self) -> str: ...
    def get_profile_type(self) -> ProfileType: ...
    def get_profile_url(self) -> str: ...
    def get_public_key(self, encoding: KeyEncoding = KeyEncoding.BECH32) -> str: ...
    def get_state(self) -> str: ...
    def get_street(self) -> str: ...
    def get_website(self) -> str: ...
    def get_zip_code(self) -> str: ...
    def get_geohash(self) -> str: ...
    def is_bot(self) -> bool: ...
    def is_nip05_validated(self) -> bool: ...
    def matches_filter(self, profile_filter: ProfileFilter) -> bool: ...
    def set_about(self, about: str) -> None: ...
    def set_banner(self, banner: str) -> None: ...
    def set_bot(self, bot: bool) -> None: ...
    def set_city(self, city: str) -> None: ...
    def set_country(self, country: str) -> None: ...
    def set_created_at(self, created_at: int) -> None: ...
    def set_display_name(self, display_name: str) -> None: ...
    def set_environment(self, environment: Literal["production", "demo"]) -> None: ...
    def set_email(self, email: str) -> None: ...
    def set_name(self, name: str) -> None: ...
    def set_namespace(self, namespace: Union[Namespace, str]) -> None: ...
    def set_nip05(self, nip05: str) -> None: ...
    def set_phone(self, phone: str) -> None: ...
    def set_picture(self, picture: str) -> None: ...
    def set_profile_type(self, profile_type: Union[ProfileType, str]) -> None: ...
    def set_state(self, state: str) -> None: ...
    def set_street(self, street: str) -> None: ...
    def set_website(self, website: str) -> None: ...
    def set_zip_code(self, zip_code: str) -> None: ...
    def set_geohash(self, geohash: str) -> None: ...
    def to_dict(self) -> dict: ...
    def to_json(self) -> str: ...
    def __eq__(self, other: object) -> bool: ...
    def __hash__(self) -> int: ...
    async def _fetch_nip05_metadata(self, nip05: str) -> dict: ...
    async def _validate_profile_nip05(self) -> bool: ...
    def _validate_url(self, url: str) -> str: ...
    @classmethod
    async def from_metadata(cls, metadata: Metadata, public_key: str) -> "Profile": ...
    @classmethod
    async def from_event(cls, event: Event) -> "Profile": ...
    @classmethod
    def from_json(cls, json_str: str) -> "Profile": ...
    @staticmethod
    def _normalize_hashtag(tag: str) -> str: ...

class NostrKeys(BaseModel):
    """
    NostrKeys is a class that contains a public and private key.
    """

    model_config = ConfigDict(arbitrary_types_allowed=True)
    keys: Keys

    def __init__(self, private_key: Optional[str] = None) -> None: ...
    def get_public_key(self, encoding: KeyEncoding = KeyEncoding.BECH32) -> str: ...
    def get_private_key(self, encoding: KeyEncoding = KeyEncoding.BECH32) -> str: ...
    def to_json(self, encoding: KeyEncoding = KeyEncoding.BECH32) -> str: ...
    def __str__(self) -> str: ...
    @classmethod
    def from_private_key(cls, private_key: str) -> "NostrKeys": ...
    @classmethod
    def derive_public_key(
        cls, private_key: str, encoding: KeyEncoding = KeyEncoding.BECH32
    ) -> str: ...

def _extract_nostr_tags(event: Event) -> List[List[str]]: ...

class ClassifiedListing(BaseModel):
    """
    Represents a marketplace classified listing (kind 30402).
    """

    model_config = ConfigDict(arbitrary_types_allowed=True)

    id: str
    title: str
    description: str
    price_amount: Optional[float]
    price_currency: Optional[str]
    price_frequency: Optional[str]
    listing_type: str
    listing_format: str
    visibility: str
    stock: Optional[int]
    summary: Optional[str]
    images: List[Dict[str, Optional[str]]]
    specs: List[List[str]]
    weight_value: Optional[str]
    weight_unit: Optional[str]
    dimensions_value: Optional[str]
    dimensions_unit: Optional[str]
    location: Optional[str]
    geohash: Optional[str]
    categories: List[str]
    shipping_options: List[Dict[str, Optional[str]]]
    collections: List[str]
    seller: str

    def set_seller(self, seller: str) -> None: ...
    def get_seller(self) -> str: ...
    def to_dict(self) -> dict: ...
    def to_json(self) -> str: ...
    @classmethod
    def from_event(cls, event: Event) -> "ClassifiedListing": ...

class Collection(BaseModel):
    """
    Represents a product collection (kind 30405).
    """

    model_config = ConfigDict(arbitrary_types_allowed=True)

    id: str
    title: str
    description: str
    product_references: List[str]
    image: Optional[str]
    summary: Optional[str]
    location: Optional[str]
    geohash: Optional[str]
    shipping_options: List[Dict[str, Optional[str]]]
    author: str

    def set_author(self, author: str) -> None: ...
    def get_author(self) -> str: ...
    def to_dict(self) -> dict: ...
    def to_json(self) -> str: ...
    @classmethod
    def from_event(cls, event: Event) -> "Collection": ...

class ProductShippingCost(BaseModel):
    psc_id: str
    psc_cost: float

    def __init__(self, psc_id: str, psc_cost: float) -> None: ...
    def get_id(self) -> str: ...
    def get_cost(self) -> float: ...
    def set_id(self, psc_id: str) -> None: ...
    def set_cost(self, psc_cost: float) -> None: ...
    def to_dict(self) -> dict: ...
    def to_json(self) -> str: ...
    def __str__(self) -> str: ...

class StallShippingMethod(BaseModel):
    """
    Represents a shipping method for a stall.
    """

    ssm_id: str
    ssm_cost: float
    ssm_name: str
    ssm_regions: List[str] = Field(default_factory=list)

    def __init__(
        self,
        ssm_id: str,
        ssm_cost: float,
        ssm_name: str,
        ssm_regions: Optional[List[str]] = None,
    ) -> None: ...
    def get_id(self) -> str: ...
    def get_cost(self) -> float: ...
    def get_name(self) -> str: ...
    def get_regions(self) -> List[str]: ...
    def set_id(self, ssm_id: str) -> None: ...
    def set_cost(self, ssm_cost: float) -> None: ...
    def set_name(self, ssm_name: str) -> None: ...
    def set_regions(self, ssm_regions: List[str]) -> None: ...
    def to_dict(self) -> dict: ...
    def to_json(self) -> str: ...
    def __str__(self) -> str: ...

class Product(BaseModel):
    model_config = ConfigDict(arbitrary_types_allowed=True)

    id: str
    stall_id: str
    name: str
    description: str
    images: List[str]
    currency: str
    price: float
    quantity: int
    shipping: List[ProductShippingCost]
    categories: List[str] = Field(default_factory=list)
    specs: List[List[str]] = Field(default_factory=list)
    seller: str

    def set_seller(self, seller: str) -> None: ...
    def get_seller(self) -> str: ...
    @classmethod
    def from_product_data(cls, product_data: ProductData) -> "Product": ...
    def to_product_data(self) -> ProductData: ...
    def to_dict(self) -> dict: ...
    def to_json(self) -> str: ...
    @classmethod
    def from_json(cls, json_str: str) -> "Product": ...
    def __eq__(self, other: object) -> bool: ...

class Stall(BaseModel):
    """
    Stall represents a NIP-15 stall.
    TBD: NIP-15 does not have a geohash field. Add logic to retrieve geohash from
    somewhere else when using the from_stall_data() class constructor.
    """

    model_config = ConfigDict(arbitrary_types_allowed=True)

    id: str
    name: str
    description: str
    currency: str
    shipping: List[StallShippingMethod]
    geohash: Optional[str] = None

    def get_geohash(self) -> str: ...
    def set_geohash(self, geohash: str) -> None: ...
    def to_dict(self) -> dict: ...
    def to_json(self) -> str: ...
    def to_stall_data(self) -> StallData: ...
    @classmethod
    def from_json(cls, stall_content: str) -> "Stall": ...

# class Delegation(BaseModel):
#     author: str
#     conditions: str
#     sig: str
#     tag: list[str]
#     created_at: int
#     expires_at: int
#     allowed_kinds: Set[int]

#     @classmethod
#     def parse(cls, raw: str | dict) -> "Delegation": ...
#     def validate_event(self, event: Event) -> None: ...
#     @property
#     def delegation_tag(self) -> list[str]: ...
