"""Validation helpers when a shipment moves from draft to published."""

from __future__ import annotations

import logging
import threading

from django.db import transaction

from core.countries import (
    is_valid_country_code,
    normalize_country_code,
    operational_region_error_for_country,
    return_trip_error_for_pickup_country,
)
from core.geocoding import maybe_fill_country_code_from_coords

logger = logging.getLogger(__name__)


def run_shipment_publish_side_effects(shipment_id: int) -> dict:
    """Dispatch rate requests and nearby-load notifications for a published shipment."""
    from core.models import Shipment
    from core.rate_distribution import dispatch_rate_requests_for_shipment
    from api.notify import notify_nearby_transporters_new_shipment

    try:
        shipment = Shipment.objects.get(pk=shipment_id)
    except Shipment.DoesNotExist:
        return {'error': 'not_found'}

    if shipment.status != Shipment.Status.PUBLISHED:
        return {'skipped': 'not_published'}

    rate_count = 0
    nearby_count = 0
    try:
        rate_count = len(dispatch_rate_requests_for_shipment(shipment))
    except Exception:
        logger.exception('Rate request dispatch failed for shipment %s', shipment_id)
    try:
        nearby_count = notify_nearby_transporters_new_shipment(shipment)
    except Exception:
        logger.exception('NEARBY_LOAD notify failed for shipment %s', shipment_id)
    return {'rate_requests': rate_count, 'nearby_notified': nearby_count}


def enqueue_shipment_publish_side_effects(shipment_id: int) -> None:
    """Run publish side effects after commit; prefer Celery, fall back to a background thread."""

    def _run_inline():
        try:
            run_shipment_publish_side_effects(shipment_id)
        except Exception:
            logger.exception('Inline publish side effects failed for shipment %s', shipment_id)

    def _dispatch():
        try:
            from core.tasks import run_shipment_publish_side_effects_task

            run_shipment_publish_side_effects_task.delay(shipment_id)
        except Exception as exc:
            logger.warning(
                'Celery enqueue failed for shipment %s publish side effects (%s); using background thread',
                shipment_id,
                exc,
            )
            threading.Thread(target=_run_inline, daemon=True).start()

    transaction.on_commit(_dispatch)


def prepare_shipment_for_publish(shipment) -> tuple[bool, str | dict[str, str] | None]:
    """
    Ensure pickup/delivery country codes exist before publishing.
    Attempts reverse geocoding when coordinates are present.
    """
    updated_fields: list[str] = []

    if maybe_fill_country_code_from_coords(
        shipment,
        field='pickup_country_code',
        lat_field='pickup_lat',
        lon_field='pickup_lon',
    ):
        updated_fields.append('pickup_country_code')

    if maybe_fill_country_code_from_coords(
        shipment,
        field='delivery_country_code',
        lat_field='delivery_lat',
        lon_field='delivery_lon',
    ):
        updated_fields.append('delivery_country_code')

    if updated_fields:
        shipment.save(update_fields=[*updated_fields, 'updated_at'])

    errors: dict[str, str] = {}
    pickup = normalize_country_code(shipment.pickup_country_code)
    delivery = normalize_country_code(shipment.delivery_country_code)

    if not pickup:
        errors['pickup_country_code'] = (
            'Required when publishing. Provide a valid ISO code or pickup coordinates.'
        )
    elif not is_valid_country_code(pickup):
        errors['pickup_country_code'] = 'Invalid ISO 3166-1 alpha-2 country code.'
    else:
        region_error = operational_region_error_for_country(pickup)
        if region_error:
            return False, region_error

    if not delivery:
        errors['delivery_country_code'] = (
            'Required when publishing. Provide a valid ISO code or delivery coordinates.'
        )
    elif not is_valid_country_code(delivery):
        errors['delivery_country_code'] = 'Invalid ISO 3166-1 alpha-2 country code.'
    else:
        region_error = operational_region_error_for_country(delivery)
        if region_error:
            return False, region_error

    if shipment.is_return:
        return_error = return_trip_error_for_pickup_country(pickup)
        if return_error:
            return False, return_error

    if errors:
        return False, errors
    return True, None
