from sqlalchemy import Integer, String, DateTime, Boolean, Text, Column, ForeignKey
from sqlalchemy.sql import func
from sqlalchemy.sql.schema import Table
from sqlalchemy.orm import relationship, Mapped, mapped_column
from src.core.utils.enums import AddressTypes, InvoiceStatusTypes
from typing import Dict, List, Optional, TYPE_CHECKING
from src.apps.base.schemas.common import AddressSchema
from src.apps.base.models.base import Base
from sqlalchemy.ext.hybrid import hybrid_property
from src.apps.users.schemas.user_common import UserSchema
from datetime import datetime

if TYPE_CHECKING:
    from typing import Any
    from src.apps.base.models.address import Address
    from src.apps.files.models.file import File
    from src.apps.transactions.models.transactions import Transactions
    from src.apps.customers.models.customer_contact import CustomerContact
    from src.apps.merchants.models.merchant import Merchant
    from src.apps.payment_methods.models.payment_methods import PaymentMethod
    from src.apps.users.models.user import User
    from src.apps.customers.models.customer_users import CustomerUsers
    from src.apps.payment_requests.models.payment_request_authorizations import PaymentRequestAuthorizations
    from src.apps.notes.models.note import Note
    from src.apps.invoices.models.invoice import Invoice

customer_address_map = Table(
    "customer_addresses",
    Base.metadata,
    Column("customer_id", ForeignKey("customers.id")),
    Column("address_id", ForeignKey("address.id")),
)

customer_attachments_map = Table(
    "customer_attachments",
    Base.metadata,
    Column("customer_id", ForeignKey("customers.id")),
    Column("file_id", ForeignKey("files.id")),
)

customer_notes_map = Table(
    "customer_notes",
    Base.metadata,
    Column("customer_id", ForeignKey("customers.id")),
    Column("note_id", ForeignKey("notes.id")),
)


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

    __tablename__ = "customers"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True, autoincrement=True)
    uin: Mapped[str] = mapped_column(String(255), index=True, unique=True)
    first_name: Mapped[Optional[str]] = mapped_column(String(32), nullable=True)
    middle_name: Mapped[Optional[str]] = mapped_column(String(32), nullable=True)
    last_name: Mapped[Optional[str]] = mapped_column(String(32), nullable=True)
    email: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
    phone: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
    customer_id: Mapped[Optional[str]] = mapped_column(String(50), index=True, unique=True, nullable=True)
    account_type: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
    customer_type: Mapped[Optional[str]] = mapped_column(String(50), nullable=True)
    industry: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
    business_legal_name: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
    office_phone: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
    account_expires_on: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
    timezone: Mapped[Optional[str]] = mapped_column(String(32), nullable=True)
    is_active: Mapped[bool] = mapped_column(Boolean, default=False)
    is_vendor: 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_id: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
    website: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
    account_tax_id: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
    tags: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
    account_literal: Mapped[Optional[str]] = mapped_column(Text, nullable=True)

    addresses: Mapped[List["Address"]] = relationship("Address", back_populates="customer")
    attachments: Mapped[List["File"]] = relationship("File", secondary=customer_attachments_map)
    transactions: Mapped[List["Transactions"]] = relationship("Transactions", back_populates="customer")
    contacts: Mapped[List["CustomerContact"]] = relationship("CustomerContact", back_populates="customer")

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

    merchant_id: Mapped[int] = mapped_column(Integer, ForeignKey("merchants.id"))
    merchant: Mapped["Merchant"] = relationship("Merchant")

    payment_methods: Mapped[List["PaymentMethod"]] = relationship("PaymentMethod", back_populates="customer")
    
    user_account_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("users.id"), nullable=True)
    user_account: Mapped[Optional["User"]] = relationship("User")

    users: Mapped[List["CustomerUsers"]] = relationship("CustomerUsers", back_populates="customer")

    payment_request_authorizations: Mapped[List["PaymentRequestAuthorizations"]] = relationship(
        "PaymentRequestAuthorizations", back_populates="customer"
    )
    notes: Mapped[List["Note"]] = relationship(
        "Note", secondary=customer_notes_map, back_populates="customer"
    )

    invoices: Mapped[List["Invoice"]] = relationship("Invoice", back_populates="customer")

    # @hybrid_property
    # def upcoming_invoice_amount(self) -> float:
    #     """Calculate the total pending payment for the customer"""
    #     # Temporarily commented out due to import issues
    #     return 0.0

    # @hybrid_property
    # def past_due_amount(self) -> float:
    #     """Calculate the total pending payment for the customer"""
    #     # Temporarily commented out due to import issues
    #     return 0.0

    @hybrid_property
    def is_registered(self) -> bool:
        return self.user_account_id is not None

    # @hybrid_property
    # def user(self) -> UserSchema:
    #     # Temporarily commented out due to import issues
    #     return None

    @hybrid_property
    def tags_list(self) -> List[str]:
        if self.tags:
            return self.tags.split(",")
        return []

    @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 shipping_address(self) -> AddressSchema:
        if self.addresses and len(self.addresses) > 0:
            shipping_address_map = next(
                (
                    address
                    for address in self.addresses
                    if address.address_type == AddressTypes.SHIPPING
                ),
                None,
            )
            if shipping_address_map is None:
                return self.default_address
            return AddressSchema(
                **shipping_address_map._asdict(include_properties=False)
            )
        else:
            return None

    @hybrid_property
    def billing_address(self) -> AddressSchema:
        if self.addresses and len(self.addresses) > 0:
            billing_address_map = next(
                (
                    address
                    for address in self.addresses
                    if address.address_type == AddressTypes.BILLING
                ),
                None,
            )
            if billing_address_map is None:
                return self.default_address
            return AddressSchema(
                **billing_address_map._asdict(include_properties=False)
            )
        else:
            return None

    @hybrid_property
    def last_active_date(self):
        if self.transactions:
            return max(transaction.ocurred_at for transaction in self.transactions)
        else:
            return None
