from datetime import datetime
from zoneinfo import ZoneInfo
from sqlalchemy import Integer, ForeignKey, Index, Text, DECIMAL, BigInteger, Boolean, String, DateTime
from sqlalchemy.orm import Mapped, mapped_column, relationship
from sqlalchemy.ext.hybrid import hybrid_property
from .base import Base
from typing import Optional

class User(Base):
    __tablename__ = "users"

    telegram_id: Mapped[int] = mapped_column(BigInteger, unique=True, primary_key=True)
    username: Mapped[str] = mapped_column(String(64), nullable=True, index=True)
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), 
        default=lambda: datetime.now(ZoneInfo("Europe/Moscow")),
        nullable=False
    )
    ban: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    ref_id: Mapped[Optional[int]] = mapped_column(BigInteger, nullable=True, index=True)
    balance: Mapped[float] = mapped_column(BigInteger, default=0, nullable=False)
    
    subscriptions: Mapped[list["VPNSubscription"]] = relationship(
        back_populates="user",
        lazy="selectin"  # Optimize loading of related subscriptions
    )
    payments: Mapped[list["Payment"]] = relationship(
        back_populates="user",
        lazy="selectin"  # Optimize loading of related payments
    )
    orders: Mapped[list["Order"]] = relationship(
        back_populates="user",
        lazy="selectin"  # Optimize loading of related orders
    )

    __table_args__ = (
        Index('idx_user_telegram_username', 'telegram_id', 'username'),
    )

class VPNSubscription(Base):
    __tablename__ = "vpn_subscriptions"

    id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
    user_id: Mapped[int] = mapped_column(BigInteger, ForeignKey("users.telegram_id"), nullable=False)
    config_data: Mapped[str] = mapped_column(Text, nullable=True)
    start_date: Mapped[datetime] = mapped_column(
        DateTime(timezone=True),
        default=lambda: datetime.now(ZoneInfo("Europe/Moscow")),
        nullable=False
    )
    end_date: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
    plan_id: Mapped[int] = mapped_column(Integer, ForeignKey("vpn_plans.id"), nullable=False)
    server_name: Mapped[str] = mapped_column(String(64), nullable=True)
    status: Mapped[str] = mapped_column(String(20), default="active", nullable=False)

    user: Mapped["User"] = relationship(back_populates="subscriptions", lazy="selectin")
    plan: Mapped["VPNPlan"] = relationship(back_populates="subscriptions", lazy="selectin")

    __table_args__ = (
        Index('idx_subscription_user_dates', 'user_id', 'start_date', 'end_date'),
        Index('idx_subscription_status', 'status'),
    )

    @hybrid_property
    def is_active(self) -> bool:
        now = datetime.now(ZoneInfo("Europe/Moscow"))
        # Ensure end_date is timezone-aware
        end_date = self.end_date if self.end_date.tzinfo else self.end_date.replace(tzinfo=ZoneInfo("Europe/Moscow"))
        return now <= end_date

class Payment(Base):
    __tablename__ = "payments"

    id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
    user_id: Mapped[int] = mapped_column(BigInteger, ForeignKey("users.telegram_id"), nullable=False)
    amount: Mapped[float] = mapped_column(DECIMAL(10, 2), nullable=False)  # More precise for financial data
    payment_id: Mapped[str] = mapped_column(String(64), unique=True, nullable=True)
    status: Mapped[str] = mapped_column(
        String(20),
        default="pending",
        nullable=False
    )
    provider: Mapped[str] = mapped_column(String(64), nullable=False)
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True),
        default=lambda: datetime.now(ZoneInfo("Europe/Moscow")),
        nullable=False
    )
    paid_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)
    
    user: Mapped["User"] = relationship(back_populates="payments", lazy="selectin")

    __table_args__ = (
        Index('idx_payment_user_status', 'user_id', 'status'),
        Index('idx_payment_dates', 'created_at', 'paid_at'),
    )

class Order(Base):
    __tablename__ = "orders"

    id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
    user_id: Mapped[int] = mapped_column(BigInteger, ForeignKey("users.telegram_id"), nullable=False)
    amount: Mapped[float] = mapped_column(DECIMAL(10, 2), nullable=False)
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True),
        default=lambda: datetime.now(ZoneInfo("Europe/Moscow")),
        nullable=False
    )
    type_of_order: Mapped[str] = mapped_column(String(64), default="buy", nullable=False) # buy, referal_payment, extend_subscription
    product: Mapped[str] = mapped_column(String(64), default="vpn_subscription", nullable=False) # vpn_subscription, or another product
    product_info: Mapped[str] = mapped_column(Text, nullable=True) # json with product info

    user: Mapped["User"] = relationship(back_populates="orders", lazy="selectin")

    __table_args__ = (
        Index('idx_order_user_dates', 'user_id', 'created_at'),
        Index('idx_order_type', 'type_of_order'),
    )

class VPNPlan(Base):
    __tablename__ = "vpn_plans"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    name: Mapped[str] = mapped_column(String(64), nullable=False)
    duration_days: Mapped[int] = mapped_column(Integer, nullable=False)
    price: Mapped[float] = mapped_column(DECIMAL(10, 2), nullable=False)  # More precise for financial data
    description: Mapped[str] = mapped_column(Text, nullable=True)

    subscriptions: Mapped[list["VPNSubscription"]] = relationship(
        back_populates="plan",
        lazy="selectin"  # Optimize loading of related subscriptions
    )

    __table_args__ = (
        Index('idx_plan_price', 'price'),
        Index('idx_plan_duration', 'duration_days'),
    )
