from datetime import datetime
from typing import Annotated, List, Literal, Optional
from uuid import UUID

from pydantic import (
    BaseModel,
    EmailStr,
    Field,
    RootModel,
    StringConstraints,
)

from remnawave.enums import TrafficLimitStrategy, UserStatus
from remnawave.utils.happ_crypt import create_happ_crypto_link


class UserActiveInboundsDto(BaseModel):
    uuid: UUID
    tag: str
    type: str
    network: str | None = None
    security: str | None = None


class UserLastConnectedNodeDto(BaseModel):
    connected_at: datetime = Field(alias="connectedAt")
    node_name: str = Field(alias="nodeName")


class ActiveInternalSquadDto(BaseModel):
    uuid: UUID
    name: str


class HappCrypto(BaseModel):
    crypto_link: str = Field(alias="cryptoLink")


class CreateUserRequestDto(BaseModel):
    """Request DTO for creating a user"""
    # Required fields
    username: Annotated[
        str, 
        StringConstraints(pattern=r"^[a-zA-Z0-9_-]+$", min_length=3, max_length=36)
    ] = Field(..., description="Unique username for the user")
    expire_at: datetime = Field(..., serialization_alias="expireAt", description="Account expiration date")
    
    # Optional fields with defaults
    status: UserStatus = Field(default=UserStatus.ACTIVE, description="User account status")
    traffic_limit_strategy: TrafficLimitStrategy = Field(
        default=TrafficLimitStrategy.NO_RESET,
        serialization_alias="trafficLimitStrategy",
        description="Traffic reset strategy"
    )
    
    # Optional fields
    short_uuid: Optional[str] = Field(None, serialization_alias="shortUuid")
    trojan_password: Optional[Annotated[str, StringConstraints(min_length=8, max_length=32)]] = Field(
        None, serialization_alias="trojanPassword"
    )
    vless_uuid: Optional[UUID] = Field(None, serialization_alias="vlessUuid")
    ss_password: Optional[Annotated[str, StringConstraints(min_length=8, max_length=32)]] = Field(
        None, serialization_alias="ssPassword"
    )
    traffic_limit_bytes: Optional[int] = Field(
        None, serialization_alias="trafficLimitBytes", ge=0
    )
    created_at: Optional[datetime] = Field(None, serialization_alias="createdAt")
    last_traffic_reset_at: Optional[datetime] = Field(None, serialization_alias="lastTrafficResetAt")
    description: Optional[str] = None
    tag: Optional[Annotated[str, StringConstraints(max_length=16, pattern=r"^[A-Z0-9_]+$")]] = None
    telegram_id: Optional[int] = Field(None, serialization_alias="telegramId")
    email: Optional[EmailStr] = None
    hwid_device_limit: Optional[int] = Field(None, serialization_alias="hwidDeviceLimit", ge=0)
    active_internal_squads: Optional[List[UUID]] = Field(None, serialization_alias="activeInternalSquads")
    uuid: Optional[UUID] = Field(
        None, 
        description="Optional. Pass UUID to create user with specific UUID, otherwise it will be generated automatically."
    )
    external_squad_uuid: Optional[UUID] = Field(None, serialization_alias="externalSquadUuid")


class UpdateUserRequestDto(BaseModel):
    """Request DTO for updating a user"""
    # Either username or uuid must be provided, uuid has priority
    username: Optional[str] = Field(None, description="Username of the user")
    uuid: Optional[UUID] = Field(
        None, 
        description="UUID of the user. UUID has higher priority than username"
    )
    
    # Optional update fields
    status: Optional[UserStatus] = None
    description: Optional[str] = None
    email: Optional[EmailStr] = None
    expire_at: Optional[datetime] = Field(None, serialization_alias="expireAt")
    hwid_device_limit: Optional[int] = Field(None, serialization_alias="hwidDeviceLimit", ge=0)
    tag: Optional[Annotated[str, StringConstraints(max_length=16, pattern=r"^[A-Z0-9_]+$")]] = None
    telegram_id: Optional[int] = Field(None, serialization_alias="telegramId")
    traffic_limit_bytes: Optional[int] = Field(None, serialization_alias="trafficLimitBytes", ge=0)
    traffic_limit_strategy: Optional[TrafficLimitStrategy] = Field(
        None, serialization_alias="trafficLimitStrategy"
    )
    active_internal_squads: Optional[List[UUID]] = Field(
        None, serialization_alias="activeInternalSquads"
    )
    external_squad_uuid: Optional[UUID] = Field(None, serialization_alias="externalSquadUuid")


class UserTrafficDto(BaseModel):
    """User traffic information"""
    used_traffic_bytes: int = Field(alias="usedTrafficBytes")
    lifetime_used_traffic_bytes: int = Field(alias="lifetimeUsedTrafficBytes")
    online_at: Optional[datetime] = Field(None, alias="onlineAt")
    first_connected_at: Optional[datetime] = Field(None, alias="firstConnectedAt")
    last_connected_node_uuid: Optional[UUID] = Field(None, alias="lastConnectedNodeUuid")


class UserResponseDto(BaseModel):
    """User response DTO - обновленная структура с userTraffic"""
    uuid: UUID
    id: int
    short_uuid: str = Field(alias="shortUuid")
    username: str
    status: UserStatus = Field(default=UserStatus.ACTIVE)
    traffic_limit_bytes: int = Field(0, alias="trafficLimitBytes")
    traffic_limit_strategy: TrafficLimitStrategy = Field(
        TrafficLimitStrategy.NO_RESET, alias="trafficLimitStrategy"
    )
    expire_at: datetime = Field(alias="expireAt")
    telegram_id: Optional[int] = Field(None, alias="telegramId")
    email: Optional[str] = None
    description: Optional[str] = None
    tag: Optional[str] = None
    hwid_device_limit: Optional[int] = Field(None, alias="hwidDeviceLimit")
    external_squad_uuid: Optional[UUID] = Field(None, alias="externalSquadUuid")
    trojan_password: str = Field(alias="trojanPassword")
    vless_uuid: UUID = Field(alias="vlessUuid")
    ss_password: str = Field(alias="ssPassword")
    last_trigger_threshold: int = Field(0, alias="lastTriggeredThreshold")
    sub_revoked_at: Optional[datetime] = Field(None, alias="subRevokedAt")
    sub_last_user_agent: Optional[str] = Field(None, alias="subLastUserAgent")
    sub_last_opened_at: Optional[datetime] = Field(None, alias="subLastOpenedAt")
    last_traffic_reset_at: Optional[datetime] = Field(None, alias="lastTrafficResetAt")
    created_at: datetime = Field(alias="createdAt")
    updated_at: datetime = Field(alias="updatedAt")
    subscription_url: str = Field(alias="subscriptionUrl")
    active_internal_squads: list[ActiveInternalSquadDto] = Field(alias="activeInternalSquads")
    user_traffic: UserTrafficDto = Field(alias="userTraffic")
    
    @property
    def used_traffic_bytes(self) -> int:
        """Backward compatibility property"""
        return self.user_traffic.used_traffic_bytes
    
    @property
    def lifetime_used_traffic_bytes(self) -> int:
        """Backward compatibility property"""
        return self.user_traffic.lifetime_used_traffic_bytes
    
    @property
    def online_at(self) -> Optional[datetime]:
        """Backward compatibility property"""
        return self.user_traffic.online_at
    
    @property
    def first_connected(self) -> Optional[datetime]:
        """Backward compatibility property"""
        return self.user_traffic.first_connected_at
    
    @property
    def last_connected_node_uuid(self) -> Optional[UUID]:
        """Backward compatibility property"""
        return self.user_traffic.last_connected_node_uuid
    
    @property
    def happ(self) -> HappCrypto:
        """Generate Happ Crypto Link"""
        crypto_link = create_happ_crypto_link(self.subscription_url)
        return HappCrypto(cryptoLink=crypto_link)
    
    def happ_with_version(self, version: Literal["v3", "v4"] = "v4") -> HappCrypto:
        return self._generate_happ(version=version)

    def _generate_happ(self, version):
        crypto_link = create_happ_crypto_link(content=self.subscription_url, method=version)
        return HappCrypto(cryptoLink=crypto_link)


class RevokeUserRequestDto(BaseModel):
    """Request DTO for revoking user subscription"""
    short_uuid: Optional[str] = Field(
        None,
        serialization_alias="shortUuid",
        description="Optional. If not provided, a new short UUID will be generated by Remnawave.",
        min_length=6,
        max_length=48,
        pattern=r"^[a-zA-Z0-9_-]+$",
    )
    revoke_only_passwords: Optional[bool] = Field(
        None,
        serialization_alias="revokeOnlyPasswords",
        description="Optional. If true, only passwords will be revoked without changing the short UUID.",
    )

class SubscriptionRequestRecord(BaseModel):
    """Subscription request history record"""
    id: int
    user_uuid: UUID = Field(alias="userUuid")
    request_at: datetime = Field(alias="requestAt")
    request_ip: Optional[str] = Field(alias="requestIp")
    user_agent: Optional[str] = Field(alias="userAgent")


class SubscriptionRequestsResponseData(BaseModel):
    """Subscription requests response data"""
    total: int
    records: List[SubscriptionRequestRecord]


class CreateUserResponseDto(UserResponseDto):
    """Response for create user"""
    pass


class UpdateUserResponseDto(UserResponseDto):
    """Response for update user"""
    pass


class GetUserByUuidResponseDto(UserResponseDto):
    """Response for get user by UUID"""
    pass


class GetUserByShortUuidResponseDto(UserResponseDto):
    """Response for get user by short UUID"""
    pass


class GetUserByUsernameResponseDto(UserResponseDto):
    """Response for get user by username"""
    pass


class GetUserByIdResponseDto(UserResponseDto):
    """Response for get user by ID"""
    pass


class DisableUserResponseDto(UserResponseDto):
    """Response for disable user"""
    pass


class EnableUserResponseDto(UserResponseDto):
    """Response for enable user"""
    pass


class ResetUserTrafficResponseDto(UserResponseDto):
    """Response for reset user traffic"""
    pass


class RevokeUserSubscriptionResponseDto(UserResponseDto):
    """Response for revoke user subscription"""
    pass


class ActivateAllInboundsResponseDto(UserResponseDto):
    """Response for activate all inbounds"""
    pass


class UsersResponseDto(BaseModel):
    """Users collection response"""
    users: list[UserResponseDto]
    total: int


class GetAllUsersResponseDto(UsersResponseDto):
    """Response for get all users"""
    pass


class TagsResponseDto(BaseModel):
    """Tags collection response"""
    tags: list[str]


class GetAllTagsResponseDto(TagsResponseDto):
    """Response for get all tags"""
    pass


class GetSubscriptionRequestsResponseDto(SubscriptionRequestsResponseData):
    """Response for get subscription requests"""
    pass


class GetUserSubscriptionRequestHistoryResponseDto(SubscriptionRequestsResponseData):
    """Response for get user subscription request history"""
    pass


class DeleteUserResponseDto(BaseModel):
    """Response for delete user"""
    is_deleted: bool = Field(alias="isDeleted")


class EmailUserResponseDto(RootModel[list[UserResponseDto]]):
    """Response for get users by email"""
    def __iter__(self):
        return iter(self.root)

    def __getitem__(self, item):
        return self.root[item]
    
    def __bool__(self):
        """Return True if list is not empty"""
        return bool(self.root)
    
    def __len__(self):
        """Return length of list"""
        return len(self.root)


class TagUserResponseDto(RootModel[list[UserResponseDto]]):
    """Response for get users by tag"""
    def __iter__(self):
        return iter(self.root)

    def __getitem__(self, item):
        return self.root[item]
    
    def __bool__(self):
        """Return True if list is not empty"""
        return bool(self.root)
    
    def __len__(self):
        """Return length of list"""
        return len(self.root)


class TelegramUserResponseDto(RootModel[list[UserResponseDto]]):
    """Response for get users by telegram ID"""
    def __iter__(self):
        return iter(self.root)

    def __getitem__(self, item):
        return self.root[item]
    
    def __bool__(self):
        """Return True if list is not empty"""
        return bool(self.root)
    
    def __len__(self):
        """Return length of list"""
        return len(self.root)