# app/modules/tenancy/models/store.py
"""
Store model representing entities that sell products or services.

This module defines the Store model along with its relationships to
other models such as User (owner), Product, Customer, and Order.

Note: MarketplaceImportJob relationships are owned by the marketplace module.
"""

from sqlalchemy import (
    JSON,
    Boolean,
    Column,
    DateTime,
    ForeignKey,
    Index,
    Integer,
    String,
    Text,
)
from sqlalchemy.orm import relationship

from app.core.config import settings

# Import Base from the central database module instead of creating a new one
from app.core.database import Base
from models.database.base import SoftDeleteMixin, TimestampMixin


class Store(Base, TimestampMixin, SoftDeleteMixin):
    """Represents a store in the system."""

    __tablename__ = "stores"  # Name of the table in the database
    __table_args__ = (
        Index("uq_stores_store_code_active", "store_code", unique=True, postgresql_where="deleted_at IS NULL"),
        Index("uq_stores_subdomain_active", "subdomain", unique=True, postgresql_where="deleted_at IS NULL"),
    )

    id = Column(
        Integer, primary_key=True, index=True
    )  # Primary key and indexed column for store ID

    # Merchant relationship
    merchant_id = Column(
        Integer, ForeignKey("merchants.id"), nullable=False, index=True
    )  # Foreign key to the parent merchant

    store_code = Column(
        String, index=True, nullable=False
    )  # Indexed, non-nullable store code column (unique among non-deleted)
    subdomain = Column(
        String(100), nullable=False, index=True
    )  # Non-nullable subdomain column (unique among non-deleted)
    name = Column(
        String, nullable=False
    )  # Non-nullable name column for the store (brand name)
    description = Column(Text)  # Optional text description column for the store

    # Letzshop URLs - multi-language support (brand-specific marketplace feeds)
    letzshop_csv_url_fr = Column(String)  # URL for French CSV in Letzshop
    letzshop_csv_url_en = Column(String)  # URL for English CSV in Letzshop
    letzshop_csv_url_de = Column(String)  # URL for German CSV in Letzshop

    # Letzshop Store Identity (for linking to Letzshop marketplace profile)
    letzshop_store_id = Column(
        String(100), unique=True, nullable=True, index=True
    )  # Letzshop's store identifier
    letzshop_store_slug = Column(
        String(200), nullable=True, index=True
    )  # Letzshop shop URL slug (e.g., "my-shop" from letzshop.lu/vendors/my-shop)

    # ========================================================================
    # Letzshop Feed Settings (atalanda namespace)
    # ========================================================================
    # These are default values applied to all products in the Letzshop feed
    # See https://letzshop.lu/en/dev#google_csv for documentation

    # Default VAT rate for new products: 0 (exempt), 3 (super-reduced), 8 (reduced), 14 (intermediate), 17 (standard)
    letzshop_default_tax_rate = Column(Integer, default=17, nullable=False)

    # Product sort priority on Letzshop (0.0-10.0, higher = displayed first)
    # Note: Having all products rated above 7 is not permitted by Letzshop
    letzshop_boost_sort = Column(String(10), default="5.0")  # Stored as string for precision

    # Delivery method: 'nationwide', 'package_delivery', 'self_collect' (comma-separated for multiple)
    # 'nationwide' automatically includes package_delivery and self_collect
    letzshop_delivery_method = Column(String(100), default="package_delivery")

    # Pre-order days: number of days before item ships (default 1 day)
    letzshop_preorder_days = Column(Integer, default=1)

    # Status (store-specific, can differ from merchant status)
    is_active = Column(
        Boolean, default=True
    )  # Boolean to indicate if the store brand is active
    is_verified = Column(
        Boolean, default=False
    )  # Boolean to indicate if the store brand is verified

    # ========================================================================
    # Contact Information (nullable = inherit from merchant)
    # ========================================================================
    # These fields allow store-specific branding/identity.
    # If null, the value is inherited from the parent merchant.
    contact_email = Column(String(255), nullable=True)  # Override merchant contact email
    contact_phone = Column(String(50), nullable=True)  # Override merchant contact phone
    website = Column(String(255), nullable=True)  # Override merchant website
    business_address = Column(Text, nullable=True)  # Override merchant business address
    tax_number = Column(String(100), nullable=True)  # Override merchant tax number

    # ========================================================================
    # Language Settings
    # ========================================================================
    # Supported languages: en, fr, de, lb (Luxembourgish)
    default_language = Column(
        String(5), nullable=False, default="fr"
    )  # Default language for store content (products, emails, etc.)
    dashboard_language = Column(
        String(5), nullable=False, default="fr"
    )  # Language for store team dashboard UI
    storefront_language = Column(
        String(5), nullable=False, default="fr"
    )  # Default language for customer-facing storefront
    storefront_languages = Column(
        JSON, nullable=False, default=["fr", "de", "en", "lb"]
    )  # Array of enabled languages for storefront language selector

    # Currency/number formatting locale (e.g., 'fr-LU' = "29,99 EUR", 'en-GB' = "EUR29.99")
    # NULL means inherit from platform default (AdminSetting 'default_storefront_locale')
    storefront_locale = Column(String(10), nullable=True)

    # ========================================================================
    # Relationships
    # ========================================================================
    merchant = relationship(
        "Merchant", back_populates="stores"
    )  # Relationship with Merchant model for the parent merchant
    store_users = relationship(
        "StoreUser", back_populates="store"
    )  # Relationship with StoreUser model for users in this store
    products = relationship(
        "Product", back_populates="store"
    )  # Relationship with Product model for products of this store
    customers = relationship(
        "Customer", back_populates="store"
    )  # Relationship with Customer model for customers of this store
    orders = relationship(
        "Order", back_populates="store"
    )  # Relationship with Order model for orders placed by this store
    # NOTE: marketplace_import_jobs relationship removed - owned by marketplace module
    # Use: MarketplaceImportJob.query.filter_by(store_id=store.id) instead

    # Letzshop integration credentials (one-to-one)
    letzshop_credentials = relationship(
        "StoreLetzshopCredentials",
        back_populates="store",
        uselist=False,
        cascade="all, delete-orphan",
    )

    # Invoice settings (one-to-one)
    invoice_settings = relationship(
        "StoreInvoiceSettings",
        back_populates="store",
        uselist=False,
        cascade="all, delete-orphan",
    )

    # Invoices (one-to-many)
    invoices = relationship(
        "Invoice",
        back_populates="store",
        cascade="all, delete-orphan",
    )

    # Email template overrides (one-to-many)
    email_templates = relationship(
        "StoreEmailTemplate",
        back_populates="store",
        cascade="all, delete-orphan",
    )

    # Email settings (one-to-one) - store SMTP/provider configuration
    email_settings = relationship(
        "StoreEmailSettings",
        back_populates="store",
        uselist=False,
        cascade="all, delete-orphan",
    )

    # Add-ons purchased by store (one-to-many)
    addons = relationship(
        "StoreAddOn",
        back_populates="store",
        cascade="all, delete-orphan",
    )

    # Billing/invoice history (one-to-many)
    billing_history = relationship(
        "BillingHistory",
        back_populates="store",
        cascade="all, delete-orphan",
        order_by="BillingHistory.invoice_date.desc()",
    )

    domains = relationship(
        "StoreDomain",
        back_populates="store",
        cascade="all, delete-orphan",
        order_by="StoreDomain.is_primary.desc()",
    )  # Relationship with StoreDomain model for custom domains of the store

    # Single theme relationship (ONE store = ONE theme)
    # A store has ONE active theme stored in the store_themes table.
    # Theme presets available: default, modern, classic, minimal, vibrant
    store_theme = relationship(
        "StoreTheme",
        back_populates="store",
        uselist=False,
        cascade="all, delete-orphan",
    )  # Relationship with StoreTheme model for the active theme of the store

    # Content pages relationship (store can override platform default pages)
    content_pages = relationship(
        "ContentPage", back_populates="store", cascade="all, delete-orphan"
    )  # Relationship with ContentPage model for store-specific content pages

    # Onboarding progress (one-to-one)
    onboarding = relationship(
        "StoreOnboarding",
        back_populates="store",
        uselist=False,
        cascade="all, delete-orphan",
    )

    # Media library (one-to-many)
    media_files = relationship(
        "MediaFile",
        back_populates="store",
        cascade="all, delete-orphan",
    )

    # Platform memberships (many-to-many via junction table)
    store_platforms = relationship(
        "StorePlatform",
        back_populates="store",
        cascade="all, delete-orphan",
    )

    def __repr__(self):
        """String representation of the Store object."""
        return f"<Store(id={self.id}, store_code='{self.store_code}', name='{self.name}', subdomain='{self.subdomain}')>"

    # ========================================================================
    # Theme Helper Methods to get active theme and other related information
    # ========================================================================

    def get_effective_theme(self) -> dict:
        """
        Get active theme for this store.

        Returns theme from store_themes table, or default theme if not set.

        Returns:
            dict: Theme configuration with colors, fonts, layout, etc.
        """
        # Check store_themes table
        if self.store_theme and self.store_theme.is_active:
            return self.store_theme.to_dict()

        # Return default theme
        return self._get_default_theme()

    def _get_default_theme(self) -> dict:
        """Return the default theme configuration."""
        return {
            "theme_name": "default",
            "colors": {
                "primary": "#6366f1",
                "secondary": "#8b5cf6",
                "accent": "#ec4899",
                "background": "#ffffff",
                "text": "#1f2937",
                "border": "#e5e7eb",
            },
            "fonts": {"heading": "Inter, sans-serif", "body": "Inter, sans-serif"},
            "branding": {
                "logo": None,
                "logo_dark": None,
                "favicon": None,
                "banner": None,
            },
            "layout": {"style": "grid", "header": "fixed", "product_card": "modern"},
            "social_links": {},
            "custom_css": None,
            "css_variables": {
                "--color-primary": "#6366f1",
                "--color-secondary": "#8b5cf6",
                "--color-accent": "#ec4899",
                "--color-background": "#ffffff",
                "--color-text": "#1f2937",
                "--color-border": "#e5e7eb",
                "--font-heading": "Inter, sans-serif",
                "--font-body": "Inter, sans-serif",
            },
        }

    def get_primary_color(self) -> str:
        """Get primary color from active theme."""
        theme = self.get_effective_theme()
        return theme.get("colors", {}).get(
            "primary", "#6366f1"
        )  # Default to default theme if not found

    def get_logo_url(self) -> str:
        """Get logo URL from active theme."""
        theme = self.get_effective_theme()
        return theme.get("branding", {}).get(
            "logo"
        )  # Return None or the logo URL if found

    # ========================================================================
    # Domain Helper Methods
    # ========================================================================

    @property
    def primary_domain(self):
        """Get the primary custom domain for this store."""
        for domain in self.domains:
            if domain.is_primary and domain.is_active:
                return domain.domain  # Return the domain if it's primary and active
        return None

    @property
    def effective_domain(self) -> str | None:
        """
        Get effective domain: store override > merchant domain > subdomain fallback.

        Domain Resolution Priority:
        1. Store-specific custom domain (StoreDomain) -> highest priority
        2. Merchant domain (MerchantDomain) -> inherited default
        3. Store subdomain ({store.subdomain}.{main_domain}) -> fallback
        """
        if self.primary_domain:
            return self.primary_domain
        if self.merchant and self.merchant.primary_domain:
            return self.merchant.primary_domain
        return f"{self.subdomain}.{settings.main_domain}"

    @property
    def all_domains(self):
        """Get all active domains (subdomain + custom domains)."""
        domains = [
            f"{self.subdomain}.{settings.main_domain}"
        ]  # Start with the main subdomain
        for domain in self.domains:
            if domain.is_active:
                domains.append(domain.domain)  # Add other active custom domains
        return domains

    # ========================================================================
    # Contact Resolution Helper Properties
    # ========================================================================
    # These properties return the effective value (store override or merchant fallback)

    @property
    def effective_contact_email(self) -> str | None:
        """Get contact email (store override or merchant fallback)."""
        if self.contact_email is not None:
            return self.contact_email
        return self.merchant.contact_email if self.merchant else None

    @property
    def effective_contact_phone(self) -> str | None:
        """Get contact phone (store override or merchant fallback)."""
        if self.contact_phone is not None:
            return self.contact_phone
        return self.merchant.contact_phone if self.merchant else None

    @property
    def effective_website(self) -> str | None:
        """Get website (store override or merchant fallback)."""
        if self.website is not None:
            return self.website
        return self.merchant.website if self.merchant else None

    @property
    def effective_business_address(self) -> str | None:
        """Get business address (store override or merchant fallback)."""
        if self.business_address is not None:
            return self.business_address
        return self.merchant.business_address if self.merchant else None

    @property
    def effective_tax_number(self) -> str | None:
        """Get tax number (store override or merchant fallback)."""
        if self.tax_number is not None:
            return self.tax_number
        return self.merchant.tax_number if self.merchant else None

    def get_contact_info_with_inheritance(self) -> dict:
        """
        Get all contact info with inheritance flags.

        Returns dict with resolved values and flags indicating if inherited from merchant.
        """
        merchant = self.merchant
        return {
            "contact_email": self.effective_contact_email,
            "contact_email_inherited": self.contact_email is None
            and merchant is not None,
            "contact_phone": self.effective_contact_phone,
            "contact_phone_inherited": self.contact_phone is None
            and merchant is not None,
            "website": self.effective_website,
            "website_inherited": self.website is None and merchant is not None,
            "business_address": self.effective_business_address,
            "business_address_inherited": self.business_address is None
            and merchant is not None,
            "tax_number": self.effective_tax_number,
            "tax_number_inherited": self.tax_number is None and merchant is not None,
        }


class StoreUser(Base, TimestampMixin, SoftDeleteMixin):
    """
    Represents a user's team membership in a store.

    Ownership is determined via User.is_owner_of(store_id) which checks
    Merchant.owner_user_id, NOT a field on this table.

    This table is for team members only (invited by owner).
    """

    __tablename__ = "store_users"

    id = Column(Integer, primary_key=True, index=True)
    """Unique identifier for each StoreUser entry."""

    store_id = Column(Integer, ForeignKey("stores.id"), nullable=False)
    """Foreign key linking to the associated Store."""

    user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
    """Foreign key linking to the associated User."""

    # Role for team members (determines granular permissions)
    role_id = Column(Integer, ForeignKey("roles.id"), nullable=True)
    """Foreign key linking to the associated Role."""

    invited_by = Column(Integer, ForeignKey("users.id"))
    """Foreign key linking to the user who invited this StoreUser."""
    invitation_token = Column(String, nullable=True, index=True)  # For email activation
    invitation_sent_at = Column(DateTime, nullable=True)
    invitation_accepted_at = Column(DateTime, nullable=True)

    is_active = Column(
        Boolean, default=False, nullable=False
    )  # False until invitation accepted
    """Indicates whether the StoreUser role is active."""

    # Relationships
    store = relationship("Store", back_populates="store_users")
    """Relationship to the Store model, representing the associated store."""

    user = relationship(
        "User", foreign_keys=[user_id], back_populates="store_memberships"
    )
    """Relationship to the User model, representing the user who holds this role within the store."""

    inviter = relationship("User", foreign_keys=[invited_by])
    """Optional relationship to the User model, representing the user who invited this StoreUser."""

    role = relationship("Role", back_populates="store_users")
    """Relationship to the Role model, representing the role held by the store user."""

    def __repr__(self) -> str:
        """Return a string representation of the StoreUser instance."""
        role_name = self.role.name if self.role else "no-role"
        return f"<StoreUser(store_id={self.store_id}, user_id={self.user_id}, role={role_name})>"

    @property
    def is_owner(self) -> bool:
        """Check if this user is the owner of the store (via merchant ownership)."""
        return self.user.is_owner_of(self.store_id)

    @property
    def is_invitation_pending(self) -> bool:
        """Check if invitation is still pending."""
        return self.invitation_token is not None and self.invitation_accepted_at is None

    def has_permission(self, permission: str) -> bool:
        """
        Check if user has a specific permission.

        Owners (merchant owners) always have all permissions.
        Team members check their role's permissions.
        """
        # Owners have all permissions
        if self.is_owner:
            return True

        # Inactive users have no permissions
        if not self.is_active:
            return False

        # Check role permissions
        if self.role and self.role.permissions:
            return permission in self.role.permissions

        return False

    def get_all_permissions(self) -> list:
        """Get all permissions this user has."""
        if self.is_owner:
            from app.modules.tenancy.services.permission_discovery_service import (
                permission_discovery_service,
            )

            return list(permission_discovery_service.get_all_permission_ids())

        if self.role and self.role.permissions:
            return self.role.permissions

        return []


class Role(Base, TimestampMixin):
    """Represents a role within a store's system."""

    __tablename__ = "roles"  # Name of the table in the database

    id = Column(Integer, primary_key=True, index=True)
    """Unique identifier for each Role entry."""

    store_id = Column(Integer, ForeignKey("stores.id"), nullable=False)
    """Foreign key linking to the associated Store."""

    name = Column(String(100), nullable=False)
    """Name of the role, with a maximum length of 100 characters."""

    permissions = Column(JSON, default=list)
    """Permissions assigned to this role, stored as a JSON array."""

    store = relationship("Store")
    """Relationship to the Store model, representing the associated store."""

    store_users = relationship("StoreUser", back_populates="role")
    """Back-relationship to the StoreUser model, representing users with this role."""

    def __repr__(self) -> str:
        """Return a string representation of the Role instance.

        Returns:
            str: A string that includes the id and name of the Role instance.
        """
        return f"<Role(id={self.id}, name='{self.name}', store_id={self.store_id})>"


__all__ = ["Store", "StoreUser", "Role"]
