# app/modules/tenancy/services/platform_service.py
"""
Platform Service

Business logic for platform management in the Multi-Platform CMS.

Platforms represent different business offerings (OMS, Loyalty, Site Builder, Main Marketing).
Each platform has its own:
- Marketing pages (homepage, pricing, features)
- Store defaults (about, terms, privacy)
- Configuration and branding
"""

import logging
from dataclasses import dataclass

from sqlalchemy import func
from sqlalchemy.orm import Session

from app.modules.tenancy.exceptions import (
    PlatformNotFoundException,
)
from app.modules.tenancy.models import Platform, StorePlatform

logger = logging.getLogger(__name__)


@dataclass
class PlatformStats:
    """Platform statistics."""

    platform_id: int
    platform_code: str
    platform_name: str
    store_count: int
    platform_pages_count: int
    store_defaults_count: int
    store_overrides_count: int = 0
    published_pages_count: int = 0
    draft_pages_count: int = 0


class PlatformService:
    """Service for platform operations."""

    @staticmethod
    def get_platform_by_code(db: Session, code: str) -> Platform:
        """
        Get platform by code.

        Args:
            db: Database session
            code: Platform code (oms, loyalty, main, etc.)

        Returns:
            Platform object

        Raises:
            PlatformNotFoundException: If platform not found
        """
        platform = db.query(Platform).filter(Platform.code == code).first()

        if not platform:
            raise PlatformNotFoundException(code)

        return platform

    @staticmethod
    def get_platform_by_code_optional(db: Session, code: str) -> Platform | None:
        """
        Get platform by code, returns None if not found.

        Args:
            db: Database session
            code: Platform code

        Returns:
            Platform object or None
        """
        return db.query(Platform).filter(Platform.code == code).first()

    @staticmethod
    def get_platform_by_id(db: Session, platform_id: int) -> Platform:
        """
        Get platform by ID.

        Args:
            db: Database session
            platform_id: Platform ID

        Returns:
            Platform object

        Raises:
            PlatformNotFoundException: If platform not found
        """
        platform = db.query(Platform).filter(Platform.id == platform_id).first()

        if not platform:
            raise PlatformNotFoundException(str(platform_id))

        return platform

    @staticmethod
    def get_default_platform(db: Session) -> Platform | None:
        """Get the first/default platform."""
        return db.query(Platform).first()

    @staticmethod
    def list_platforms(
        db: Session, include_inactive: bool = False
    ) -> list[Platform]:
        """
        List all platforms.

        Args:
            db: Database session
            include_inactive: Include inactive platforms

        Returns:
            List of Platform objects
        """
        query = db.query(Platform)

        if not include_inactive:
            query = query.filter(Platform.is_active == True)

        return query.order_by(Platform.id).all()

    @staticmethod
    def get_store_count(db: Session, platform_id: int) -> int:
        """
        Get count of stores on a platform.

        Args:
            db: Database session
            platform_id: Platform ID

        Returns:
            Store count
        """
        return (
            db.query(func.count(StorePlatform.store_id))
            .filter(StorePlatform.platform_id == platform_id)
            .scalar()
            or 0
        )

    @staticmethod
    def get_active_store_count(db: Session, platform_id: int) -> int:
        """
        Get count of active stores on a platform.

        Args:
            db: Database session
            platform_id: Platform ID

        Returns:
            Active store count
        """
        from app.modules.tenancy.models import Store

        return (
            db.query(func.count(StorePlatform.store_id))
            .join(Store, Store.id == StorePlatform.store_id)
            .filter(
                StorePlatform.platform_id == platform_id,
                Store.is_active == True,  # noqa: E712
            )
            .scalar()
            or 0
        )

    @staticmethod
    def _get_content_page_model():
        """Deferred import for CMS ContentPage model."""
        from app.modules.cms.models import ContentPage

        return ContentPage

    @staticmethod
    def get_platform_pages_count(db: Session, platform_id: int) -> int:
        """
        Get count of platform marketing pages.

        Args:
            db: Database session
            platform_id: Platform ID

        Returns:
            Platform pages count
        """
        ContentPage = PlatformService._get_content_page_model()
        return (
            db.query(func.count(ContentPage.id))
            .filter(
                ContentPage.platform_id == platform_id,
                ContentPage.store_id is None,
                ContentPage.is_platform_page == True,
            )
            .scalar()
            or 0
        )

    @staticmethod
    def get_store_defaults_count(db: Session, platform_id: int) -> int:
        """
        Get count of store default pages.

        Args:
            db: Database session
            platform_id: Platform ID

        Returns:
            Store defaults count
        """
        ContentPage = PlatformService._get_content_page_model()
        return (
            db.query(func.count(ContentPage.id))
            .filter(
                ContentPage.platform_id == platform_id,
                ContentPage.store_id is None,
                ContentPage.is_platform_page == False,
            )
            .scalar()
            or 0
        )

    @staticmethod
    def get_store_overrides_count(db: Session, platform_id: int) -> int:
        """
        Get count of store override pages.

        Args:
            db: Database session
            platform_id: Platform ID

        Returns:
            Store overrides count
        """
        ContentPage = PlatformService._get_content_page_model()
        return (
            db.query(func.count(ContentPage.id))
            .filter(
                ContentPage.platform_id == platform_id,
                ContentPage.store_id is not None,
            )
            .scalar()
            or 0
        )

    @staticmethod
    def get_published_pages_count(db: Session, platform_id: int) -> int:
        """
        Get count of published pages on a platform.

        Args:
            db: Database session
            platform_id: Platform ID

        Returns:
            Published pages count
        """
        ContentPage = PlatformService._get_content_page_model()
        return (
            db.query(func.count(ContentPage.id))
            .filter(
                ContentPage.platform_id == platform_id,
                ContentPage.is_published == True,
            )
            .scalar()
            or 0
        )

    @staticmethod
    def get_draft_pages_count(db: Session, platform_id: int) -> int:
        """
        Get count of draft pages on a platform.

        Args:
            db: Database session
            platform_id: Platform ID

        Returns:
            Draft pages count
        """
        ContentPage = PlatformService._get_content_page_model()
        return (
            db.query(func.count(ContentPage.id))
            .filter(
                ContentPage.platform_id == platform_id,
                ContentPage.is_published == False,
            )
            .scalar()
            or 0
        )

    @classmethod
    def get_platform_stats(cls, db: Session, platform: Platform) -> PlatformStats:
        """
        Get comprehensive statistics for a platform.

        Args:
            db: Database session
            platform: Platform object

        Returns:
            PlatformStats dataclass
        """
        return PlatformStats(
            platform_id=platform.id,
            platform_code=platform.code,
            platform_name=platform.name,
            store_count=cls.get_store_count(db, platform.id),
            platform_pages_count=cls.get_platform_pages_count(db, platform.id),
            store_defaults_count=cls.get_store_defaults_count(db, platform.id),
            store_overrides_count=cls.get_store_overrides_count(db, platform.id),
            published_pages_count=cls.get_published_pages_count(db, platform.id),
            draft_pages_count=cls.get_draft_pages_count(db, platform.id),
        )

    # ========================================================================
    # StorePlatform cross-module public API methods
    # ========================================================================

    @staticmethod
    def get_first_active_platform_id_for_store(db: Session, store_id: int) -> int | None:
        """
        Get the first active platform ID for a store (ordered by joined_at).

        Used as a fallback when platform_id is not available from JWT context
        (e.g. background tasks, old tokens).

        Args:
            db: Database session
            store_id: Store ID

        Returns:
            Platform ID or None if no platform assigned
        """
        result = (
            db.query(StorePlatform.platform_id)
            .filter(
                StorePlatform.store_id == store_id,
                StorePlatform.is_active == True,  # noqa: E712
            )
            .order_by(StorePlatform.joined_at)
            .first()
        )
        return result[0] if result else None

    @staticmethod
    def get_active_platform_ids_for_store(db: Session, store_id: int) -> list[int]:
        """
        Get all active platform IDs for a store.

        Args:
            db: Database session
            store_id: Store ID

        Returns:
            List of platform IDs
        """
        results = (
            db.query(StorePlatform.platform_id)
            .filter(
                StorePlatform.store_id == store_id,
                StorePlatform.is_active == True,  # noqa: E712
            )
            .order_by(StorePlatform.joined_at)
            .all()
        )
        return [r[0] for r in results]

    @staticmethod
    def get_store_platform_entry(
        db: Session, store_id: int, platform_id: int
    ) -> StorePlatform | None:
        """
        Get a specific StorePlatform entry.

        Args:
            db: Database session
            store_id: Store ID
            platform_id: Platform ID

        Returns:
            StorePlatform object or None
        """
        return (
            db.query(StorePlatform)
            .filter(
                StorePlatform.store_id == store_id,
                StorePlatform.platform_id == platform_id,
            )
            .first()
        )

    @staticmethod
    def get_store_ids_for_platform(
        db: Session, platform_id: int, active_only: bool = True
    ) -> list[int]:
        """
        Get store IDs subscribed to a platform.

        Args:
            db: Database session
            platform_id: Platform ID
            active_only: Only return active store-platform links

        Returns:
            List of store IDs
        """
        query = db.query(StorePlatform.store_id).filter(
            StorePlatform.platform_id == platform_id,
        )
        if active_only:
            query = query.filter(StorePlatform.is_active == True)  # noqa: E712
        return [r[0] for r in query.all()]

    @staticmethod
    def ensure_store_platform(
        db: Session,
        store_id: int,
        platform_id: int,
        is_active: bool,
        tier_id: int | None = None,
    ) -> StorePlatform | None:
        """
        Upsert a StorePlatform entry.

        If the entry exists, update is_active (and tier_id if provided).
        If missing and is_active=True, create it.
        If missing and is_active=False, no-op.

        Args:
            db: Database session
            store_id: Store ID
            platform_id: Platform ID
            is_active: Whether the store-platform link is active
            tier_id: Optional subscription tier ID

        Returns:
            The StorePlatform entry, or None if no-op
        """
        existing = (
            db.query(StorePlatform)
            .filter(
                StorePlatform.store_id == store_id,
                StorePlatform.platform_id == platform_id,
            )
            .first()
        )

        if existing:
            existing.is_active = is_active
            if tier_id is not None:
                existing.tier_id = tier_id
            return existing

        if is_active:
            sp = StorePlatform(
                store_id=store_id,
                platform_id=platform_id,
                is_active=True,
                tier_id=tier_id,
            )
            db.add(sp)
            return sp

        return None

    @staticmethod
    def create_platform(db: Session, data: dict) -> Platform:
        """
        Create a new platform.

        Note: This method does NOT commit the transaction.
        The caller (API endpoint) is responsible for committing.

        Args:
            db: Database session
            data: Dictionary of fields for the new platform

        Returns:
            Created Platform object (with pending changes)
        """
        platform = Platform()
        for field, value in data.items():
            if hasattr(platform, field):
                setattr(platform, field, value)
        db.add(platform)
        logger.info(f"[PLATFORMS] Created platform: {platform.code}")
        return platform

    @staticmethod
    def update_platform(
        db: Session, platform: Platform, update_data: dict
    ) -> Platform:
        """
        Update platform fields.

        Note: This method does NOT commit the transaction.
        The caller (API endpoint) is responsible for committing.

        Args:
            db: Database session
            platform: Platform to update
            update_data: Dictionary of fields to update

        Returns:
            Updated Platform object (with pending changes)
        """
        for field, value in update_data.items():
            if hasattr(platform, field):
                setattr(platform, field, value)

        logger.info(f"[PLATFORMS] Updated platform: {platform.code}")

        return platform


# Singleton instance for convenience
platform_service = PlatformService()
