"""Celery tasks for refreshing %-based hull prices from Jita.

Fan-out mirrors memberaudit: a Beat orchestrator enqueues one QueueOnce task
per distinct hull type; each rewrites every shop's %-listing of that type.
"""

from celery import shared_task
from django.conf import settings

from allianceauth.services.tasks import QueueOnce

from .market import jita_prices
from .models import PCT_MODES, HullListing
from .stock import recompute_shop_stock

# Low priority so user-triggered work jumps ahead (AA priority: 1 urgent .. 9 idle).
TASK_PRIORITY = getattr(settings, "SHOP_TASKS_PRIORITY", 7)


def _apply_prices_for_type(type_id: int) -> int:
    """Recompute + store price for every %-listing of one type. Returns #changed."""
    prices = jita_prices(type_id)
    listings = list(
        HullListing.objects.filter(eve_type_id=type_id, price_mode__in=PCT_MODES)
    )
    changed = [
        listing
        for listing in listings
        if listing.apply_jita_price(prices["buy"], prices["sell"])
    ]
    if changed:
        HullListing.objects.bulk_update(changed, ["price"])
    return len(changed)


def pct_type_ids() -> list:
    """Distinct eve_type ids that appear on any %-priced hull listing."""
    return list(
        HullListing.objects.filter(price_mode__in=PCT_MODES)
        .values_list("eve_type_id", flat=True)
        .distinct()
    )


def _enqueue_pct_refreshes() -> list:
    """Enqueue one per-type task for each distinct type on a %-listing."""
    type_ids = pct_type_ids()
    for type_id in type_ids:
        refresh_price_for_type.apply_async(args=[type_id], priority=TASK_PRIORITY)
    return type_ids


@shared_task(
    bind=True,
    base=QueueOnce,
    # timeout: lock TTL backstop so a crashed/killed task's per-type lock
    # self-heals in minutes instead of the 1h default (otherwise a worker crash
    # mid-task silently blocks that type's refresh until the default TTL).
    once={"keys": ["type_id"], "graceful": True, "timeout": 600},
    max_retries=3,
)
def refresh_price_for_type(self, type_id):
    try:
        _apply_prices_for_type(type_id)
    except Exception as exc:  # ESI hiccup -> retry with backoff, then keep-last-known
        raise self.retry(exc=exc, countdown=60)


@shared_task(base=QueueOnce)
def refresh_pct_prices():
    _enqueue_pct_refreshes()


def _refresh_shop_stock(shop_id):
    """Recompute stored stock for a shop's auto-stock listings."""
    from .models import Shop
    shop = Shop.objects.filter(pk=shop_id).first()
    if shop is not None:
        recompute_shop_stock(shop)


def _enqueue_stock_refreshes():
    """Enqueue one per-shop stock task for each shop with an auto-stock listing."""
    from .models import AUTO_STOCK, Shop
    shop_ids = list(
        Shop.objects.filter(hull_listings__stock_mode=AUTO_STOCK)
        .values_list("pk", flat=True)
        .distinct()
    )
    for shop_id in shop_ids:
        refresh_stock_for_shop.apply_async(args=[shop_id], priority=TASK_PRIORITY)
    return shop_ids


@shared_task(
    bind=True,
    base=QueueOnce,
    once={"keys": ["shop_id"], "graceful": True, "timeout": 600},
    max_retries=3,
)
def refresh_stock_for_shop(self, shop_id):
    _refresh_shop_stock(shop_id)


@shared_task(base=QueueOnce)
def refresh_all_stock():
    _enqueue_stock_refreshes()
