"""Select master SKUs that are ready for a channel sample push."""

from __future__ import annotations

from typing import Any, Dict, Iterable, List, Optional, Set

from sqlalchemy import select
from sqlalchemy.orm import Session

from db.channel_assignments import active_skus_for_channel
from db.channel_exports import resolve_pipeline_channel_code
from db.compat_connections import decode_compat_connection_id
from db.models import MasterProduct, MasterProductImage, ProductChannelTaxonomyAssignment

ACTIVE_STATUS = "active"


def skus_with_gallery_images(session: Session, skus: Optional[Iterable[str]] = None) -> Set[str]:
    """Master SKUs that have at least one row in master_product_images."""
    stmt = select(MasterProductImage.sku).distinct()
    if skus is not None:
        wanted = sorted({str(s).strip() for s in skus if str(s).strip()})
        if not wanted:
            return set()
        stmt = stmt.where(MasterProductImage.sku.in_(wanted))
    return {
        str(sku).strip()
        for (sku,) in session.execute(stmt).all()
        if str(sku).strip()
    }


def skus_with_taxonomy_assignments(
    session: Session,
    channel_code: str,
    *,
    connection_id: Optional[int] = None,
) -> Set[str]:
    """Master SKUs with at least one active taxonomy assignment on the channel."""
    channel = resolve_pipeline_channel_code(channel_code)
    native_connection_id = None
    if connection_id is not None:
        channel_type, native_connection_id = decode_compat_connection_id(connection_id)
        if channel_type != channel and channel_type not in {"magento", "shopify", "generic"}:
            native_connection_id = connection_id

    stmt = (
        select(ProductChannelTaxonomyAssignment.master_sku)
        .where(ProductChannelTaxonomyAssignment.channel_code == channel)
        .where(ProductChannelTaxonomyAssignment.assignment_status == ACTIVE_STATUS)
        .distinct()
    )
    if native_connection_id is not None:
        stmt = stmt.where(
            (ProductChannelTaxonomyAssignment.connection_id == native_connection_id)
            | (ProductChannelTaxonomyAssignment.connection_id.is_(None))
        )
    return {
        str(sku).strip()
        for (sku,) in session.execute(stmt).all()
        if str(sku).strip()
    }


def active_master_skus(session: Session) -> Set[str]:
    return {
        str(sku).strip()
        for (sku,) in session.execute(
            select(MasterProduct.sku).where(MasterProduct.is_active.is_(True))
        ).all()
        if str(sku).strip()
    }


def ready_skus_for_channel(
    session: Session,
    channel_code: str,
    *,
    connection_id: Optional[int] = None,
    only_assigned: bool = True,
) -> Set[str]:
    """Assigned + taxonomy + gallery images + active master."""
    channel = resolve_pipeline_channel_code(channel_code)
    pool = active_master_skus(session)
    if only_assigned:
        pool &= active_skus_for_channel(session, channel)
    pool &= skus_with_taxonomy_assignments(session, channel, connection_id=connection_id)
    pool &= skus_with_gallery_images(session, pool)
    return pool


def select_push_ready_skus(
    session: Session,
    channels: Iterable[str],
    *,
    limit: int = 10,
    require_all_channels: bool = True,
    connection_ids: Optional[Dict[str, int]] = None,
    only_assigned: bool = True,
) -> Dict[str, Any]:
    """Pick up to ``limit`` SKUs ready to push (assignment + taxonomy + images)."""
    selected_channels = [resolve_pipeline_channel_code(ch) for ch in channels]
    if not selected_channels:
        raise ValueError("At least one channel is required")
    if limit < 1:
        raise ValueError("limit must be >= 1")

    connection_ids = connection_ids or {}
    per_channel: Dict[str, Dict[str, Any]] = {}
    ready_by_channel: Dict[str, Set[str]] = {}

    for channel in selected_channels:
        ready = ready_skus_for_channel(
            session,
            channel,
            connection_id=connection_ids.get(channel),
            only_assigned=only_assigned,
        )
        ready_by_channel[channel] = ready
        per_channel[channel] = {
            "eligible_count": len(ready),
            "connection_id": connection_ids.get(channel),
        }

    per_channel_skus: Dict[str, List[str]] = {}
    if len(selected_channels) == 1:
        channel = selected_channels[0]
        selected = sorted(ready_by_channel[channel])[:limit]
        per_channel_skus[channel] = selected
    elif require_all_channels:
        shared = set.intersection(*ready_by_channel.values()) if ready_by_channel else set()
        selected = sorted(shared)[:limit]
        per_channel_skus = {channel: selected for channel in selected_channels}
    else:
        selected_set: Set[str] = set()
        for channel in selected_channels:
            channel_skus = sorted(ready_by_channel[channel])[:limit]
            per_channel_skus[channel] = channel_skus
            selected_set.update(channel_skus)
        selected = sorted(selected_set)

    for channel in selected_channels:
        per_channel[channel]["selected_count"] = len(per_channel_skus.get(channel, []))

    unified = sorted({sku for skus in per_channel_skus.values() for sku in skus})

    return {
        "channels": selected_channels,
        "require_all_channels": require_all_channels,
        "limit": limit,
        "selected_skus": unified,
        "per_channel": per_channel,
        "per_channel_skus": per_channel_skus,
    }
