"""Picker availability is fully automatic — never a manual toggle. A picker
is unavailable for new work for exactly one reason: they've already hit the
admin-configured daily order cap for the day in question (counting every
non-cancelled order assigned to them for that delivery date — cancelling
one immediately frees a slot).

A picker who's out "delivering" (picking or in_transit) still shows up as
an option for ANY new request, Quick Delivery included — they can finish
(or pause) their current trip, swing back to pick up the new order, and
continue, as long as they haven't hit their daily cap. Being physically en
route no longer excludes them from a quick order; only running out of
daily capacity does. Everything here is computed fresh on read, not cached
in a DB flag, so there's no day-rollover job needed to reset it."""

import datetime

from accounts.supabase_client import get_client
from .site_settings import get_max_orders_per_day

DELIVERING_STATUSES = ('picking', 'in_transit')


def _order_count_for_date(client, picker_id, date_str):
    orders = (
        client.table('pickker_orders')
        .select('id, status')
        .eq('picker_id', picker_id)
        .eq('delivery_date', date_str)
        .execute()
        .data
    )
    return sum(1 for o in orders if o['status'] != 'cancelled')


def get_picker_status(picker_id, max_orders_per_day=None, for_date=None, is_quick=False):
    """Returns the picker's live availability for the given date (defaults
    to today). 'is_quick' is accepted for backward compatibility with
    existing callers but no longer excludes an in-transit picker — being
    out delivering is shown as informational context ('delivering' /
    'in_transit' in 'reason'), never a block. Only the daily order cap
    blocks assignment now."""
    client = get_client()
    target_date = for_date or datetime.date.today().isoformat()
    max_orders = max_orders_per_day if max_orders_per_day is not None else get_max_orders_per_day()

    # "Delivering right now" is a real-time physical fact — whether the
    # picker currently has ANY order in picking/in_transit status, full
    # stop. Deliberately NOT scoped to delivery_date == target_date: an
    # order can still be mid-delivery on a day other than its own
    # delivery_date (e.g. a trip that started before its scheduled date,
    # or one running past it) — filtering by date here was a real bug
    # that silently hid an actively-in-transit picker from the fleet
    # company's live map (get_fleet_live_positions) and driver-status
    # badge (fleet_dashboard's Drivers tab) whenever the order's
    # delivery_date wasn't literally today. today_count/at_capacity below
    # is correctly date-scoped (capacity really is a per-day concept);
    # this is not.
    currently_active_orders = (
        client.table('pickker_orders')
        .select('id, status')
        .eq('picker_id', picker_id)
        .in_('status', DELIVERING_STATUSES)
        .execute()
        .data
    )
    delivering = bool(currently_active_orders)
    in_transit_now = any(o['status'] == 'in_transit' for o in currently_active_orders)

    today_count = _order_count_for_date(client, picker_id, target_date)
    at_capacity = today_count >= max_orders

    if at_capacity:
        reason = 'at_capacity'
    elif delivering:
        reason = 'delivering'  # informational only (covers picking + in_transit) — never blocks by itself
    else:
        reason = 'available'

    return {
        'available': not at_capacity,
        'delivering': delivering,
        'in_transit': in_transit_now,
        'reason': reason,
        'today_count': today_count,
        'max_orders_per_day': max_orders,
    }


def get_order_counts_for_date_batch(picker_ids, date_str):
    """{picker_id: non-cancelled order count for date_str} for every id in
    picker_ids, computed from one query — the batch counterpart to
    _order_count_for_date, used by market/picker_ranking.py so scoring N
    candidate pickers' daily load costs one query instead of N. Every id in
    picker_ids is guaranteed a key (0 if they have no orders that date)."""
    picker_ids = list({pid for pid in picker_ids if pid})
    counts = {pid: 0 for pid in picker_ids}
    if not picker_ids:
        return counts
    orders = (
        get_client().table('pickker_orders')
        .select('picker_id, status')
        .in_('picker_id', picker_ids)
        .eq('delivery_date', date_str)
        .execute()
        .data
    )
    for o in orders:
        if o['status'] != 'cancelled':
            counts[o['picker_id']] = counts.get(o['picker_id'], 0) + 1
    return counts


def has_capacity_for_date(picker_id, date_str, max_orders_per_day=None):
    """Authoritative check used right before an order is actually placed —
    the picker list may be slightly stale, so this re-checks the specific
    chosen delivery date at the moment of booking."""
    client = get_client()
    max_orders = max_orders_per_day if max_orders_per_day is not None else get_max_orders_per_day()
    return _order_count_for_date(client, picker_id, date_str) < max_orders


def get_available_b2b_pickers_for_date(date_str, truck_type_id, max_orders_per_day=None, is_quick=False):
    """B2B pickers (picker_segment='b2b') who drive the given truck type and
    are available for the given delivery date — mirrors the B2C picker
    filtering logic in market/views.py:checkout_picker, scoped to B2B."""
    client = get_client()
    profiles_resp = (
        client.table('pickker_picker_profiles')
        .select('*')
        .eq('is_approved', True)
        .eq('picker_segment', 'b2b')
        .eq('truck_type_id', truck_type_id)
        .execute()
    )
    profiles = [
        p for p in profiles_resp.data
        if p.get('location_lat') is not None and p.get('location_lng') is not None
    ]

    picker_ids = [p['user_id'] for p in profiles]
    users_by_id = {}
    if picker_ids:
        users_resp = client.table('pickker_users').select('id, first_name, last_name').in_('id', picker_ids).eq('is_active', True).execute()
        users_by_id = {u['id']: u for u in users_resp.data}

    available = []
    for p in profiles:
        user = users_by_id.get(p['user_id'])
        if not user:
            continue
        status = get_picker_status(p['user_id'], max_orders_per_day, for_date=date_str, is_quick=is_quick)
        if not status['available']:
            continue
        available.append({
            'user_id': p['user_id'],
            'name': f"{user['first_name']} {user['last_name']}".strip() or 'Picker',
            'lat': float(p['location_lat']),
            'lng': float(p['location_lng']),
            'company_id': p.get('company_id'),
            'truck_id': p.get('truck_id'),
            'region_id': p.get('region_id'),
        })
    return available


def get_all_b2b_pickers_for_truck_type(truck_type_id, fallback_lat=None, fallback_lng=None):
    """Every approved B2B picker who drives the given truck type, with no
    date/capacity filtering at all — used for the fleet-company provider
    selection pass (market/fleet_ranking.py), which now happens *before* a
    delivery date is chosen (the company decides internally which driver
    actually takes the job later, once a date exists — see
    assign_driver_to_order in market/fleet.py). Real per-date capacity is
    checked afterward, once a date exists (has_capacity_for_date /
    get_order_counts_for_date_batch), not here.

    A driver who hasn't opened the app yet has no GPS position on file —
    without fallback_lat/fallback_lng they're excluded entirely (unchanged
    behavior for the plain B2B path, which has no pickup point to assume a
    position from). When a vendor-tied pickup point IS known (see
    market/views.py::_gather_b2b_candidates), passing it here as the
    fallback keeps that driver's own company from silently vanishing from
    candidates just because nobody's shared a live GPS fix yet — the real
    position is captured for real once a driver is actually assigned
    (assign_driver_to_order), never here."""
    client = get_client()
    profiles_resp = (
        client.table('pickker_picker_profiles')
        .select('*')
        .eq('is_approved', True)
        .eq('picker_segment', 'b2b')
        .eq('truck_type_id', truck_type_id)
        .execute()
    )
    has_fallback = fallback_lat is not None and fallback_lng is not None
    profiles = [
        p for p in profiles_resp.data
        if (p.get('location_lat') is not None and p.get('location_lng') is not None) or has_fallback
    ]

    picker_ids = [p['user_id'] for p in profiles]
    users_by_id = {}
    if picker_ids:
        users_resp = client.table('pickker_users').select('id, first_name, last_name').in_('id', picker_ids).eq('is_active', True).execute()
        users_by_id = {u['id']: u for u in users_resp.data}

    result = []
    for p in profiles:
        user = users_by_id.get(p['user_id'])
        if not user:
            continue
        has_real_location = p.get('location_lat') is not None and p.get('location_lng') is not None
        result.append({
            'user_id': p['user_id'],
            'name': f"{user['first_name']} {user['last_name']}".strip() or 'Picker',
            'lat': float(p['location_lat']) if has_real_location else float(fallback_lat),
            'lng': float(p['location_lng']) if has_real_location else float(fallback_lng),
            'company_id': p.get('company_id'),
            'truck_id': p.get('truck_id'),
            'region_id': p.get('region_id'),
        })
    return result


def get_active_order_load_batch(picker_ids):
    """{picker_id: count of their orders in any non-terminal status} —
    a date-agnostic fairness proxy for ranking fleet-company providers
    before a delivery date is even chosen (see get_all_b2b_pickers_for_
    truck_type above). Deliberately NOT a stand-in for a specific date's
    capacity — that would silently reintroduce the "use today's date as an
    approximation" bug this session already fixed once. This measures
    general business load ("how busy is this person right now"), same
    batching shape as get_order_counts_for_date_batch."""
    picker_ids = list({pid for pid in picker_ids if pid})
    counts = {pid: 0 for pid in picker_ids}
    if not picker_ids:
        return counts
    orders = (
        get_client().table('pickker_orders')
        .select('picker_id, status')
        .in_('picker_id', picker_ids)
        .in_('status', ['pending_payment', 'confirmed', 'picking', 'in_transit'])
        .execute()
        .data
    )
    for o in orders:
        counts[o['picker_id']] = counts.get(o['picker_id'], 0) + 1
    return counts
