"""Event-related Immich bot commands: events, latest, memory, random."""

from __future__ import annotations

import asyncio
import logging
import random as rng
from datetime import datetime, timezone
from typing import Any

from sqlmodel import select
from sqlmodel.ext.asyncio.session import AsyncSession

from ...database.engine import get_engine
from ...database.models import (
    EventLog, NotificationTracker, NotificationTrackerTarget,
    ServiceProvider, TrackingConfig,
)

from ..command_utils import get_trackers_for_provider
from ..handler import _render_cmd_template
from .common import _format_assets, build_asset_dict, fetch_albums_with_links

_LOGGER = logging.getLogger(__name__)


async def _cmd_events(
    provider: ServiceProvider,
    count: int, locale: str,
    *,
    allowed_album_ids: set[str] | None = None,
) -> dict[str, Any]:
    trackers = await get_trackers_for_provider(provider.id)
    tracker_ids = [t.id for t in trackers]
    if not tracker_ids:
        return {"events": []}

    engine = get_engine()
    async with AsyncSession(engine) as session:
        query = (
            select(EventLog)
            .where(EventLog.tracker_id.in_(tracker_ids))
            .order_by(EventLog.created_at.desc())
        )
        if allowed_album_ids is not None:
            query = query.where(EventLog.collection_id.in_(list(allowed_album_ids)))
        result = await session.exec(query.limit(count))
        events = result.all()

    events_data = [
        {"type": e.event_type, "album": e.collection_name,
         "count": e.assets_count, "date": e.created_at.strftime("%m/%d %H:%M")}
        for e in events
    ]
    return {"events": events_data}


async def cmd_latest(
    client: Any, all_album_ids: list[str], count: int,
    locale: str, response_mode: str,
    cmd_templates: dict[str, dict[str, str]],
    external_domain: str = "",
) -> str | dict[str, Any]:
    """Handle /latest command with concurrent album fetching."""
    album_ids = all_album_ids[:10]
    if not album_ids:
        return _format_assets([], "latest", "", locale, response_mode, client, cmd_templates)

    ext = external_domain.rstrip("/")
    fetched = await fetch_albums_with_links(client, album_ids, ext, include_failed=False)

    latest_assets: list[dict[str, Any]] = []
    for album_data in fetched:
        pub_url = album_data.get("public_url", "")
        album_obj = album_data.get("_album")
        if album_obj:
            for aid, asset in list(album_obj.assets.items())[:count]:
                asset_pub = f"{pub_url}/photos/{asset.id}" if pub_url else ""
                latest_assets.append(build_asset_dict(asset, public_url=asset_pub))

    latest_assets.sort(key=lambda a: a.get("createdAt", ""), reverse=True)
    return _format_assets(latest_assets[:count], "latest", "", locale, response_mode, client, cmd_templates)


async def cmd_random(
    client: Any, all_album_ids: list[str], count: int,
    locale: str, response_mode: str,
    cmd_templates: dict[str, dict[str, str]],
    external_domain: str = "",
) -> str | dict[str, Any]:
    """Handle /random command with concurrent album fetching."""
    album_ids = all_album_ids[:10]
    if not album_ids:
        return _format_assets([], "random", "", locale, response_mode, client, cmd_templates)

    ext = external_domain.rstrip("/")
    fetched = await fetch_albums_with_links(client, album_ids, ext, include_failed=False)

    random_assets: list[dict[str, Any]] = []
    for album_data in fetched:
        pub_url = album_data.get("public_url", "")
        album_obj = album_data.get("_album")
        if album_obj:
            asset_list = list(album_obj.assets.values())
            sampled = rng.sample(asset_list, min(count, len(asset_list)))
            for asset in sampled:
                asset_pub = f"{pub_url}/photos/{asset.id}" if pub_url else ""
                random_assets.append(build_asset_dict(asset, public_url=asset_pub))

    rng.shuffle(random_assets)
    return _format_assets(random_assets[:count], "random", "", locale, response_mode, client, cmd_templates)


async def _check_native_memory(provider_id: int) -> bool:
    """Check if any notification tracker for this provider uses native memory source."""
    engine = get_engine()
    async with AsyncSession(engine) as session:
        tracker_result = await session.exec(
            select(NotificationTracker).where(
                NotificationTracker.provider_id == provider_id,
            )
        )
        trackers = tracker_result.all()
        tracker_ids = [t.id for t in trackers]
        if not tracker_ids:
            return False
        tt_result = await session.exec(
            select(NotificationTrackerTarget).where(
                NotificationTrackerTarget.tracker_id.in_(tracker_ids)
            )
        )
        tc_ids = list({tt.tracking_config_id for tt in tt_result.all() if tt.tracking_config_id})
        if not tc_ids:
            return False
        tc_result = await session.exec(
            select(TrackingConfig).where(TrackingConfig.id.in_(tc_ids))
        )
        return any(tc.memory_source == "native" for tc in tc_result.all())


async def cmd_memory(
    provider_id: int, client: Any, all_album_ids: list[str], count: int,
    locale: str, response_mode: str,
    cmd_templates: dict[str, dict[str, str]],
) -> str | dict[str, Any]:
    """Handle /memory command with concurrent album fetching."""
    use_native = await _check_native_memory(provider_id)
    today = datetime.now(timezone.utc)
    memory_assets: list[dict[str, Any]] = []

    if use_native:
        memories = await client.get_memories()
        tracked_ids = set(all_album_ids) if all_album_ids else None
        for mem in memories:
            year = mem.get("data", {}).get("year")
            for raw_asset in mem.get("assets", []):
                if tracked_ids:
                    asset_albums = raw_asset.get("albums", [])
                    if not any(a.get("id") in tracked_ids for a in asset_albums):
                        continue
                memory_assets.append(build_asset_dict(raw_asset, year=year))
    else:
        album_ids = all_album_ids[:10]
        if album_ids:
            album_results = await asyncio.gather(
                *[client.get_album(aid) for aid in album_ids],
                return_exceptions=True,
            )
            month_day = (today.month, today.day)
            for album_id, result in zip(album_ids, album_results):
                if isinstance(result, Exception):
                    _LOGGER.warning("Failed to fetch album %s: %s", album_id, result)
                    continue
                if result:
                    for aid, asset in result.assets.items():
                        try:
                            dt = datetime.fromisoformat(asset.created_at.replace("Z", "+00:00"))
                            if (dt.month, dt.day) == month_day and dt.year != today.year:
                                memory_assets.append(build_asset_dict(asset, year=dt.year))
                        except (ValueError, AttributeError):
                            pass

    memory_assets = memory_assets[:count]
    if not memory_assets:
        return _render_cmd_template(cmd_templates, "no_results", locale, {"command": "memory", "query": ""})
    return _format_assets(memory_assets, "memory", "", locale, response_mode, client, cmd_templates)
