from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy import Integer, String, DateTime, ForeignKey, Boolean, Text, Column
from sqlalchemy.orm import relationship, Mapped, mapped_column
from sqlalchemy.sql import func, case
from sqlalchemy.sql.schema import Table
from sqlalchemy.ext.hybrid import hybrid_property
from src.core.config import settings
from src.core.utils.enums import InvoiceStatusTypes
from src.apps.base.schemas.common import AddressSchema
from typing import Dict, List, Optional, TYPE_CHECKING
from src.apps.users.schemas.user_common import UserSchema
from src.apps.base.models.base import Base
import uuid
from sqlalchemy.orm.session import Session
from src.core.database import SessionCelery
from datetime import datetime

if TYPE_CHECKING:
    # Forward declarations for type checking only
    from typing import Any
    from src.apps.merchants.models.merchant_settings import MerchantSettings
    from src.apps.invoices.models.invoice import Invoice
    File = Any
    Address = Any
    MerchantUsers = Any
    Transactions = Any
    Subscription = Any
    PaymentRequestAuthorizations = Any
    MerchantPaymentHistory = Any
    User = Any
else:
    # Runtime imports to avoid circular imports
    MerchantSettings = None
    Invoice = None

merchant_address_map = Table(
    "merchant_addresses",
    Base.metadata,
    Column("merchant_id", ForeignKey("merchants.id")),
    Column("address_id", ForeignKey("address.id")),
)


class Merchant(Base):
    """
    Merchant Model: ORM class for Merchant Entity
    """

    __tablename__ = "merchants"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True, autoincrement=True)
    uuid: Mapped[UUID] = mapped_column(UUID(as_uuid=True), default=uuid.uuid4, index=True, unique=True)
    uin: Mapped[str] = mapped_column(String(255), index=True, unique=True)
    merchant_id: Mapped[Optional[str]] = mapped_column(String(50), index=True, unique=True, nullable=True)
    name: Mapped[str] = mapped_column(String(255))
    tagline: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
    industry: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
    email: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
    phone: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
    registration_no: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
    license_no: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
    is_onboarded: Mapped[bool] = mapped_column(Boolean, default=False)
    is_active: Mapped[bool] = mapped_column(Boolean, default=False)
    is_verified: Mapped[bool] = mapped_column(Boolean, default=False)
    created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now())
    updated_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True, onupdate=func.now())
    deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
    tilled_status: Mapped[Optional[str]] = mapped_column(String(30), nullable=True)
    tilled_account_id: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
    tilled_customer_id: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
    subdomain: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
    custom_domain: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
    tnc_accepted: Mapped[bool] = mapped_column(Boolean, default=False)
    dob: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
    approved_by: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("users.id"), nullable=True)
    approved_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
    merchant_literal: Mapped[Optional[str]] = mapped_column(Text, nullable=True)

    brand_logo_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("files.id"), nullable=True)
    brand_logo: Mapped[Optional["File"]] = relationship("File", lazy="joined")

    # TODO: Uncomment when Transactions model is created
    transactions: Mapped[List["Transactions"]] = relationship("Transactions", back_populates="merchant")

    addresses: Mapped[List["Address"]] = relationship("Address", secondary=merchant_address_map)
    users: Mapped[List["MerchantUsers"]] = relationship("MerchantUsers", back_populates="merchant")
    invoices: Mapped[List["Invoice"]] = relationship("Invoice", back_populates="merchant")
    settings: Mapped[List["MerchantSettings"]] = relationship("MerchantSettings", back_populates="merchant")
    # TODO: Uncomment when Subscription model is created
    # subscriptions: Mapped[List["Subscription"]] = relationship("Subscription", back_populates="merchant")

    payment_request_authorizations: Mapped[List["PaymentRequestAuthorizations"]] = relationship(
        "PaymentRequestAuthorizations", back_populates="merchant"
    )
    merchant_payment_history: Mapped[List["MerchantPaymentHistory"]] = relationship(
        "MerchantPaymentHistory", back_populates="merchant"
    )

    @hybrid_property
    def owner(self) -> UserSchema:
        if self.users and len(self.users) > 0:
            owner_map = next(user for user in self.users if user.is_owner)
            return UserSchema(**owner_map.user._asdict(include_properties=False))
        else:
            return None

    @hybrid_property
    def default_address(self) -> AddressSchema:
        if self.addresses and len(self.addresses) > 0:
            default_address_map = next(
                (address for address in self.addresses if address.use_as_default), None
            )
            if default_address_map is None:
                default_address_map = self.addresses[0]
            return AddressSchema(
                **default_address_map._asdict(include_properties=False)
            )
        else:
            return None

    @hybrid_property
    def active_domain(self) -> str:
        try:
            http_protocol: str = "https" if settings.USE_SSL == "true" else "http"
            if self.subdomain and self.subdomain.strip():
                return f"{http_protocol}://{self.subdomain.strip()}"
            elif self.custom_domain and self.custom_domain.strip():
                return f"{http_protocol}://{self.custom_domain.strip()}"
            else:
                return str(settings.SERVER_FRONT) if settings.SERVER_FRONT else "http://localhost:3000"
        except Exception:
            return "http://localhost:3000"


    @hybrid_property
    def logo_url(self) -> str:
        try:
            from src.apps.merchants.models.merchant_settings import MerchantSettings
            with SessionCelery() as db:
                brand_logo = (
                    db.query(MerchantSettings)
                    .filter(
                        MerchantSettings.merchant_id == self.id,
                        MerchantSettings.field_name == "branding_info_logo",
                    )
                    .first()
                )
                if brand_logo:
                    return brand_logo.value
                return ""
        except ImportError:
            return ""

    @hybrid_property
    def preferences(self) -> Dict:
        def _get_setting_value(field_name: str):
            return next(
                (rec.value for rec in self.settings if rec.field_name == field_name),
                None,
            )

        return dict(
            country=_get_setting_value("info_country"),
            language=_get_setting_value("info_language"),
            timezone=_get_setting_value("info_timezone"),
            date_format=_get_setting_value("info_date_format"),
            time_format=_get_setting_value("info_time_format"),
        )

    @hybrid_property
    def status(self) -> str:
        text = None
        if self.is_active:
            if self.is_verified:
                if self.is_onboarded:
                    if self.approved_at:
                        text = "active"
                    else:
                        text = "pending_approval"
                    # TODO: Uncomment when Subscription model is created
                    # Check if there are any active subscriptions and if the active one is cancelled
                    # if self.subscriptions and len(self.subscriptions) > 0:
                    #     active_subscription = max(
                    #         (sub for sub in self.subscriptions if not sub.deleted_at),
                    #         key=lambda s: s.started_at,
                    #         default=None,
                    #     )
                    #     if active_subscription and active_subscription.cancelled_at:
                    #         text = "cancelled"
                    #     # If the subscription is in status fail then send the status as "expired".
                    #     elif (
                    #         active_subscription
                    #         and active_subscription.status == "expired"
                    #     ):
                    #         text = "expired"
                else:
                    text = "onboarding"
                    if self.tilled_status == "rejected":
                        text = "rejected"
                    elif self.tilled_status == "in_review":
                        text = "in_review"
            else:
                text = "pending_verfication"
        else:
            text = "inactive"

        return text

    @hybrid_property
    def avg_volume(self) -> float:
        """Calculate the average paid amount for the merchant"""
        try:
            from src.apps.invoices.models.invoice import Invoice
            with SessionCelery() as db:
                total_avg = (
                    db.query(func.avg(Invoice.paid_amount))
                    .join(Invoice.merchant)
                    .filter(Invoice.merchant_id == self.id)
                    .scalar()
                )
                return total_avg if total_avg else 0.0
        except ImportError:
            return 0.0
        
    @hybrid_property
    def mtd_volume(self) -> float:
        """Calculate the avg mtd paid amount for the merchant"""
        try:
            from src.apps.invoices.models.invoice import Invoice
            with SessionCelery() as db:
                current_month = datetime.utcnow().month
                current_year = datetime.utcnow().year
                start_of_month = datetime(current_year, current_month, 1)
                current_avg = (
                    db.query(func.avg(Invoice.paid_amount))
                    .join(Invoice.merchant)
                    .filter(
                        Invoice.merchant_id == self.id,
                        Invoice.paid_date >= start_of_month,
                        Invoice.paid_date <= datetime.utcnow()
                    )
                    .scalar()
                )
                return current_avg if current_avg else 0.0
        except ImportError:
            return 0.0
