"""Immich album change detection — produces generic ServiceEvent objects."""

from __future__ import annotations

import logging
from datetime import datetime, timezone

from notify_bridge_core.models.events import EventType, ServiceEvent
from notify_bridge_core.providers.base import ServiceProviderType

from .asset_utils import asset_to_media
from .models import ImmichAlbumData, ImmichAssetInfo

_LOGGER = logging.getLogger(__name__)

# Guard against runaway payloads when a bulk import lands in one poll tick.
# Templates iterate every entry in ``added_assets`` / ``removed_asset_ids``
# in Jinja for-loops (see defaults/*/assets_added.jinja2), and Telegram's
# media group has a hard cap of its own — sending 200k entries would both
# crash rendering and produce a message that no transport can deliver.
#
# ``added_count`` / ``removed_count`` on the event always carry the true
# totals so templates can show an accurate "N added" number even when the
# per-asset list is truncated.
_MAX_ASSETS_PER_EVENT = 50
_MAX_REMOVALS_PER_EVENT = 200


def _make_base_extra(new_album: ImmichAlbumData, external_url: str) -> dict:
    """Build the common extra dict for album events."""
    return {
        "album_url": f"{external_url}/albums/{new_album.id}",
        "people": list(new_album.people),
        "shared": new_album.shared,
        "photo_count": new_album.photo_count,
        "video_count": new_album.video_count,
        "asset_count": new_album.asset_count,
        "owner": new_album.owner,
    }


def detect_album_changes(
    old_album: ImmichAlbumData,
    new_album: ImmichAlbumData,
    pending_asset_ids: set[str],
    provider_name: str,
    external_url: str,
) -> tuple[list[ServiceEvent], set[str]]:
    """Detect changes between two album states, producing generic ServiceEvents.

    Args:
        old_album: Previous album data
        new_album: Current album data
        pending_asset_ids: Assets detected but not yet fully processed
        provider_name: Name of the provider instance
        external_url: External URL for building asset URLs

    Returns:
        Tuple of (list of ServiceEvents, updated pending_asset_ids)
    """
    added_ids = new_album.asset_ids - old_album.asset_ids
    removed_ids = old_album.asset_ids - new_album.asset_ids

    pending = set(pending_asset_ids)

    # Collect newly added processed assets
    added_assets: list[ImmichAssetInfo] = []
    for aid in added_ids:
        if aid not in new_album.assets:
            continue
        asset = new_album.assets[aid]
        if asset.is_processed:
            added_assets.append(asset)
        else:
            pending.add(aid)

    # Check if pending assets are now processed
    for aid in list(pending):
        if aid not in new_album.assets:
            pending.discard(aid)
            continue
        asset = new_album.assets[aid]
        if asset.is_processed:
            added_assets.append(asset)
            pending.discard(aid)

    # Detect metadata changes
    name_changed = old_album.name != new_album.name
    sharing_changed = old_album.shared != new_album.shared

    if not added_assets and not removed_ids and not name_changed and not sharing_changed:
        return [], pending

    now = datetime.now(timezone.utc)
    extra = _make_base_extra(new_album, external_url)
    events: list[ServiceEvent] = []

    # Emit one event per change type detected
    if added_assets:
        total_added = len(added_assets)
        truncated_added = added_assets[:_MAX_ASSETS_PER_EVENT]
        media_assets = [asset_to_media(a, external_url) for a in truncated_added]
        event_extra = dict(extra)
        if total_added > _MAX_ASSETS_PER_EVENT:
            event_extra["truncated"] = True
            event_extra["shown_count"] = _MAX_ASSETS_PER_EVENT
            _LOGGER.info(
                "Truncated assets_added event for album %s: %d → %d",
                new_album.id, total_added, _MAX_ASSETS_PER_EVENT,
            )
        events.append(ServiceEvent(
            event_type=EventType.ASSETS_ADDED,
            provider_type=ServiceProviderType.IMMICH,
            provider_name=provider_name,
            collection_id=new_album.id,
            collection_name=new_album.name,
            timestamp=now,
            added_assets=media_assets,
            removed_asset_ids=[],
            added_count=total_added,
            removed_count=0,
            extra=event_extra,
        ))

    if removed_ids:
        total_removed = len(removed_ids)
        truncated_removed = list(removed_ids)[:_MAX_REMOVALS_PER_EVENT]
        event_extra = dict(extra)
        if total_removed > _MAX_REMOVALS_PER_EVENT:
            event_extra["truncated"] = True
            event_extra["shown_count"] = _MAX_REMOVALS_PER_EVENT
            _LOGGER.info(
                "Truncated assets_removed event for album %s: %d → %d",
                new_album.id, total_removed, _MAX_REMOVALS_PER_EVENT,
            )
        events.append(ServiceEvent(
            event_type=EventType.ASSETS_REMOVED,
            provider_type=ServiceProviderType.IMMICH,
            provider_name=provider_name,
            collection_id=new_album.id,
            collection_name=new_album.name,
            timestamp=now,
            added_assets=[],
            removed_asset_ids=truncated_removed,
            added_count=0,
            removed_count=total_removed,
            extra=event_extra,
        ))

    if name_changed:
        events.append(ServiceEvent(
            event_type=EventType.COLLECTION_RENAMED,
            provider_type=ServiceProviderType.IMMICH,
            provider_name=provider_name,
            collection_id=new_album.id,
            collection_name=new_album.name,
            timestamp=now,
            added_assets=[],
            removed_asset_ids=[],
            added_count=0,
            removed_count=0,
            old_name=old_album.name,
            new_name=new_album.name,
            extra=dict(extra),
        ))

    if sharing_changed:
        events.append(ServiceEvent(
            event_type=EventType.SHARING_CHANGED,
            provider_type=ServiceProviderType.IMMICH,
            provider_name=provider_name,
            collection_id=new_album.id,
            collection_name=new_album.name,
            timestamp=now,
            added_assets=[],
            removed_asset_ids=[],
            added_count=0,
            removed_count=0,
            old_shared=old_album.shared,
            new_shared=new_album.shared,
            extra=dict(extra),
        ))

    return events, pending
