import json
from datetime import datetime, timedelta, timezone
from decimal import Decimal, InvalidOperation

from django.contrib import messages
from django.contrib.auth.hashers import check_password, make_password
from django.http import Http404, JsonResponse
from django.shortcuts import redirect, render
from django.urls import reverse
from django.utils.html import escape
from django.utils.safestring import mark_safe
from django.views.decorators.csrf import ensure_csrf_cookie
from django.views.decorators.http import require_POST

from market.cart import get_cart
from market.checkout_state import load_checkout_state
from market.earnings import (
    get_all_pickers_earnings_overview,
    get_picker_earnings_summary_and_history,
    pay_out_picker,
    record_earning_if_delivered,
    set_payout_frequency,
)
from market.emailer import notify_admin, send_email
from market.geo import DAR_ES_SALAAM_CENTER, haversine_km
from market.legal_policies import record_all_current_acceptances
from market.messaging import get_messages, mark_messages_seen, send_message
from market.order_emails import notify_new_message, notify_order_status_changed
from market.push_notifications import push_new_message, push_order_status_changed, save_subscription
from market.order_notifications import get_active_orders_for_login_flash
from market.site_url import absolute_url
from market.order_status import get_action_label, get_status_label
from market.picker_availability import get_picker_status
from market.picker_stats import get_picker_performance
from market.price_trends import get_all_products_with_trends, record_price
from market.ratings import get_picker_rating_summary, get_platform_rating_summary
from market.translations import get_t

from .account_delete import delete_or_suspend_self
from .decorators import role_required
from .forms import LoginForm, ProductForm, RegionalPriceForm, SignupForm
from .models import AccountType, Role, SourceType
from .password_reset import complete_password_reset, get_user_for_reset_token, request_password_reset
from .phone_utils import normalize_phone_variants
from .supabase_client import get_client
from .verification import generate_verification_token, resend_verification_email, send_verification_email, send_verification_sms, verify_token


def signup(request):
    if request.method == 'POST':
        form = SignupForm(request.POST, request.FILES)
        if form.is_valid():
            data = form.cleaned_data
            client = get_client()

            user_row = {
                'email': data['email'],
                'password_hash': make_password(data['password1']),
                'role': data['role'],
                'first_name': data['first_name'],
                'last_name': data['last_name'],
                'phone_number': data['phone_number'],
                'preferred_language': data.get('preferred_language') or 'en',
                'sms_language': 'sw',
                'verification_token': generate_verification_token(),
            }
            user_resp = client.table('pickker_users').insert(user_row).execute()
            user = user_resp.data[0]
            request.session['language'] = user['preferred_language']

            if data['role'] == Role.CUSTOMER:
                is_b2b_signup = (data.get('account_type') or AccountType.B2C) == AccountType.B2B
                client.table('pickker_customer_profiles').insert({
                    'user_id': user['id'],
                    'account_type': data.get('account_type') or AccountType.B2C,
                    'business_name': data.get('business_name', ''),
                    'address': data.get('address', ''),
                    'city': data.get('city', ''),
                    'area': data.get('area', ''),
                    'tin_number': data.get('tin_number', '') if is_b2b_signup else '',
                    'business_license_number': data.get('business_license_number', '') if is_b2b_signup else '',
                    # Only meaningful for a B2B signup — routes them straight
                    # into fleet_register on their first login instead of
                    # leaving them to discover the "My Fleet" nav link later.
                    'wants_fleet_registration': bool(data.get('wants_fleet_registration')) and is_b2b_signup,
                }).execute()
            else:
                client.table('pickker_picker_profiles').insert({
                    'user_id': user['id'],
                    'truck_type_id': data['truck_type_id'],
                    'license_plate': data.get('license_plate', ''),
                    'source_type': data.get('source_type') or 'market',
                    'market_location': data.get('market_location', ''),
                    'market_id': data.get('market_id'),
                    'location_lat': data.get('location_lat'),
                    'location_lng': data.get('location_lng'),
                    'license_number': data.get('license_number', ''),
                }).execute()

            record_all_current_acceptances(user['id'])

            from market.sms import send_welcome_sms, sms_lang
            send_welcome_sms(
                user.get('phone_number'), user['first_name'],
                sms_lang(user.get('sms_language')), data['role'],
            )

            send_verification_email(request, user)
            send_verification_sms(request, user)
            notify_admin(
                f"New {data['role']} signup: {user['first_name']} {user['last_name']}",
                f"A new {data['role']} account just signed up.\n\n"
                f"Name: {user['first_name']} {user['last_name']}\n"
                f"Email: {user['email']}\n"
                f"Phone: {user.get('phone_number', '')}\n\n"
                "They must verify their email before they can log in."
                + ("\nPickers also need admin approval before they're visible to customers." if data['role'] == Role.PICKER else ''),
            )
            t = get_t(request)
            body = mark_safe(t['auth_check_email_body'].format(email=escape(user['email'])))
            return render(request, 'accounts/check_email.html', {'email': user['email'], 'check_email_body': body})
    else:
        form = SignupForm()

    from market.markets import get_active_markets
    from market.truck_types import get_active_truck_types
    return render(request, 'accounts/signup.html', {
        'form': form,
        'markets': get_active_markets(),
        'truck_types': get_active_truck_types(segment='b2c'),
    })


def verify_email(request, token):
    user = verify_token(token)
    if not user:
        return render(request, 'accounts/verify_result.html', {'success': False})
    t = get_t(request)
    verified_body = t['auth_email_verified_body'].format(first_name=user.get('first_name') or '')
    return render(request, 'accounts/verify_result.html', {'success': True, 'user': user, 'verified_body': verified_body})


def resend_verification(request):
    sent = None
    if request.method == 'POST':
        email = request.POST.get('email', '').strip().lower()
        sent = resend_verification_email(request, email)
    return render(request, 'accounts/resend_verification.html', {'sent': sent})


def forgot_password(request):
    sent = None
    if request.method == 'POST':
        email = request.POST.get('email', '').strip().lower()
        request_password_reset(request, email)
        sent = True
    return render(request, 'accounts/forgot_password.html', {'sent': sent})


def reset_password(request, token):
    user = get_user_for_reset_token(token)
    if not user:
        return render(request, 'accounts/reset_password.html', {'invalid': True})

    error = None
    if request.method == 'POST':
        t = get_t(request)
        password1 = request.POST.get('password1', '')
        password2 = request.POST.get('password2', '')
        if len(password1) < 8:
            error = t['msg_password_too_short']
        elif password1 != password2:
            error = t['msg_passwords_dont_match']
        else:
            complete_password_reset(user['id'], make_password(password1))
            send_email(
                user['email'],
                'Your PickkerMarket password was changed',
                f"Hi {user.get('first_name') or ''},\n\nYour password was just changed. If this wasn't you, "
                "contact us immediately.\n\n— The PickkerMarket Team",
            )
            messages.success(request, t['msg_password_updated'])
            return redirect('accounts:login')

    return render(request, 'accounts/reset_password.html', {'invalid': False, 'error': error})


def login_view(request):
    error = None
    unverified = False
    if request.method == 'POST':
        t = get_t(request)
        form = LoginForm(request.POST)
        if form.is_valid():
            identifier = form.cleaned_data['identifier'].strip()
            password = form.cleaned_data['password']

            client = get_client()
            if '@' in identifier:
                resp = client.table('pickker_users').select('*').eq('email', identifier.lower()).execute()
            else:
                variants = normalize_phone_variants(identifier)
                resp = (
                    client.table('pickker_users').select('*').in_('phone_number', variants).execute()
                    if variants else type('EmptyResp', (), {'data': []})()
                )
            user = resp.data[0] if resp.data else None

            if user and check_password(password, user['password_hash']) and not user.get('is_verified'):
                error = t['msg_verify_email_first']
                unverified = True
            elif user and check_password(password, user['password_hash']) and not user.get('is_active'):
                error = t['msg_account_suspended']
            elif user and user.get('is_active') and user.get('is_verified') and check_password(password, user['password_hash']):
                account_type = None
                business_name = None
                if user['role'] == Role.CUSTOMER:
                    profile_resp = get_client().table('pickker_customer_profiles').select('account_type, business_name').eq('user_id', user['id']).execute()
                    if profile_resp.data:
                        account_type = profile_resp.data[0].get('account_type')
                        business_name = profile_resp.data[0].get('business_name')

                request.session['picker_user'] = {
                    'id': user['id'],
                    'email': user['email'],
                    'role': user['role'],
                    'first_name': user['first_name'],
                    'account_type': account_type,
                    'business_name': business_name,
                }
                request.session['language'] = user.get('preferred_language') or 'en'
                # Recompute for the user's own preferred language, just set
                # above — not whatever the anonymous session had before login.
                t = get_t(request)
                if user['role'] == Role.CUSTOMER:
                    saved_checkout = load_checkout_state(user['id'])
                    if saved_checkout:
                        request.session['checkout'] = saved_checkout

                    cart_count = sum(row['quantity'] for row in get_cart(user['id']))
                    if saved_checkout and saved_checkout.get('picker_id'):
                        messages.info(request, t['msg_welcome_back_checkout'].format(cart_count=cart_count))
                    elif cart_count:
                        messages.info(request, t['msg_welcome_back_cart'].format(cart_count=cart_count))

                    active_orders = get_active_orders_for_login_flash(user['id'])
                    if active_orders:
                        if len(active_orders) == 1:
                            o = active_orders[0]
                            messages.info(
                                request,
                                t['msg_order_status_flash'].format(order_id=o['id'], status_label=get_status_label(t, o['status'])),
                            )
                        else:
                            parts = ', '.join(
                                f"#{o['id']} ({get_status_label(t, o['status'])})"
                                for o in active_orders[:3]
                            )
                            messages.info(request, t['msg_orders_in_progress_flash'].format(count=len(active_orders), parts=parts))
                return redirect('accounts:post_login_redirect')
            else:
                error = t['msg_invalid_credentials']
    else:
        form = LoginForm()

    return render(request, 'accounts/login.html', {
        'form': form,
        'error': error,
        'unverified': unverified,
    })


def logout_view(request):
    request.session.flush()
    return redirect('market:home')


def post_login_redirect(request):
    picker_user = request.session.get('picker_user')
    if not picker_user:
        return redirect('accounts:login')
    if picker_user['role'] == Role.ADMIN:
        return redirect('accounts:admin_portal')
    if picker_user['role'] == Role.PICKER:
        return redirect('accounts:picker_home')

    # A B2B customer who checked "I also want to register a delivery fleet"
    # at signup is routed straight into fleet_register on their first login
    # (can't happen at signup itself — email must be verified first) rather
    # than left to find the "My Fleet" nav link on their own. One-shot: the
    # flag is cleared immediately so this never fires again after today.
    profile_resp = get_client().table('pickker_customer_profiles').select('wants_fleet_registration').eq('user_id', picker_user['id']).execute()
    if profile_resp.data and profile_resp.data[0].get('wants_fleet_registration'):
        get_client().table('pickker_customer_profiles').update({'wants_fleet_registration': False}).eq('user_id', picker_user['id']).execute()
        return redirect('market:fleet_register')

    # Customer with a checkout in progress resumes at the exact step they
    # left off at, instead of landing back on the home page. B2B/vendor-fleet
    # carts now go location -> provider -> schedule (provider chosen before a
    # date exists — see market/views.py::checkout_provider); plain B2C stays
    # location -> schedule -> picker, unchanged.
    checkout = request.session.get('checkout')
    if checkout and checkout.get('lat') is not None:
        from market.views import _needs_provider_step

        needs_provider = _needs_provider_step(request, picker_user['id'])
        if needs_provider and not checkout.get('provider_type'):
            return redirect('market:checkout_provider')
        if 'delivery_date' not in checkout:
            return redirect('market:checkout_schedule')
        if not needs_provider:
            return redirect('market:checkout_picker')
        # provider_type + delivery_date both set but no order exists yet —
        # only reachable if something interrupted the original confirm
        # request; resuming at checkout_schedule lets them just retry it.
        return redirect('market:checkout_schedule')
    return redirect('market:home')


ORDER_NEXT_STATUS = {
    'confirmed': 'picking',
    'picking': 'in_transit',
    'in_transit': 'delivered',
}


@role_required('picker')
def picker_home(request):
    from concurrent.futures import ThreadPoolExecutor

    from market.site_settings import get_platform_commission_pct

    client = get_client()
    picker_user = request.session['picker_user']
    picker_id = picker_user['id']

    # None of these seven lookups depend on each other's results — every
    # one of them used to be its own sequential Supabase round trip on
    # every single picker dashboard load (the page a picker sees the most,
    # often several times a shift). Run them in parallel instead, same
    # pattern already proven on the market page — wall-clock time becomes
    # the slowest one of these, not the sum of all seven.
    def _fetch_orders(statuses, order_by, desc=False, limit=None):
        q = (
            client.table('pickker_orders').select('*')
            .eq('picker_id', picker_id).in_('status', statuses).order(order_by, desc=desc)
        )
        if limit:
            q = q.limit(limit)
        return q.execute().data

    def _fetch_pending_count():
        return (
            client.table('pickker_orders').select('id', count='exact')
            .eq('picker_id', picker_id).eq('status', 'pending_payment').execute().count or 0
        )

    with ThreadPoolExecutor(max_workers=7) as executor:
        profile_future = executor.submit(
            lambda: (client.table('pickker_picker_profiles').select('*').eq('user_id', picker_id).execute().data or [None])[0]
        )
        active_orders_future = executor.submit(_fetch_orders, ['confirmed', 'picking', 'in_transit'], 'delivery_date')
        history_future = executor.submit(_fetch_orders, ['delivered'], 'updated_at', desc=True, limit=10)
        pending_count_future = executor.submit(_fetch_pending_count)
        earnings_future = executor.submit(get_picker_earnings_summary_and_history, picker_id, 10)
        performance_future = executor.submit(get_picker_performance, picker_id)
        rating_future = executor.submit(get_picker_rating_summary, picker_id)
        # Fetched unconditionally (cheap, and running in parallel with
        # everything else costs nothing) — only actually used below once
        # profile's approval status is known.
        status_future = executor.submit(get_picker_status, picker_id)

        profile = profile_future.result()
        active_orders = active_orders_future.result()
        history_orders = history_future.result()
        pending_count = pending_count_future.result()
        earnings, earnings_history = earnings_future.result()
        performance = performance_future.result()
        rating_summary = rating_future.result()
        availability_status = status_future.result() if profile and profile.get('is_approved') else None

    t = get_t(request)
    for order in active_orders:
        order['next_action_label'] = get_action_label(t, order['status'])
        order['status_label'] = get_status_label(t, order['status'])
    for order in history_orders:
        order['status_label'] = get_status_label(t, order['status'])

    commission_pct = get_platform_commission_pct()
    return render(request, 'accounts/picker_home.html', {
        'profile': profile,
        'active_orders': active_orders,
        'history_orders': history_orders,
        'pending_count': pending_count,
        'pending_payment_note': t['picker_pending_payment_note'].format(count=pending_count) if pending_count else '',
        'earnings': earnings,
        'earnings_history': earnings_history,
        'commission_note': t['picker_commission_note'].format(pct=commission_pct),
        'performance': performance,
        'rating_summary': rating_summary,
        'availability_status': availability_status,
    })


@role_required('picker')
def picker_picking_guide(request):
    """Static, bilingual best-practice tips for picking produce at the
    market — linked from the picker dashboard for anyone who wants a
    refresher on what customers actually value in a good picker."""
    return render(request, 'accounts/picker_picking_guide.html')


@role_required('picker')
def picker_log_fuel_stop(request):
    """B2B driver logs a fuel stop — a quick roadside form, not a profile
    edit, so no map: just an optional one-tap 'record my current location'
    button (plain getCurrentPosition) alongside the amount/liters/odometer/
    receipt fields."""
    from market.fuel_logs import create_fuel_log

    picker_user = request.session['picker_user']
    client = get_client()
    profile_resp = client.table('pickker_picker_profiles').select('*').eq('user_id', picker_user['id']).execute()
    profile = profile_resp.data[0] if profile_resp.data else None
    if not profile or not profile.get('company_id'):
        raise Http404

    if request.method == 'POST':
        t = get_t(request)
        receipt_url = None
        if request.FILES.get('receipt'):
            from .picker_documents import upload_picker_document
            try:
                receipt_url = upload_picker_document(
                    request.FILES['receipt'], 'fuel-receipts',
                    allowed_content_types=('image/jpeg', 'image/png', 'image/webp', 'application/pdf'),
                    max_size_bytes=8 * 1024 * 1024,
                )
            except ValueError as e:
                messages.error(request, str(e))
                return redirect('accounts:picker_log_fuel_stop')

        def _decimal_or_none(value):
            value = (value or '').strip()
            return value if value else None

        create_fuel_log(
            picker_id=picker_user['id'],
            company_id=profile['company_id'],
            truck_id=profile.get('truck_id'),
            station_name=request.POST.get('station_name', ''),
            amount_spent=_decimal_or_none(request.POST.get('amount_spent')),
            liters=_decimal_or_none(request.POST.get('liters')),
            odometer_km=_decimal_or_none(request.POST.get('odometer_km')),
            lat=_decimal_or_none(request.POST.get('lat')),
            lng=_decimal_or_none(request.POST.get('lng')),
            receipt_url=receipt_url,
            notes=request.POST.get('notes', ''),
        )
        messages.success(request, t['fuel_log_saved'])
        return redirect('accounts:picker_home')

    return render(request, 'accounts/picker_fuel_log.html', {})


@role_required('picker')
def picker_signoff_forms(request):
    """Lists the driver's own fleet company's active hand-off/sign-off
    templates — a driver picks one to fill out and sign."""
    from market.signoff_forms import get_active_templates_for_driver

    picker_user = request.session['picker_user']
    templates = get_active_templates_for_driver(picker_user['id'])
    return render(request, 'accounts/picker_signoff_forms.html', {'templates': templates})


@role_required('picker')
def picker_fill_signoff_form(request, template_id):
    """Renders only the fields the company enabled on this template (the
    driver never sees a disabled catalog field), plus a signature pad, and
    records the submission as a standalone entry — not tied to any order."""
    from market.signoff_forms import FIELD_CATALOG, create_submission, get_template

    picker_user = request.session['picker_user']
    client = get_client()
    profile_resp = client.table('pickker_picker_profiles').select('*').eq('user_id', picker_user['id']).execute()
    profile = profile_resp.data[0] if profile_resp.data else None
    template = get_template(template_id)
    if not profile or not template or template.get('company_id') != profile.get('company_id'):
        raise Http404

    fields_config = template.get('fields_config') or {}
    active_fields = []
    for key, meta in FIELD_CATALOG.items():
        cfg = fields_config.get(key) or {}
        if not cfg.get('enabled'):
            continue
        active_fields.append({
            'key': key,
            'type': meta['type'],
            'options': meta.get('options'),
            'label': cfg.get('label') or get_t(request)[meta['default_label_key']],
        })

    if request.method == 'POST':
        signature = request.POST.get('signature_data_url', '').strip()
        if not signature:
            messages.error(request, get_t(request)['signoff_form_signature_required'])
        else:
            values = {}
            for f in active_fields:
                if f['type'] == 'checkbox':
                    values[f['key']] = request.POST.get(f['key']) == 'on'
                else:
                    values[f['key']] = request.POST.get(f['key'], '').strip()
            create_submission(
                template_id=template_id, company_id=template['company_id'], picker_id=picker_user['id'],
                values=values, signature_data_url=signature, truck_id=profile.get('truck_id'),
            )
            messages.success(request, get_t(request)['signoff_form_submitted'])
            return redirect('accounts:picker_signoff_forms')

    return render(request, 'accounts/picker_fill_signoff_form.html', {
        'template': template,
        'active_fields': active_fields,
    })


@role_required('picker')
@require_POST
def picker_set_payout_frequency(request):
    picker_user = request.session['picker_user']
    frequency = request.POST.get('payout_frequency')
    set_payout_frequency(picker_user['id'], frequency)
    messages.success(request, get_t(request)['msg_payout_preference_set'].format(frequency=frequency))
    return redirect('accounts:picker_home')


@role_required('picker')
def picker_profile_edit(request):
    picker_user = request.session['picker_user']
    client = get_client()
    profile_resp = client.table('pickker_picker_profiles').select('*').eq('user_id', picker_user['id']).execute()
    profile = profile_resp.data[0] if profile_resp.data else None
    user_resp = client.table('pickker_users').select('*').eq('id', picker_user['id']).execute()
    user_row = user_resp.data[0] if user_resp.data else None

    if request.method == 'POST':
        first_name = request.POST.get('first_name', '').strip()
        sms_language = request.POST.get('sms_language', '').strip()
        client.table('pickker_users').update({
            'first_name': first_name,
            'last_name': request.POST.get('last_name', '').strip(),
            'phone_number': request.POST.get('phone_number', '').strip(),
            'sms_language': sms_language if sms_language in ('en', 'sw') else (user_row.get('sms_language') if user_row else 'sw'),
        }).eq('id', picker_user['id']).execute()

        lat = request.POST.get('lat')
        lng = request.POST.get('lng')
        from market.regions import get_or_create_region
        region = get_or_create_region(request.POST.get('region_name', ''))
        update_row = {
            'market_location': request.POST.get('market_location', '').strip(),
            'regions_served': request.POST.get('regions_served', '').strip(),
            'region_id': region['id'] if region else None,
            'location_lat': float(lat) if lat else profile.get('location_lat'),
            'location_lng': float(lng) if lng else profile.get('location_lng'),
        }
        if request.FILES.get('photo'):
            from .picker_documents import upload_picker_document
            update_row['photo_url'] = upload_picker_document(request.FILES['photo'], 'photos')
        license_number = request.POST.get('license_number')
        if license_number is not None:
            update_row['license_number'] = license_number.strip()
        if profile.get('picker_segment') == 'b2b':
            # Fleet-linked drivers (company_id set) get their truck type from
            # the truck their company assigns them to — self-editing it here
            # would desync truck_type_id from the actual truck_id they drive.
            if not profile.get('company_id'):
                truck_type_id = request.POST.get('truck_type_id')
                update_row['truck_type_id'] = int(truck_type_id) if truck_type_id else profile.get('truck_type_id')
            from market.fleet import set_picker_warehouses
            set_picker_warehouses(picker_user['id'], request.POST.getlist('warehouses'))
        else:
            market_id = request.POST.get('market_id')
            truck_type_id = request.POST.get('truck_type_id')
            update_row.update({
                'truck_type_id': int(truck_type_id) if truck_type_id else profile.get('truck_type_id'),
                'license_plate': request.POST.get('license_plate', '').strip(),
                'source_type': request.POST.get('source_type', profile['source_type']),
                'market_id': int(market_id) if market_id else None,
            })
        client.table('pickker_picker_profiles').update(update_row).eq('user_id', picker_user['id']).execute()

        # Keep the session's display name in sync so the nav avatar initial
        # and role label update immediately without needing to log back in.
        picker_user['first_name'] = first_name
        request.session['picker_user'] = picker_user
        request.session.modified = True

        messages.success(request, get_t(request)['msg_profile_updated'])
        return redirect('accounts:picker_profile_edit')

    from market.markets import get_active_markets
    from market.regions import get_region
    from market.truck_types import get_active_truck_types

    if profile and profile.get('region_id'):
        region = get_region(profile['region_id'])
        profile['region_name'] = region['name_en'] if region else ''

    warehouses = []
    picker_warehouse_keys = set()
    if profile and profile.get('picker_segment') == 'b2b':
        from market.fleet import get_picker_warehouses, get_vendor_tie_options
        warehouses = get_vendor_tie_options()
        picker_warehouse_keys = {f"{w['vendor_type']}:{w['vendor_id']}" for w in get_picker_warehouses(picker_user['id'])}

    return render(request, 'accounts/picker_profile_edit.html', {
        'user_row': user_row,
        'profile': profile,
        'source_choices': SourceType.CHOICES,
        'truck_types': get_active_truck_types(segment=profile.get('picker_segment')) if profile else [],
        'markets': get_active_markets(),
        'warehouses': warehouses,
        'picker_warehouse_keys': picker_warehouse_keys,
        'center': DAR_ES_SALAAM_CENTER,
    })


@role_required('picker')
def picker_settings(request):
    return render(request, 'accounts/picker_settings.html')


@role_required('picker')
@require_POST
def picker_delete_account(request):
    picker_user = request.session['picker_user']
    deleted, message = delete_or_suspend_self(picker_user['id'], get_t(request))
    request.session.flush()
    messages.success(request, message)
    return redirect('market:home')


@role_required('picker')
def advance_order(request, order_id):
    if request.method == 'POST':
        picker_user = request.session['picker_user']
        client = get_client()
        resp = client.table('pickker_orders').select('*').eq('id', order_id).execute()
        order = resp.data[0] if resp.data else None
        if order and order.get('picker_id') == picker_user['id']:
            next_status = ORDER_NEXT_STATUS.get(order['status'])
            if next_status == 'picking':
                from market.shop_payments import is_shop_payment_complete
                from market.vendors import is_vendor_payment_complete
                if not is_shop_payment_complete(order_id) or not is_vendor_payment_complete(order_id):
                    messages.error(request, get_t(request)['msg_shop_payment_pending'])
                    return redirect('accounts:picker_home')
            if next_status:
                update_row = {'status': next_status}
                if next_status == 'picking':
                    update_row['picking_started_at'] = datetime.now(timezone.utc).isoformat()
                elif next_status == 'in_transit':
                    update_row['delivery_started_at'] = datetime.now(timezone.utc).isoformat()
                elif next_status == 'delivered':
                    update_row['delivered_at'] = datetime.now(timezone.utc).isoformat()
                client.table('pickker_orders').update(update_row).eq('id', order_id).execute()
                order['status'] = next_status
                if next_status == 'delivered':
                    record_earning_if_delivered(order)
                notify_order_status_changed(order, next_status, changed_by_role='picker')
                push_order_status_changed(order, next_status.replace('_', ' ').title())
                if next_status == 'in_transit':
                    # Heading out now — take them straight to live navigation
                    # instead of back to the dashboard.
                    return redirect('accounts:picker_live_navigation', order_id=order_id)
    return redirect('accounts:picker_home')


@role_required('admin')
def admin_portal(request):
    """Each independent Supabase lookup is a real network round trip, so
    they're grouped into parallel waves (products/orders/picker_profiles
    first, then the per-order batches that need order ids) rather than run
    one after another — cuts this page's load time roughly 3x without
    changing what it queries."""
    from concurrent.futures import ThreadPoolExecutor

    from market.price_trends import get_trend_badges_for_products
    from market.shop_payments import get_paid_market_ids_batch, get_required_markets_batch
    from market.vendors import get_order_vendor_ties_batch

    client = get_client()

    with ThreadPoolExecutor(max_workers=3) as executor:
        products_future = executor.submit(lambda: client.table('pickker_products').select('*').order('name').execute().data)
        orders_future = executor.submit(lambda: client.table('pickker_orders').select('*').order('created_at', desc=True).execute().data)
        picker_profiles_future = executor.submit(lambda: client.table('pickker_picker_profiles').select('is_approved').execute().data)
        products = products_future.result()
        orders = orders_future.result()
        picker_profiles = picker_profiles_future.result()

    product_ids = [p['id'] for p in products]
    order_ids = [o['id'] for o in orders]
    user_ids = {o['customer_id'] for o in orders} | {o['picker_id'] for o in orders if o.get('picker_id')}

    def _fetch_users():
        if not user_ids:
            return {}
        resp = client.table('pickker_users').select('id, email, first_name, last_name').in_('id', list(user_ids)).execute()
        return {u['id']: u for u in resp.data}

    with ThreadPoolExecutor(max_workers=4) as executor:
        trend_badges_future = executor.submit(get_trend_badges_for_products, product_ids)
        users_future = executor.submit(_fetch_users)
        required_markets_future = executor.submit(get_required_markets_batch, order_ids)
        vendor_tie_future = executor.submit(get_order_vendor_ties_batch, order_ids)
        trend_badges = trend_badges_future.result()
        users_by_id = users_future.result()
        required_markets_by_order = required_markets_future.result()
        vendor_tie_by_order = vendor_tie_future.result()

    for product in products:
        product['trend_badge'] = trend_badges.get(product['id'])

    paid_market_ids_by_order = get_paid_market_ids_batch(list(required_markets_by_order.keys()))

    for order in orders:
        customer = users_by_id.get(order['customer_id'])
        order['customer_name'] = f"{customer['first_name']} {customer['last_name']}".strip() if customer else 'Unknown'
        picker = users_by_id.get(order.get('picker_id'))
        order['picker_name'] = f"{picker['first_name']} {picker['last_name']}".strip() if picker else '—'
        order['picking_duration_min'] = None
        if order.get('picking_started_at') and order.get('delivery_started_at'):
            started = datetime.fromisoformat(order['picking_started_at'].replace('Z', '+00:00'))
            left = datetime.fromisoformat(order['delivery_started_at'].replace('Z', '+00:00'))
            order['picking_duration_min'] = round((left - started).total_seconds() / 60, 1)

        required_markets = required_markets_by_order.get(order['id'], [])
        paid_market_ids = paid_market_ids_by_order.get(order['id'], set())
        order['required_markets'] = required_markets
        order['unpaid_markets'] = [m for m in required_markets if m['id'] not in paid_market_ids]
        order['shop_payment_complete'] = not order['unpaid_markets']
        order['vendor_tie'] = vendor_tie_by_order.get(order['id'])

    pending_picker_count = sum(1 for p in picker_profiles if not p.get('is_approved'))

    context = {
        'products': products,
        'product_count': len(products),
        'trending_count': sum(1 for p in products if p.get('is_trending')),
        'orders': orders,
        'pending_payment_count': sum(1 for o in orders if o['status'] == 'pending_payment'),
        'pending_picker_count': pending_picker_count,
        'status_choices': [
            ('pending_payment', 'Pending Payment'),
            ('confirmed', 'Confirmed'),
            ('picking', 'Picking'),
            ('in_transit', 'In Transit'),
            ('delivered', 'Delivered'),
            ('cancelled', 'Cancelled'),
        ],
    }
    return render(request, 'accounts/admin_portal.html', context)


@role_required('admin')
def admin_approve_payment(request, order_id):
    if request.method == 'POST':
        client = get_client()
        resp = client.table('pickker_orders').update({
            'status': 'confirmed', 'confirmed_at': datetime.now(timezone.utc).isoformat(),
        }).eq('id', order_id).eq('status', 'pending_payment').execute()
        if resp.data:
            order = resp.data[0]
            order['status'] = 'confirmed'
            notify_order_status_changed(order, 'confirmed', changed_by_role='admin')
            push_order_status_changed(order, 'Confirmed')
    return redirect('accounts:admin_portal')


@role_required('admin')
def admin_release_payment(request, order_id):
    """Marks the customer's payment for this order as released to whichever
    picker/company fulfilled it -- a communication label for trust/security
    ("your payment was held until delivery, now it's released"), not a real
    money-movement action (this app has no payment gateway anywhere; every
    payment is mobile-money-outside-the-app + WhatsApp proof + this kind of
    manual admin confirmation). Deliberately does NOT touch
    market/earnings.py's picker payout ledger -- record_earning_if_delivered
    already creates that picker's payout-eligible row the instant the order
    is marked delivered, independent of this label."""
    if request.method == 'POST':
        client = get_client()
        resp = client.table('pickker_orders').update({
            'payment_released_at': datetime.now(timezone.utc).isoformat(),
        }).eq('id', order_id).eq('status', 'delivered').is_('payment_released_at', 'null').execute()
        if resp.data:
            order = resp.data[0]
            from market.order_emails import notify_payment_released
            from market.push_notifications import push_payment_released
            notify_payment_released(order)
            push_payment_released(order)
    return redirect('accounts:admin_portal')


@role_required('admin')
def admin_set_order_status(request, order_id):
    if request.method == 'POST':
        status = request.POST.get('status')
        valid_statuses = {'pending_payment', 'confirmed', 'picking', 'in_transit', 'delivered', 'cancelled'}
        if status in ('picking', 'in_transit', 'delivered'):
            # A company-routed order can sit with company_id set and no
            # driver yet (see market/fleet.py::assign_driver_to_order) —
            # never let it get flipped to a fulfillment status with nobody
            # actually assigned to drive it. picker.advance_order already
            # enforces this implicitly (it requires picker_id == the acting
            # picker); admin's own override had no equivalent check.
            order_resp = get_client().table('pickker_orders').select('picker_id').eq('id', order_id).execute()
            if not order_resp.data or not order_resp.data[0].get('picker_id'):
                messages.error(request, 'This order has no driver assigned yet — assign one before advancing its status.')
                return redirect('accounts:admin_portal')
        if status == 'picking':
            from market.shop_payments import is_shop_payment_complete
            from market.vendors import is_vendor_payment_complete
            if not is_shop_payment_complete(order_id) or not is_vendor_payment_complete(order_id):
                messages.error(request, 'This order needs its shop/vendor payment recorded (see below) before it can move to Picking.')
                return redirect('accounts:admin_portal')
        if status in valid_statuses:
            client = get_client()
            update_row = {'status': status}
            if status == 'picking':
                update_row['picking_started_at'] = datetime.now(timezone.utc).isoformat()
            elif status == 'in_transit':
                update_row['delivery_started_at'] = datetime.now(timezone.utc).isoformat()
            elif status == 'delivered':
                update_row['delivered_at'] = datetime.now(timezone.utc).isoformat()
            resp = client.table('pickker_orders').update(update_row).eq('id', order_id).execute()
            if resp.data:
                order = resp.data[0]
                order['status'] = status
                if status == 'delivered':
                    record_earning_if_delivered(order)
                notify_order_status_changed(order, status, changed_by_role='admin')
                push_order_status_changed(order, status.replace('_', ' ').title())
    return redirect('accounts:admin_portal')


@role_required('admin')
@require_POST
def admin_mark_shop_payment(request, order_id):
    from market.shop_payments import record_shop_payment

    market_id = request.POST.get('market_id')
    amount = request.POST.get('amount', '').strip()
    try:
        market_id = int(market_id)
        amount_value = Decimal(amount)
        if amount_value <= 0:
            raise ValueError
    except (TypeError, ValueError, InvalidOperation):
        messages.error(request, 'Enter a valid market and a positive amount.')
        return redirect('accounts:admin_portal')

    admin_user = request.session['picker_user']
    admin_name = f"{admin_user.get('first_name', '')} {admin_user.get('last_name', '')}".strip() or admin_user.get('email', 'Admin')
    record_shop_payment(
        order_id, market_id, amount_value, admin_user['id'], admin_name,
        reference_note=request.POST.get('reference_note', ''),
    )
    messages.success(request, 'Shop payment recorded — the picker can now start picking this order.')
    return redirect('accounts:admin_portal')


@role_required('admin')
@require_POST
def admin_record_vendor_payment(request, order_id):
    """Same gate/ledger shape as admin_mark_shop_payment above, but for an
    order tied to a Warehouse/Industry (see market/vendors.py) rather than
    a Market — a separate, additive table, so this is a separate view
    rather than branching the existing one."""
    from market.vendors import get_order_vendor_tie, record_vendor_payment

    tie = get_order_vendor_tie(order_id)
    amount = request.POST.get('amount', '').strip()
    try:
        amount_value = Decimal(amount)
        if amount_value <= 0 or not tie:
            raise ValueError
    except (TypeError, ValueError, InvalidOperation):
        messages.error(request, 'Enter a valid positive amount for an order that has a warehouse/industry tie.')
        return redirect('accounts:admin_portal')

    admin_user = request.session['picker_user']
    admin_name = f"{admin_user.get('first_name', '')} {admin_user.get('last_name', '')}".strip() or admin_user.get('email', 'Admin')
    record_vendor_payment(
        order_id, tie['vendor_type'], tie['vendor_id'], amount_value, admin_user['id'], admin_name,
        reference_note=request.POST.get('reference_note', ''),
    )
    messages.success(request, 'Vendor payment recorded — the picker can now start picking this order.')
    return redirect('accounts:admin_portal')


@role_required('admin')
def admin_users(request):
    from market.truck_types import get_all_truck_types

    client = get_client()
    users = client.table('pickker_users').select('*').order('created_at', desc=True).execute().data

    customer_by_user = {p['user_id']: p for p in client.table('pickker_customer_profiles').select('*').execute().data}
    picker_by_user = {p['user_id']: p for p in client.table('pickker_picker_profiles').select('*').execute().data}
    truck_types_by_id = {t['id']: t for t in get_all_truck_types()}

    for u in users:
        u['customer_profile'] = customer_by_user.get(u['id'])
        u['picker_profile'] = picker_by_user.get(u['id'])
        if u['picker_profile'] and u['picker_profile'].get('truck_type_id'):
            u['picker_profile']['truck_type'] = truck_types_by_id.get(u['picker_profile']['truck_type_id'])

    pending_picker_count = sum(
        1 for u in users if u['role'] == Role.PICKER and u['picker_profile'] and not u['picker_profile'].get('is_approved')
    )

    role_filter = request.GET.get('role', '')
    if role_filter:
        users = [u for u in users if u['role'] == role_filter]

    picker_segment_filter = request.GET.get('picker_segment', '')
    if picker_segment_filter:
        users = [u for u in users if u['picker_profile'] and u['picker_profile'].get('picker_segment') == picker_segment_filter]

    search = request.GET.get('q', '').strip().lower()
    if search:
        users = [
            u for u in users
            if search in u['email'].lower() or search in f"{u['first_name']} {u['last_name']}".lower()
        ]

    return render(request, 'accounts/admin_users.html', {
        'users': users,
        'role_filter': role_filter,
        'picker_segment_filter': picker_segment_filter,
        'search': request.GET.get('q', ''),
        'pending_picker_count': pending_picker_count,
    })


@role_required('admin')
def admin_register_b2b_picker(request):
    from market.truck_types import get_active_truck_types
    from django.utils.crypto import get_random_string
    from .password_reset import create_reset_token_for_user

    truck_types = get_active_truck_types(segment='b2b')

    if request.method == 'POST':
        email = request.POST.get('email', '').strip().lower()
        first_name = request.POST.get('first_name', '').strip()
        last_name = request.POST.get('last_name', '').strip()
        phone_number = request.POST.get('phone_number', '').strip()
        truck_type_id = request.POST.get('truck_type_id')
        market_location = request.POST.get('market_location', '').strip()
        regions_served = request.POST.get('regions_served', '').strip()
        lat = request.POST.get('location_lat')
        lng = request.POST.get('location_lng')

        if not email or not first_name or not truck_type_id:
            messages.error(request, 'Name, email, and truck type are required.')
        else:
            client = get_client()
            existing = client.table('pickker_users').select('id').eq('email', email).execute()
            if existing.data:
                messages.error(request, f'{email} is already registered.')
            else:
                user_row = {
                    'email': email,
                    'password_hash': make_password(get_random_string(32)),
                    'role': Role.PICKER,
                    'first_name': first_name,
                    'last_name': last_name,
                    'phone_number': phone_number,
                    'preferred_language': 'en',
                    'sms_language': 'sw',
                    'is_verified': True,
                    'is_active': True,
                }
                user_resp = client.table('pickker_users').insert(user_row).execute()
                user = user_resp.data[0]

                client.table('pickker_picker_profiles').insert({
                    'user_id': user['id'],
                    'picker_segment': 'b2b',
                    'truck_type_id': int(truck_type_id),
                    'market_location': market_location,
                    'regions_served': regions_served,
                    'location_lat': float(lat) if lat else None,
                    'location_lng': float(lng) if lng else None,
                    'is_approved': True,
                }).execute()

                token = create_reset_token_for_user(user['id'])
                set_password_url = absolute_url(request, reverse('accounts:reset_password', args=[token]))
                send_email(
                    email,
                    "You've been registered as a PickkerMarket B2B delivery partner",
                    f"Hi {first_name},\n\n"
                    "An admin has registered you as a B2B delivery partner on PickkerMarket. "
                    "You're already approved and ready to receive deliveries.\n\n"
                    f"Set your password to log in (this link is valid for 1 hour — if it expires, use "
                    "\"Forgot password\" on the login page instead):\n"
                    f"{set_password_url}\n\n"
                    "— The PickkerMarket Team",
                )
                from market.sms import send_welcome_sms, sms_lang
                send_welcome_sms(phone_number, first_name, sms_lang(user.get('sms_language')), 'picker', needs_verification=False)

                notify_admin(
                    f"B2B picker registered: {first_name} {last_name}",
                    f"Email: {email}\nTruck type: {truck_type_id}\nRegistered by admin, auto-approved.",
                )
                messages.success(request, f'{first_name} {last_name} registered as a B2B picker and notified by email.')
                return redirect('accounts:admin_users')

    return render(request, 'accounts/admin_register_b2b_picker.html', {
        'truck_types': truck_types,
        'center': DAR_ES_SALAAM_CENTER,
    })


@role_required('admin')
@require_POST
def admin_approve_picker(request, user_id):
    get_client().table('pickker_picker_profiles').update({'is_approved': True}).eq('user_id', user_id).execute()
    picker_resp = get_client().table('pickker_users').select('email, first_name').eq('id', user_id).execute()
    if picker_resp.data:
        picker = picker_resp.data[0]
        send_email(
            picker['email'],
            'Your PickkerMarket picker account is approved!',
            f"Hi {picker['first_name'] or ''},\n\n"
            "Your picker account has been approved by our team. You can now go available in your "
            "dashboard and start receiving deliveries.\n\n"
            "— The PickkerMarket Team",
        )
    messages.success(request, 'Picker approved — they can now go available and appear to customers.')
    return redirect('accounts:admin_users')


@role_required('admin')
@require_POST
def admin_toggle_user_active(request, user_id):
    client = get_client()
    resp = client.table('pickker_users').select('is_active').eq('id', user_id).execute()
    if resp.data:
        current = resp.data[0]['is_active']
        client.table('pickker_users').update({'is_active': not current}).eq('id', user_id).execute()
        messages.success(request, 'User suspended.' if current else 'User activated.')
    return redirect('accounts:admin_users')


@role_required('admin')
@require_POST
def admin_delete_user(request, user_id):
    """Permanently removes a user account. Blocked if the user has any order
    history (as customer or picker) so real transaction/delivery records can
    never be silently destroyed — Suspend is the tool for disabling access
    to an account like that. Only clean accounts (never transacted) can be
    permanently deleted this way."""
    client = get_client()
    user_resp = client.table('pickker_users').select('email').eq('id', user_id).execute()
    if not user_resp.data:
        return redirect('accounts:admin_users')
    user_email = user_resp.data[0]['email']

    has_orders = (
        client.table('pickker_orders').select('id').eq('customer_id', user_id).limit(1).execute().data
        or client.table('pickker_orders').select('id').eq('picker_id', user_id).limit(1).execute().data
    )
    owns_company = client.table('pickker_fleet_companies').select('id').eq('owner_user_id', user_id).limit(1).execute().data
    if has_orders or owns_company:
        reason = 'order history' if has_orders else 'a registered fleet company'
        messages.error(
            request,
            f"Can't permanently delete {user_email} — this account has {reason} that must be kept as a "
            "real record. Use Suspend instead to block their access.",
        )
        return redirect('accounts:admin_users')

    # Every other table below has a restrictive (NO ACTION) foreign key to
    # this user — none represent a standalone real record worth blocking
    # deletion over (order history / company ownership above are the only
    # things that do), so they're cleared here rather than checked. Without
    # this, deleting an account that ever sent a message, was rated, or had
    # an earnings/payout entry touched fails with a raw FK-violation error —
    # the real bug being fixed here, not something new this session broke.
    client.table('pickker_saved_plans').delete().eq('customer_id', user_id).execute()
    client.table('pickker_api_keys').delete().eq('owner_user_id', user_id).execute()
    client.table('pickker_api_subscriptions').delete().eq('owner_user_id', user_id).execute()
    client.table('pickker_order_messages').delete().eq('sender_id', user_id).execute()
    client.table('pickker_fleet_messages').delete().eq('sender_id', user_id).execute()
    client.table('pickker_fleet_messages').delete().eq('driver_id', user_id).execute()
    client.table('pickker_fleet_message_reads').delete().eq('user_id', user_id).execute()
    client.table('pickker_fleet_message_reads').delete().eq('driver_id', user_id).execute()
    client.table('pickker_picker_earnings').update({'paid_by': None}).eq('paid_by', user_id).execute()
    client.table('pickker_ratings').delete().eq('customer_id', user_id).execute()
    client.table('pickker_ratings').delete().eq('picker_id', user_id).execute()
    # No real FK constraint (plain bigint columns, same convention as the
    # rest of this schema), but clear for data hygiene rather than leaving
    # orphaned rows pointing at a deleted user.
    client.table('pickker_fuel_logs').delete().eq('picker_id', user_id).execute()
    client.table('pickker_signoff_submissions').delete().eq('picker_id', user_id).execute()
    client.table('pickker_users').delete().eq('id', user_id).execute()
    messages.success(request, f'{user_email} has been permanently deleted.')
    return redirect('accounts:admin_users')


@role_required('admin')
def admin_stale_orders(request):
    """Orders stuck on pending_payment for longer than a reasonable window —
    reviewed and deleted one at a time by an admin, never automatically, so a
    genuine payment that's about to be confirmed can't be wiped out by mistake."""
    try:
        days = int(request.GET.get('days', 3))
    except ValueError:
        days = 3
    days = max(1, min(days, 90))

    cutoff = (datetime.now(timezone.utc) - timedelta(days=days)).isoformat()
    client = get_client()
    orders = (
        client.table('pickker_orders')
        .select('*')
        .eq('status', 'pending_payment')
        .lt('created_at', cutoff)
        .order('created_at')
        .execute()
        .data
    )

    customer_ids = {o['customer_id'] for o in orders}
    users_by_id = {}
    if customer_ids:
        users_resp = client.table('pickker_users').select('id, email, first_name, last_name').in_('id', list(customer_ids)).execute()
        users_by_id = {u['id']: u for u in users_resp.data}

    now = datetime.now(timezone.utc)
    for o in orders:
        customer = users_by_id.get(o['customer_id'])
        o['customer_name'] = f"{customer['first_name']} {customer['last_name']}".strip() if customer else 'Unknown'
        o['customer_email'] = customer['email'] if customer else ''
        created = datetime.fromisoformat(o['created_at'].replace('Z', '+00:00'))
        o['age_days'] = (now - created).days

    return render(request, 'accounts/admin_stale_orders.html', {'orders': orders, 'days': days})


@role_required('admin')
@require_POST
def admin_delete_stale_order(request, order_id):
    """Re-checks the status server-side before deleting — protects against a
    payment being confirmed in another tab between page load and this click."""
    from market.stock import restore_stock
    from market.vendors import get_order_vendor_tie

    client = get_client()
    resp = client.table('pickker_orders').select('status').eq('id', order_id).execute()
    if resp.data and resp.data[0]['status'] == 'pending_payment':
        # A warehouse/industry-tied order already had its stock decremented
        # at checkout time (see _checkout_picker_vendor_fleet's _after_create
        # in market/views.py) — well before payment is ever confirmed. If
        # this stale order is simply deleted, that stock is gone for good;
        # restore it here first, same as the rollback path used when a
        # multi-item order's own decrement fails partway through.
        tie = get_order_vendor_tie(order_id)
        if tie:
            items = client.table('pickker_order_items').select('product_id, quantity').eq('order_id', order_id).execute().data
            for item in items:
                restore_stock(tie['vendor_type'], tie['vendor_id'], item['product_id'], item['quantity'], order_id=order_id)
        client.table('pickker_orders').delete().eq('id', order_id).execute()
        messages.success(request, f'Order #{order_id} deleted.')
    else:
        messages.error(request, f"Order #{order_id} is no longer pending payment — it was left untouched.")
    return redirect('accounts:admin_stale_orders')


@role_required('admin')
def admin_payment_methods(request):
    from market.payment_methods import PROVIDER_CHOICES, create_payment_method, get_all_payment_methods

    if request.method == 'POST':
        provider = request.POST.get('provider', 'other')
        lipa_number = request.POST.get('lipa_number', '').strip()
        if lipa_number:
            create_payment_method(
                provider=provider,
                lipa_number=lipa_number,
                account_name=request.POST.get('account_name', ''),
                instructions=request.POST.get('instructions', ''),
            )
            messages.success(request, 'Payment method added.')
        else:
            messages.error(request, 'A Lipa Namba / number is required.')
        return redirect('accounts:admin_payment_methods')

    return render(request, 'accounts/admin_payment_methods.html', {
        'methods': get_all_payment_methods(),
        'provider_choices': PROVIDER_CHOICES,
    })


@role_required('admin')
def admin_contact_settings(request):
    from market.site_settings import (
        get_contact_settings, get_whatsapp_number, update_contact_settings, update_whatsapp_number,
    )

    if request.method == 'POST':
        update_contact_settings(
            support_email=request.POST.get('support_email', ''),
            inquiry_email=request.POST.get('inquiry_email', ''),
            direct_email=request.POST.get('direct_email', ''),
            phone_numbers=request.POST.get('phone_numbers', ''),
            office_address=request.POST.get('office_address', ''),
        )
        update_whatsapp_number(request.POST.get('whatsapp_number', ''))
        messages.success(request, 'Contact info updated.')
        return redirect('accounts:admin_contact_settings')

    return render(request, 'accounts/admin_contact_settings.html', {
        'contact_settings': get_contact_settings(),
        'whatsapp_number': get_whatsapp_number(),
    })


@role_required('admin')
def admin_home_content(request):
    from market.site_content import (
        create_news, create_partner, get_all_news, get_all_partners, get_hero_image_url, get_home_text,
        get_how_to_content, get_partners_heading, update_hero_image_url, update_home_text, update_how_to_content,
        update_partners_heading,
    )

    if request.method == 'POST':
        if 'hero_image_save' in request.POST:
            if request.FILES.get('hero_image'):
                update_hero_image_url(_save_uploaded_image(request.FILES['hero_image'], 'home'))
                messages.success(request, 'Homepage hero image updated.')
            else:
                messages.error(request, 'Choose an image to upload.')
        elif 'home_text_save' in request.POST:
            update_home_text(
                hero_title_line1=request.POST.get('hero_title_line1', ''),
                hero_title_line2=request.POST.get('hero_title_line2', ''),
                hero_title_line3=request.POST.get('hero_title_line3', ''),
                home_plan_label=request.POST.get('home_plan_label', ''),
                home_order_label=request.POST.get('home_order_label', ''),
                home_market_pill=request.POST.get('home_market_pill', ''),
                home_news_heading=request.POST.get('home_news_heading', ''),
            )
            messages.success(request, 'Homepage text updated.')
        elif 'how_to_save' in request.POST:
            image_url = _save_uploaded_image(request.FILES['image'], 'home') if request.FILES.get('image') else None
            thumbnail_url = _save_uploaded_image(request.FILES['thumbnail'], 'home') if request.FILES.get('thumbnail') else None
            update_how_to_content(
                title=request.POST.get('how_to_title', ''),
                description=request.POST.get('how_to_description', ''),
                video_url=request.POST.get('video_url', ''),
                image_url=image_url,
                thumbnail_url=thumbnail_url,
            )
            messages.success(request, 'Homepage content updated.')
        elif 'partners_heading_save' in request.POST:
            update_partners_heading(request.POST.get('partners_heading', ''), request.POST.get('partners_subheading', ''))
            messages.success(request, 'Partners section heading updated.')
        elif request.POST.get('partner_name'):
            if not request.FILES.get('partner_logo'):
                messages.error(request, 'Choose a logo image for the new partner.')
            else:
                logo_url = _save_uploaded_image(request.FILES['partner_logo'], 'partners')
                create_partner(request.POST.get('partner_name', ''), logo_url, request.POST.get('partner_website', ''))
                messages.success(request, 'Partner added.')
        elif request.POST.get('news_title'):
            news_image_url = _save_uploaded_image(request.FILES['news_image'], 'home') if request.FILES.get('news_image') else None
            create_news(request.POST.get('news_title', ''), request.POST.get('news_body', ''), image_url=news_image_url)
            messages.success(request, 'News item added.')
        return redirect('accounts:admin_home_content')

    return render(request, 'accounts/admin_home_content.html', {
        'hero_image_url': get_hero_image_url(),
        'home_text': get_home_text(),
        'how_to': get_how_to_content(),
        'news_items': get_all_news(),
        'partners': get_all_partners(),
        'partners_heading': get_partners_heading(),
    })


@role_required('admin')
@require_POST
def admin_edit_partner(request, partner_id):
    from market.site_content import update_partner
    logo_url = _save_uploaded_image(request.FILES['partner_logo'], 'partners') if request.FILES.get('partner_logo') else None
    update_partner(partner_id, request.POST.get('partner_name', ''), request.POST.get('partner_website', ''), logo_url=logo_url)
    messages.success(request, 'Partner updated.')
    return redirect('accounts:admin_home_content')


@role_required('admin')
@require_POST
def admin_toggle_partner(request, partner_id):
    from market.site_content import toggle_partner_active
    now_active = toggle_partner_active(partner_id)
    messages.success(request, 'Partner shown on homepage.' if now_active else 'Partner hidden from homepage.')
    return redirect('accounts:admin_home_content')


@role_required('admin')
def admin_broadcast(request):
    from market.broadcast import build_market_trend_message, send_broadcast

    context = {
        'audience': 'customers',
        'use_sms': True,
        'use_email': True,
        'subject': '',
        'message': '',
    }

    if request.method == 'POST':
        audience = request.POST.get('audience', 'customers')
        subject = request.POST.get('subject', '').strip() or 'Update from PickkerMarket'
        message = request.POST.get('message', '').strip()
        use_sms = request.POST.get('use_sms') == '1'
        use_email = request.POST.get('use_email') == '1'
        context.update({'audience': audience, 'use_sms': use_sms, 'use_email': use_email, 'subject': subject, 'message': message})

        if 'fill_market_trend' in request.POST:
            context['message'] = build_market_trend_message()
            context['subject'] = 'Market Price Update — PickkerMarket'
        elif not message:
            messages.error(request, 'Write a message before sending.')
        elif not use_sms and not use_email:
            messages.error(request, 'Choose at least one channel — SMS or email.')
        else:
            result = send_broadcast(audience, subject, message, use_sms, use_email)
            messages.success(
                request,
                f"Sent to {result['recipient_count']} recipient(s) — "
                f"{result['sms_sent']} SMS delivered, {result['email_attempted']} email(s) attempted.",
            )
            context.update({'subject': '', 'message': ''})

    return render(request, 'accounts/admin_broadcast.html', context)


@role_required('admin')
@require_POST
def admin_delete_partner(request, partner_id):
    from market.site_content import delete_partner
    delete_partner(partner_id)
    messages.success(request, 'Partner deleted.')
    return redirect('accounts:admin_home_content')


@role_required('admin')
@require_POST
def admin_edit_news(request, news_id):
    from market.site_content import update_news
    image_url = _save_uploaded_image(request.FILES['news_image'], 'home') if request.FILES.get('news_image') else None
    update_news(news_id, request.POST.get('news_title', ''), request.POST.get('news_body', ''), image_url=image_url)
    messages.success(request, 'News item updated.')
    return redirect('accounts:admin_home_content')


@role_required('admin')
@require_POST
def admin_toggle_news(request, news_id):
    from market.site_content import toggle_news_active
    now_active = toggle_news_active(news_id)
    messages.success(request, 'News item shown on homepage.' if now_active else 'News item hidden from homepage.')
    return redirect('accounts:admin_home_content')


@role_required('admin')
@require_POST
def admin_delete_news(request, news_id):
    from market.site_content import delete_news
    delete_news(news_id)
    messages.success(request, 'News item deleted.')
    return redirect('accounts:admin_home_content')


WEEKDAY_CHOICES = [
    (0, 'Monday'), (1, 'Tuesday'), (2, 'Wednesday'), (3, 'Thursday'),
    (4, 'Friday'), (5, 'Saturday'), (6, 'Sunday'),
]


@role_required('admin')
def admin_picker_settings(request):
    from market.site_settings import (
        get_b2b_far_distance_km, get_b2b_km_per_day, get_b2b_max_orders_per_day, get_close_radius_km,
        get_default_market_radius_km, get_default_min_order_kg,
        get_delivery_weekdays, get_far_radius_km, get_market_radius_options_km,
        get_max_orders_per_day, get_multi_stop_pickup_enabled, update_b2b_far_distance_km,
        update_b2b_km_per_day, update_b2b_max_orders_per_day,
        update_close_radius_km, update_default_market_radius_km, update_default_min_order_kg,
        update_delivery_weekdays, update_far_radius_km, update_market_radius_options_km,
        update_max_orders_per_day, update_multi_stop_pickup_enabled,
    )

    if request.method == 'POST':
        try:
            value = int(request.POST.get('max_orders_per_day', ''))
            b2b_value = int(request.POST.get('b2b_max_orders_per_day', ''))
        except ValueError:
            value = None
            b2b_value = None
        try:
            default_radius = float(request.POST.get('default_market_radius_km', ''))
        except ValueError:
            default_radius = None
        try:
            close_radius = float(request.POST.get('close_radius_km', ''))
            far_radius = float(request.POST.get('far_radius_km', ''))
            default_min_order_kg = float(request.POST.get('default_min_order_kg', ''))
        except ValueError:
            close_radius = far_radius = default_min_order_kg = None
        try:
            b2b_far_distance = float(request.POST.get('b2b_far_distance_km', ''))
            b2b_km_per_day = float(request.POST.get('b2b_km_per_day', ''))
        except ValueError:
            b2b_far_distance = b2b_km_per_day = None
        weekdays = [int(d) for d in request.POST.getlist('delivery_weekdays')]
        try:
            radius_options = [float(x.strip()) for x in request.POST.get('market_radius_options_km', '').split(',') if x.strip()]
        except ValueError:
            radius_options = []
        if value is None or value < 1 or b2b_value is None or b2b_value < 1:
            messages.error(request, 'Enter a whole number of at least 1 for both fields.')
        elif not weekdays:
            messages.error(request, 'Select at least one delivery day.')
        elif default_radius is None or default_radius <= 0:
            messages.error(request, 'Enter a default market radius (km) greater than 0.')
        elif close_radius is None or close_radius <= 0 or far_radius is None or far_radius <= close_radius:
            messages.error(request, 'Enter a close-delivery radius (km) greater than 0, and a far-delivery radius (km) greater than the close radius.')
        elif default_min_order_kg is None or default_min_order_kg < 0:
            messages.error(request, 'Enter a default minimum order (kg) of 0 or more.')
        elif b2b_far_distance is None or b2b_far_distance <= 0 or b2b_km_per_day is None or b2b_km_per_day <= 0:
            messages.error(request, 'Enter a B2B far-order distance (km) and km-per-day figure, both greater than 0.')
        elif not update_market_radius_options_km(radius_options):
            messages.error(request, 'Enter at least one valid "near you" radius (km), comma-separated.')
        else:
            update_max_orders_per_day(value)
            update_b2b_max_orders_per_day(b2b_value)
            update_delivery_weekdays(weekdays)
            update_multi_stop_pickup_enabled(request.POST.get('multi_stop_pickup_enabled') == '1')
            update_default_market_radius_km(default_radius)
            update_close_radius_km(close_radius)
            update_far_radius_km(far_radius)
            update_default_min_order_kg(default_min_order_kg)
            update_b2b_far_distance_km(b2b_far_distance)
            update_b2b_km_per_day(b2b_km_per_day)
            messages.success(request, f'Saved — B2C pickers capped at {value}/day, B2B pickers at {b2b_value}/day.')
            return redirect('accounts:admin_picker_settings')

    active_weekdays = get_delivery_weekdays()
    return render(request, 'accounts/admin_picker_settings.html', {
        'max_orders_per_day': get_max_orders_per_day(),
        'b2b_max_orders_per_day': get_b2b_max_orders_per_day(),
        'weekday_choices': [{'value': v, 'label': label, 'checked': v in active_weekdays} for v, label in WEEKDAY_CHOICES],
        'multi_stop_pickup_enabled': get_multi_stop_pickup_enabled(),
        'market_radius_options_km': ','.join(str(int(v)) if v == int(v) else str(v) for v in get_market_radius_options_km()),
        'default_market_radius_km': get_default_market_radius_km(),
        'close_radius_km': get_close_radius_km(),
        'far_radius_km': get_far_radius_km(),
        'default_min_order_kg': get_default_min_order_kg(),
        'b2b_far_distance_km': get_b2b_far_distance_km(),
        'b2b_km_per_day': get_b2b_km_per_day(),
    })


@role_required('admin')
def admin_fee_settings(request):
    from market.site_settings import get_fee_settings, update_fee_settings

    fields = [
        'delivery_fee_per_km', 'b2b_delivery_fee_per_km', 'b2b_delivery_fee_per_kg',
        'picking_tier1_max_kg', 'picking_tier1_fee',
        'picking_tier2_max_kg', 'picking_tier2_fee', 'picking_tier3_max_kg',
        'picking_tier3_fee', 'picking_tier4_fee', 'picking_extra_item_fee', 'quick_delivery_fee_pct', 'platform_commission_pct',
        'package_small_max_kg', 'package_small_max_qty', 'package_fee_small',
        'package_medium_max_kg', 'package_medium_max_qty', 'package_fee_medium', 'package_fee_large',
    ]

    if request.method == 'POST':
        try:
            values = {f: Decimal(request.POST.get(f, '')) for f in fields}
        except Exception:
            values = None
        if values is None or any(v < 0 for v in values.values()) or values['platform_commission_pct'] > 100:
            messages.error(request, 'Enter valid non-negative numbers for every field (commission must be 0-100%).')
        else:
            update_fee_settings(**{f: str(v) for f, v in values.items()})
            messages.success(request, 'Fee settings saved.')
            return redirect('accounts:admin_fee_settings')

    return render(request, 'accounts/admin_fee_settings.html', {
        'fees': get_fee_settings(),
    })


@role_required('admin')
@require_POST
def admin_edit_payment_method(request, method_id):
    from market.payment_methods import update_payment_method
    update_payment_method(
        method_id,
        provider=request.POST.get('provider', 'other'),
        lipa_number=request.POST.get('lipa_number', ''),
        account_name=request.POST.get('account_name', ''),
        instructions=request.POST.get('instructions', ''),
    )
    messages.success(request, 'Payment method updated.')
    return redirect('accounts:admin_payment_methods')


@role_required('admin')
@require_POST
def admin_toggle_payment_method(request, method_id):
    from market.payment_methods import toggle_payment_method_active
    now_active = toggle_payment_method_active(method_id)
    messages.success(request, 'Payment method activated.' if now_active else 'Payment method deactivated.')
    return redirect('accounts:admin_payment_methods')


@role_required('admin')
def admin_truck_types(request):
    from market.truck_types import create_truck_type, get_all_truck_types

    if request.method == 'POST':
        name = request.POST.get('name', '').strip()
        max_weight_kg = request.POST.get('max_weight_kg', '').strip()
        base_rate = request.POST.get('base_rate', '').strip()
        if name and max_weight_kg and base_rate:
            icon_url = _save_truck_icon(request.FILES['icon_image']) if request.FILES.get('icon_image') else None
            per_km_rate = request.POST.get('per_km_rate', '').strip()
            create_truck_type(
                name=name,
                capacity_label=request.POST.get('capacity_label', ''),
                max_weight_kg=max_weight_kg,
                base_rate=base_rate,
                icon=request.POST.get('icon', ''),
                icon_url=icon_url,
                segment=request.POST.get('segment', 'b2b'),
                per_km_rate=per_km_rate or None,
            )
            messages.success(request, f'{name} added.')
        else:
            messages.error(request, 'Name, max weight, and base rate are required.')
        return redirect('accounts:admin_truck_types')

    return render(request, 'accounts/admin_truck_types.html', {
        'truck_types': get_all_truck_types(),
    })


@role_required('admin')
def admin_fleet_companies(request):
    from market.fleet import get_all_companies, get_company_drivers, get_company_trucks, get_truck_warehouses
    from market.regions import get_regions_by_id
    from market.vendors import get_vendors_owned_by_company

    companies = get_all_companies()
    owner_ids = [c['owner_user_id'] for c in companies]
    profiles_by_owner = {}
    if owner_ids:
        profiles_resp = get_client().table('pickker_customer_profiles').select('user_id, tin_number, business_license_number').in_('user_id', owner_ids).execute()
        profiles_by_owner = {p['user_id']: p for p in profiles_resp.data}
    regions_by_id = get_regions_by_id([c.get('region_id') for c in companies])

    for company in companies:
        trucks = get_company_trucks(company['id'])
        for truck in trucks:
            truck['warehouses'] = get_truck_warehouses(truck['id'])
        company['trucks'] = trucks
        company['drivers'] = get_company_drivers(company['id'])
        company['owned_vendors'] = get_vendors_owned_by_company(company['id'])
        owner_profile = profiles_by_owner.get(company['owner_user_id'], {})
        company['tin_number'] = owner_profile.get('tin_number')
        company['business_license_number'] = owner_profile.get('business_license_number')
        company['region_name'] = regions_by_id.get(company.get('region_id'), {}).get('name_en')

    return render(request, 'accounts/admin_fleet_companies.html', {
        'companies': companies,
    })


@role_required('admin')
def admin_fleet_orders(request):
    """Platform-wide visibility over every order awaiting internal company
    assignment (market/fleet.py::get_all_unassigned_orders — company_id set,
    picker_id still None) — the admin counterpart to a company's own
    'Pending Assignment' tab on fleet_dashboard, scoped to every company at
    once instead of just the caller's own."""
    from market.fleet import get_all_companies, get_all_unassigned_orders, get_assignment_sla_badge, get_company_drivers, resolve_picker_truck_type
    from market.truck_types import get_active_truck_types

    orders = get_all_unassigned_orders()
    companies_by_id = {c['id']: c for c in get_all_companies()}
    truck_types_by_id = {tt['id']: tt for tt in get_active_truck_types(segment='b2b')}
    drivers_by_company = {}
    for order in orders:
        company_id = order['company_id']
        order['company'] = companies_by_id.get(company_id)
        sla = get_assignment_sla_badge(order)
        order['sla_hours'], order['sla_class'] = sla if sla else (None, None)
        if company_id not in drivers_by_company:
            drivers_by_company[company_id] = get_company_drivers(company_id)
        order_truck_type = truck_types_by_id.get(order.get('truck_type_id'))
        eligible_drivers = []
        for d in drivers_by_company[company_id]:
            if not d.get('user', {}).get('is_active'):
                continue
            driver_truck_type = truck_types_by_id.get(resolve_picker_truck_type(d))
            if order_truck_type and driver_truck_type and float(driver_truck_type['max_weight_kg']) < float(order_truck_type.get('max_weight_kg') or 0):
                continue
            eligible_drivers.append(d)
        order['eligible_drivers'] = eligible_drivers

    return render(request, 'accounts/admin_fleet_orders.html', {
        'orders': orders,
    })


@role_required('admin')
@require_POST
def admin_assign_order_driver(request, order_id):
    """Admin override of the company's own driver-assignment step — never
    bypasses the same validation (order really unassigned, driver really
    belongs to that order's company, truck weight-capable, driver has a
    location) since it calls the exact same market/fleet.py::
    assign_driver_to_order used by the company's self-service path, just
    without the company_id scope so admin can act across every company."""
    from market.fleet import assign_driver_to_order

    driver_id = request.POST.get('driver_id')
    if not driver_id:
        messages.error(request, 'Choose a driver.')
        return redirect('accounts:admin_fleet_orders')

    ok, reason = assign_driver_to_order(order_id, int(driver_id))
    t = get_t(request)
    if ok:
        messages.success(request, t['msg_driver_assigned'])
    else:
        messages.error(request, t.get(f'msg_assign_{reason}', t['msg_assign_not_found']))
    return redirect('accounts:admin_fleet_orders')


@role_required('admin')
def admin_fleet_trucks(request):
    from market.fleet import (
        admin_create_truck, admin_update_truck, get_all_companies, get_all_trucks_with_company, get_truck_warehouses,
        get_vendor_tie_options, set_truck_warehouses,
    )
    from market.truck_types import get_active_truck_types

    if request.method == 'POST':
        company_id = request.POST.get('company_id')
        truck_type_id = request.POST.get('truck_type_id')
        if company_id and truck_type_id:
            lat = _parse_coord(request.POST.get('lat'))
            lng = _parse_coord(request.POST.get('lng'))
            truck = admin_create_truck(
                int(company_id), truck_type_id,
                nickname=request.POST.get('nickname', ''), license_plate=request.POST.get('license_plate', ''),
                lat=lat, lng=lng,
            )
            if truck:
                set_truck_warehouses(truck['id'], request.POST.getlist('warehouses'))
            messages.success(request, 'Truck registered.')
        else:
            messages.error(request, 'Choose a company and truck type.')
        return redirect('accounts:admin_fleet_trucks')

    trucks = get_all_trucks_with_company()
    for truck in trucks:
        truck['warehouses'] = get_truck_warehouses(truck['id'])

    return render(request, 'accounts/admin_fleet_trucks.html', {
        'trucks': trucks,
        'companies': get_all_companies(),
        'truck_types': get_active_truck_types(segment='b2b'),
        'warehouses': get_vendor_tie_options(),
        'center': DAR_ES_SALAAM_CENTER,
    })


@role_required('admin')
@require_POST
def admin_edit_fleet_truck(request, truck_id):
    from market.fleet import admin_update_truck, set_truck_warehouses

    company_id = request.POST.get('company_id')
    truck_type_id = request.POST.get('truck_type_id')
    lat = _parse_coord(request.POST.get('lat'))
    lng = _parse_coord(request.POST.get('lng'))
    admin_update_truck(
        truck_id, int(company_id), truck_type_id,
        nickname=request.POST.get('nickname', ''), license_plate=request.POST.get('license_plate', ''),
        lat=lat, lng=lng,
    )
    set_truck_warehouses(truck_id, request.POST.getlist('warehouses'))
    messages.success(request, 'Truck updated.')
    return redirect('accounts:admin_fleet_trucks')


@role_required('admin')
@require_POST
def admin_delete_fleet_truck(request, truck_id):
    from market.fleet import admin_delete_truck
    admin_delete_truck(truck_id)
    messages.success(request, 'Truck removed.')
    return redirect('accounts:admin_fleet_trucks')


@role_required('admin')
@require_POST
def admin_approve_fleet_company(request, company_id):
    from market.fleet import approve_company, get_company

    approve_company(company_id)
    company = get_company(company_id)
    if company:
        owner_resp = get_client().table('pickker_users').select('email, first_name').eq('id', company['owner_user_id']).execute()
        if owner_resp.data:
            owner = owner_resp.data[0]
            send_email(
                owner['email'],
                'Your PickkerMarket fleet is approved!',
                f"Hi {owner['first_name'] or ''},\n\n"
                f"Your fleet, {company['company_name']}, has been approved by our team. You can now add trucks "
                "and invite drivers from your fleet dashboard.\n\n"
                "— The PickkerMarket Team",
            )
    messages.success(request, 'Fleet approved — the company can now add trucks and drivers.')
    return redirect('accounts:admin_fleet_companies')


@role_required('admin')
def admin_api_subscriptions(request):
    from market.api_auth import list_all_subscriptions

    return render(request, 'accounts/admin_api_subscriptions.html', {
        'subscriptions': list_all_subscriptions(),
    })


@role_required('admin')
@require_POST
def admin_activate_api_subscription(request, subscription_id):
    from market.api_auth import activate_subscription

    activate_subscription(subscription_id, request.session['picker_user']['id'], plan=request.POST.get('plan') or None)
    messages.success(request, 'Subscription activated — the API key is now live.')
    return redirect('accounts:admin_api_subscriptions')


@role_required('admin')
@require_POST
def admin_suspend_api_subscription(request, subscription_id):
    from market.api_auth import suspend_subscription

    suspend_subscription(subscription_id)
    messages.success(request, 'Subscription suspended — the API key is now blocked.')
    return redirect('accounts:admin_api_subscriptions')


@role_required('admin')
@require_POST
def admin_edit_truck_type(request, truck_type_id):
    from market.truck_types import update_truck_type
    icon_url = _save_truck_icon(request.FILES['icon_image']) if request.FILES.get('icon_image') else None
    per_km_rate = request.POST.get('per_km_rate', '').strip()
    update_truck_type(
        truck_type_id,
        name=request.POST.get('name', ''),
        capacity_label=request.POST.get('capacity_label', ''),
        max_weight_kg=request.POST.get('max_weight_kg', ''),
        base_rate=request.POST.get('base_rate', ''),
        icon=request.POST.get('icon', ''),
        icon_url=icon_url,
        segment=request.POST.get('segment', 'b2b'),
        per_km_rate=per_km_rate or None,
    )
    messages.success(request, 'Truck type updated.')
    return redirect('accounts:admin_truck_types')


@role_required('admin')
@require_POST
def admin_toggle_truck_type(request, truck_type_id):
    from market.truck_types import toggle_truck_type_active
    now_active = toggle_truck_type_active(truck_type_id)
    messages.success(request, 'Truck type activated.' if now_active else 'Truck type deactivated.')
    return redirect('accounts:admin_truck_types')


def _parse_aliases(raw):
    return [a.strip() for a in (raw or '').split(',') if a.strip()]


def _parse_coord(raw):
    raw = (raw or '').strip()
    return float(raw) if raw else None


@role_required('admin')
def admin_known_areas(request):
    import csv
    import io

    from market.known_areas import bulk_import_areas, create_area, get_all_areas

    if request.method == 'POST':
        csv_file = request.FILES.get('csv_file')
        if csv_file:
            try:
                decoded = csv_file.read().decode('utf-8-sig')
                reader = csv.DictReader(io.StringIO(decoded))
                rows = [
                    {'name': row.get('name'), 'lat': row.get('lat'), 'lng': row.get('lng'), 'aliases': row.get('aliases')}
                    for row in reader
                ]
            except (UnicodeDecodeError, csv.Error):
                messages.error(request, "Couldn't read that file — make sure it's a plain CSV.")
                return redirect('accounts:admin_known_areas')
            created, updated, skipped = bulk_import_areas(rows)
            if created or updated:
                parts = []
                if created:
                    parts.append(f'{created} added')
                if updated:
                    parts.append(f'{updated} updated')
                if skipped:
                    parts.append(f'{skipped} skipped (missing name or invalid coordinates)')
                messages.success(request, 'CSV import: ' + ', '.join(parts) + '.')
            else:
                messages.error(request, "No valid rows found — check the CSV has 'name', 'lat', 'lng' columns.")
            return redirect('accounts:admin_known_areas')

        name = request.POST.get('name', '').strip()
        if name:
            from market.regions import get_or_create_region
            region = get_or_create_region(request.POST.get('region_name', ''))
            create_area(
                name=name,
                lat=_parse_coord(request.POST.get('lat')),
                lng=_parse_coord(request.POST.get('lng')),
                aliases=_parse_aliases(request.POST.get('aliases')),
                region_id=region['id'] if region else None,
            )
            messages.success(request, f'{name.title()} added.')
        else:
            messages.error(request, 'A name is required.')
        return redirect('accounts:admin_known_areas')

    from market.regions import get_regions_by_id
    areas = get_all_areas()
    regions_by_id = get_regions_by_id([a.get('region_id') for a in areas])
    for a in areas:
        a['region_name'] = regions_by_id.get(a.get('region_id'), {}).get('name_en')
    unresolved_count = sum(1 for a in areas if a['lat'] is None)
    return render(request, 'accounts/admin_known_areas.html', {
        'areas': areas,
        'unresolved_count': unresolved_count,
    })


@role_required('admin')
@require_POST
def admin_edit_known_area(request, area_id):
    from market.known_areas import update_area
    from market.regions import get_or_create_region
    region = get_or_create_region(request.POST.get('region_name', ''))
    update_area(
        area_id,
        name=request.POST.get('name', ''),
        lat=_parse_coord(request.POST.get('lat')),
        lng=_parse_coord(request.POST.get('lng')),
        aliases=_parse_aliases(request.POST.get('aliases')),
        region_id=region['id'] if region else None,
    )
    messages.success(request, 'Area updated.')
    return redirect('accounts:admin_known_areas')


@role_required('admin')
@require_POST
def admin_delete_known_area(request, area_id):
    from market.known_areas import delete_area
    delete_area(area_id)
    messages.success(request, 'Area removed.')
    return redirect('accounts:admin_known_areas')


@role_required('admin')
def admin_regions(request):
    from market.regions import get_all_regions, get_or_create_region

    if request.method == 'POST':
        region_name = request.POST.get('region_name', '').strip()
        if region_name:
            get_or_create_region(region_name)
            messages.success(request, f'{region_name} added.')
        else:
            messages.error(request, 'Search for and pick a region first.')
        return redirect('accounts:admin_regions')

    return render(request, 'accounts/admin_regions.html', {
        'regions': get_all_regions(),
    })


@role_required('admin')
@require_POST
def admin_edit_region(request, region_id):
    from market.regions import update_region
    update_region(region_id, request.POST.get('name_en', ''), request.POST.get('name_sw', ''))
    messages.success(request, 'Region updated.')
    return redirect('accounts:admin_regions')


@role_required('admin')
@require_POST
def admin_delete_region(request, region_id):
    from market.regions import delete_region
    delete_region(region_id)
    messages.success(request, 'Region removed.')
    return redirect('accounts:admin_regions')


@role_required('admin')
def admin_markets(request):
    import csv
    import io

    from market.categories import get_all_categories
    from market.markets import bulk_import_markets, create_market, get_all_markets, get_market_categories

    if request.method == 'POST':
        csv_file = request.FILES.get('csv_file')
        if csv_file:
            try:
                decoded = csv_file.read().decode('utf-8-sig')
                rows = list(csv.DictReader(io.StringIO(decoded)))
            except (UnicodeDecodeError, csv.Error):
                messages.error(request, "Couldn't read that file — make sure it's a plain CSV.")
                return redirect('accounts:admin_markets')
            created, updated, skipped = bulk_import_markets(rows)
            if created or updated:
                parts = []
                if created:
                    parts.append(f'{created} added')
                if updated:
                    parts.append(f'{updated} updated')
                if skipped:
                    parts.append(f'{skipped} skipped (missing name or invalid coordinates)')
                messages.success(request, 'CSV import: ' + ', '.join(parts) + '.')
            else:
                messages.error(request, "No valid rows found — check the CSV has market name/lat/lng columns.")
            return redirect('accounts:admin_markets')

        name = request.POST.get('name', '').strip()
        lat = _parse_coord(request.POST.get('lat'))
        lng = _parse_coord(request.POST.get('lng'))
        if name and lat is not None and lng is not None:
            from market.regions import get_or_create_region
            logo_url = _save_market_logo(request.FILES['logo']) if request.FILES.get('logo') else None
            region_row = get_or_create_region(request.POST.get('region_name', ''))
            create_market(
                request.POST.get('region', ''), name, request.POST.get('notes', ''), lat, lng,
                contact_phone=request.POST.get('contact_phone', ''),
                contact_person=request.POST.get('contact_person', ''),
                landmark_note=request.POST.get('landmark_note', ''),
                categories=request.POST.getlist('categories'),
                logo_url=logo_url,
                region_id=region_row['id'] if region_row else None,
            )
            messages.success(request, f'{name} added.')
        else:
            messages.error(request, 'A name and map location are required.')
        return redirect('accounts:admin_markets')

    from market.regions import get_regions_by_id
    markets = get_all_markets()
    regions_by_id = get_regions_by_id(m.get('region_id') for m in markets)
    for market in markets:
        market['category_list'] = get_market_categories(market)
        region_row = regions_by_id.get(market.get('region_id'))
        market['region_display_name'] = region_row['name_en'] if region_row else ''
    return render(request, 'accounts/admin_markets.html', {
        'markets': markets,
        'categories': get_all_categories(),
        'center': DAR_ES_SALAAM_CENTER,
    })


@role_required('admin')
@require_POST
def admin_edit_market(request, market_id):
    from market.markets import update_market
    from market.regions import get_or_create_region
    logo_url = _save_market_logo(request.FILES['logo']) if request.FILES.get('logo') else None
    region_row = get_or_create_region(request.POST.get('region_name', ''))
    update_market(
        market_id,
        region=request.POST.get('region', ''),
        region_id=region_row['id'] if region_row else None,
        name=request.POST.get('name', ''),
        notes=request.POST.get('notes', ''),
        lat=_parse_coord(request.POST.get('lat')),
        lng=_parse_coord(request.POST.get('lng')),
        contact_phone=request.POST.get('contact_phone', ''),
        contact_person=request.POST.get('contact_person', ''),
        landmark_note=request.POST.get('landmark_note', ''),
        categories=request.POST.getlist('categories'),
        logo_url=logo_url,
    )
    messages.success(request, 'Market updated.')
    return redirect('accounts:admin_markets')


@role_required('admin')
@require_POST
def admin_toggle_market(request, market_id):
    from market.markets import toggle_market_active
    now_active = toggle_market_active(market_id)
    messages.success(request, 'Market shown to pickers.' if now_active else 'Market hidden from pickers.')
    return redirect('accounts:admin_markets')


@role_required('admin')
@require_POST
def admin_delete_market(request, market_id):
    from market.markets import delete_market
    delete_market(market_id)
    messages.success(request, 'Market removed.')
    return redirect('accounts:admin_markets')


@role_required('admin')
def admin_vendors(request, vendor_type):
    """One shared CRUD page for both new orderable source types (Warehouse,
    Industry — see market/vendors.py) rather than two near-identical admin
    views/templates, since they only differ by a couple of extra fields.
    Market keeps its own dedicated admin_markets page above, unchanged."""
    from market.vendors import VENDOR_TYPES, create_vendor, get_all_vendors
    if vendor_type not in VENDOR_TYPES:
        return redirect('accounts:admin_portal')

    from market.categories import get_all_categories
    from market.fleet import get_all_companies
    from market.vendors import get_vendor_categories

    if request.method == 'POST':
        name = request.POST.get('name', '').strip()
        lat = _parse_coord(request.POST.get('lat'))
        lng = _parse_coord(request.POST.get('lng'))
        if name and lat is not None and lng is not None:
            from market.regions import get_or_create_region
            logo_url = _save_uploaded_image(request.FILES['logo'], f'{vendor_type}s') if request.FILES.get('logo') else None
            company_id = request.POST.get('company_id') or None
            region_row = get_or_create_region(request.POST.get('region_name', ''))
            create_vendor(
                vendor_type, request.POST.get('region', ''), name, request.POST.get('notes', ''), lat, lng,
                contact_phone=request.POST.get('contact_phone', ''),
                contact_person=request.POST.get('contact_person', ''),
                landmark_note=request.POST.get('landmark_note', ''),
                categories=request.POST.getlist('categories'),
                logo_url=logo_url,
                company_id=int(company_id) if company_id else None,
                storage_capacity_note=request.POST.get('storage_capacity_note', ''),
                license_number=request.POST.get('license_number', ''),
                bulk_only=request.POST.get('bulk_only') == 'on',
                min_order_kg=_parse_coord(request.POST.get('min_order_kg')),
                close_radius_min_order_kg=_parse_coord(request.POST.get('close_radius_min_order_kg')),
                region_id=region_row['id'] if region_row else None,
                free_delivery_min_kg=_parse_coord(request.POST.get('free_delivery_min_kg')),
                free_picking_min_kg=_parse_coord(request.POST.get('free_picking_min_kg')),
            )
            messages.success(request, f'{name} added.')
        else:
            messages.error(request, 'A name and map location are required.')
        return redirect('accounts:admin_vendors', vendor_type=vendor_type)

    from market.regions import get_regions_by_id
    from market.stock import get_stock_for_vendor
    client = get_client()
    vendors = get_all_vendors(vendor_type)
    regions_by_id = get_regions_by_id(v.get('region_id') for v in vendors)
    for v in vendors:
        v['category_list'] = get_vendor_categories(v)
        v['stock'] = get_stock_for_vendor(vendor_type, v['id'])
        v['tied_products'] = client.table('pickker_products').select('id, name').eq('vendor_type', vendor_type).eq('vendor_id', v['id']).execute().data
        region_row = regions_by_id.get(v.get('region_id'))
        v['region_display_name'] = region_row['name_en'] if region_row else ''
    return render(request, 'accounts/admin_vendors.html', {
        'vendor_type': vendor_type,
        'vendors': vendors,
        'categories': get_all_categories(),
        'companies': get_all_companies(),
        'center': DAR_ES_SALAAM_CENTER,
    })


@role_required('admin')
@require_POST
def admin_receive_vendor_stock(request, vendor_type, vendor_id):
    from decimal import InvalidOperation

    from market.stock import receive_stock
    from market.vendors import VENDOR_TYPES
    if vendor_type not in VENDOR_TYPES:
        return redirect('accounts:admin_portal')

    product_id = request.POST.get('product_id')
    quantity = request.POST.get('quantity', '').strip()
    try:
        product_id = int(product_id)
        quantity = int(quantity)
        if quantity <= 0:
            raise ValueError
    except (TypeError, ValueError, InvalidOperation):
        messages.error(request, 'Choose a product and a positive quantity.')
        return redirect('accounts:admin_vendors', vendor_type=vendor_type)

    admin_user = request.session['picker_user']
    receive_stock(vendor_type, vendor_id, product_id, quantity, admin_user['id'])
    messages.success(request, f'{quantity} received into stock.')
    return redirect('accounts:admin_vendors', vendor_type=vendor_type)


@role_required('admin')
@require_POST
def admin_edit_vendor(request, vendor_type, vendor_id):
    from market.regions import get_or_create_region
    from market.vendors import VENDOR_TYPES, update_vendor
    if vendor_type not in VENDOR_TYPES:
        return redirect('accounts:admin_portal')
    logo_url = _save_uploaded_image(request.FILES['logo'], f'{vendor_type}s') if request.FILES.get('logo') else None
    company_id = request.POST.get('company_id') or None
    region_row = get_or_create_region(request.POST.get('region_name', ''))
    update_vendor(
        vendor_type, vendor_id,
        region=request.POST.get('region', ''),
        region_id=region_row['id'] if region_row else None,
        name=request.POST.get('name', ''),
        notes=request.POST.get('notes', ''),
        lat=_parse_coord(request.POST.get('lat')),
        lng=_parse_coord(request.POST.get('lng')),
        contact_phone=request.POST.get('contact_phone', ''),
        contact_person=request.POST.get('contact_person', ''),
        landmark_note=request.POST.get('landmark_note', ''),
        categories=request.POST.getlist('categories'),
        logo_url=logo_url,
        company_id=int(company_id) if company_id else None,
        storage_capacity_note=request.POST.get('storage_capacity_note') if vendor_type == 'warehouse' else None,
        license_number=request.POST.get('license_number') if vendor_type == 'industry' else None,
        bulk_only=(request.POST.get('bulk_only') == 'on') if vendor_type == 'industry' else None,
        min_order_kg=_parse_coord(request.POST.get('min_order_kg')),
        close_radius_min_order_kg=_parse_coord(request.POST.get('close_radius_min_order_kg')),
        free_delivery_min_kg=_parse_coord(request.POST.get('free_delivery_min_kg')),
        free_picking_min_kg=_parse_coord(request.POST.get('free_picking_min_kg')),
    )
    messages.success(request, f'{vendor_type.title()} updated.')
    return redirect('accounts:admin_vendors', vendor_type=vendor_type)


@role_required('admin')
@require_POST
def admin_toggle_vendor(request, vendor_type, vendor_id):
    from market.vendors import VENDOR_TYPES, toggle_vendor_active
    if vendor_type not in VENDOR_TYPES:
        return redirect('accounts:admin_portal')
    now_active = toggle_vendor_active(vendor_type, vendor_id)
    messages.success(request, f'{vendor_type.title()} shown.' if now_active else f'{vendor_type.title()} hidden.')
    return redirect('accounts:admin_vendors', vendor_type=vendor_type)


@role_required('admin')
@require_POST
def admin_delete_vendor(request, vendor_type, vendor_id):
    from market.vendors import VENDOR_TYPES, delete_vendor
    if vendor_type not in VENDOR_TYPES:
        return redirect('accounts:admin_portal')
    delete_vendor(vendor_type, vendor_id)
    messages.success(request, f'{vendor_type.title()} removed.')
    return redirect('accounts:admin_vendors', vendor_type=vendor_type)


@role_required('admin')
def admin_payouts(request):
    return render(request, 'accounts/admin_payouts.html', {'pickers': get_all_pickers_earnings_overview()})


@role_required('admin')
@require_POST
def admin_pay_picker(request, picker_id):
    admin_user = request.session['picker_user']
    pay_out_picker(picker_id, admin_user['id'])
    messages.success(request, 'Payout recorded — all pending earnings for this picker are now marked paid.')
    return redirect('accounts:admin_payouts')


@role_required('admin')
def admin_financial_dashboard(request):
    client = get_client()
    orders = client.table('pickker_orders').select('*').execute().data
    delivered = [o for o in orders if o['status'] == 'delivered']

    total_revenue = sum((Decimal(str(o['items_subtotal'])) for o in delivered), Decimal('0'))
    total_delivery_fees = sum((Decimal(str(o['delivery_fee'])) for o in delivered), Decimal('0'))
    total_picking_fees = sum((Decimal(str(o.get('picking_fee') or 0)) for o in delivered), Decimal('0'))

    pickers_overview = get_all_pickers_earnings_overview()
    total_pending_payouts = sum((p['pending_amount'] for p in pickers_overview), Decimal('0'))
    total_paid_payouts = sum((p['paid_amount'] for p in pickers_overview), Decimal('0'))

    pending_payment_orders = [o for o in orders if o['status'] == 'pending_payment']

    from market.site_settings import get_platform_commission_pct

    earnings_rows = client.table('pickker_picker_earnings').select('commission_amount').execute().data
    total_commission_earned = sum(
        (Decimal(str(r['commission_amount'])) for r in earnings_rows if r.get('commission_amount')), Decimal('0'),
    )

    return render(request, 'accounts/admin_financial_dashboard.html', {
        'delivered_count': len(delivered),
        'total_revenue': total_revenue,
        'total_delivery_fees': total_delivery_fees,
        'total_picking_fees': total_picking_fees,
        'total_pending_payouts': total_pending_payouts,
        'total_paid_payouts': total_paid_payouts,
        'total_commission_earned': total_commission_earned,
        'platform_commission_pct': get_platform_commission_pct(),
        'pending_payment_count': len(pending_payment_orders),
        'pending_payment_total': sum((Decimal(str(o['total_amount'])) for o in pending_payment_orders), Decimal('0')),
        'pickers_overview': pickers_overview,
        'platform_rating': get_platform_rating_summary(),
    })


@role_required('admin')
def admin_analytics(request):
    from market.admin_analytics import get_admin_analytics

    try:
        days = int(request.GET.get('days', 365))
    except ValueError:
        days = 365
    return render(request, 'accounts/admin_analytics.html', {
        'data': get_admin_analytics(days=days),
        'days': days,
    })


def accept_policies(request):
    from django.utils.http import url_has_allowed_host_and_scheme

    from market.legal_policies import get_outdated_policies, localize_policy, record_acceptance

    picker_user = request.session.get('picker_user')
    if not picker_user:
        return redirect('accounts:login')

    next_url = request.GET.get('next') or request.POST.get('next') or ''
    if not url_has_allowed_host_and_scheme(next_url, allowed_hosts={request.get_host()}):
        next_url = ''

    outdated = get_outdated_policies(picker_user['id'])

    if request.method == 'POST':
        if request.POST.get('agree'):
            for policy in outdated:
                record_acceptance(picker_user['id'], policy['policy_type'], policy['version'])
            return redirect(next_url or 'accounts:post_login_redirect')
        messages.error(request, 'Please confirm you have read and agree to continue.')

    lang = request.session.get('language', 'en')
    return render(request, 'accounts/accept_policies.html', {
        'outdated_policies': [localize_policy(p, lang) for p in outdated],
        'next': next_url,
    })


@role_required('admin')
def admin_legal_policies(request):
    from market.legal_policies import get_all_policies

    return render(request, 'accounts/admin_legal_policies.html', {
        'policies': get_all_policies(),
    })


@role_required('admin')
@require_POST
def admin_edit_legal_policy(request, policy_type):
    from market.legal_policies import POLICY_TYPES, update_policy

    if policy_type not in POLICY_TYPES:
        raise Http404
    update_policy(
        policy_type,
        title=request.POST.get('title', ''),
        content=request.POST.get('content', ''),
        title_sw=request.POST.get('title_sw', ''),
        content_sw=request.POST.get('content_sw', ''),
    )
    messages.success(request, 'Policy updated — users who already accepted the previous version will be asked to review and accept the change.')
    return redirect('accounts:admin_legal_policies')


@role_required('picker')
def picker_order_detail(request, order_id):
    from market.measure_units import get_unit_label_map, per_each_unit_display, quantity_unit_display
    from market.product_i18n import get_product_badges, get_product_display_names
    from market.shop_payments import get_required_markets_with_status

    picker_user = request.session['picker_user']
    client = get_client()
    resp = client.table('pickker_orders').select('*').eq('id', order_id).execute()
    order = resp.data[0] if resp.data else None
    if not order or order.get('picker_id') != picker_user['id']:
        raise Http404
    mark_messages_seen(picker_user['id'], order_id)

    items_resp = client.table('pickker_order_items').select('*').eq('order_id', order_id).execute()
    customer_resp = client.table('pickker_users').select('first_name, last_name, phone_number, email').eq('id', order['customer_id']).execute()
    customer = customer_resp.data[0] if customer_resp.data else None
    items_subtotal = sum(Decimal(str(item['line_total'])) for item in items_resp.data)
    order['status_label'] = get_status_label(get_t(request), order['status'])

    # The order/measure/unit rows are an immutable snapshot from when the
    # order was placed, but the *label* shown for them should still follow
    # whichever language the picker has the site set to right now — same as
    # every other unit display in the app — rather than staying frozen in
    # whatever language the customer had active at checkout.
    lang = request.session.get('language', 'en')
    unit_label_map = get_unit_label_map(lang)
    product_display_names = get_product_display_names((item.get('product_id') for item in items_resp.data), lang)
    product_badges = get_product_badges(item.get('product_id') for item in items_resp.data)
    for item in items_resp.data:
        key = item.get('measure_type') or item.get('unit')
        item['unit_display'] = unit_label_map.get(key, item.get('measure_label') or item.get('unit'))
        item['per_unit_text'] = per_each_unit_display(key, lang)
        item['qty_unit_display'] = quantity_unit_display(item['quantity'], key, lang)
        item['product_display_name'] = product_display_names.get(item.get('product_id'), item.get('product_name'))
        badge = product_badges.get(item.get('product_id'), {})
        item['market_name'] = badge.get('market_name')
        item['market_logo_url'] = badge.get('market_logo_url')
        item['quality_grade'] = badge.get('quality_grade')

    from market.shop_payments import is_shop_payment_complete
    required_markets = get_required_markets_with_status(order_id)
    for m in required_markets:
        m['can_mark_picked_up'] = order['status'] == 'picking' and not m['picked_up'] and (not m['requires_payment'] or m['payment_complete'])

    # A warehouse/industry-tied order routed through the ordinary B2C
    # picker flow (see market/views.py::_checkout_picker_b2c's vendor
    # branch, Phase 1 close-radius routing) — previously this info only
    # ever reached admin views, since every vendor-tied order used to go
    # through the fleet/company path, where the assigned driver already
    # knew their own company's warehouse.
    from market.vendors import get_order_vendor_tie
    vendor_tie = get_order_vendor_tie(order_id)
    if vendor_tie:
        vendor_tie['can_mark_picked_up'] = order['status'] == 'picking' and not vendor_tie['picked_up'] and (not vendor_tie['requires_payment'] or vendor_tie['payment_complete'])

    return render(request, 'accounts/picker_order_detail.html', {
        'order': order,
        'items': items_resp.data,
        'items_subtotal': items_subtotal,
        'customer': customer,
        'required_markets': required_markets,
        'shop_payment_complete': is_shop_payment_complete(order_id),
        'vendor_tie': vendor_tie,
        'messages_thread': get_messages(order_id),
        'my_user_id': picker_user['id'],
    })


@role_required('picker')
@require_POST
def picker_mark_market_picked_up(request, order_id, market_id):
    """Picker confirms they've physically collected from one of this order's
    required markets — a multi-stop pickup checks off one stop at a time,
    so the live route (see market/shop_payments.py::get_remaining_pickup_waypoints)
    only routes through whatever's still ahead."""
    from market.shop_payments import mark_market_picked_up

    picker_user = request.session['picker_user']
    client = get_client()
    order_resp = client.table('pickker_orders').select('id, picker_id').eq('id', order_id).execute()
    order = order_resp.data[0] if order_resp.data else None
    if not order or order.get('picker_id') != picker_user['id']:
        raise Http404
    mark_market_picked_up(order_id, market_id)
    messages.success(request, get_t(request)['msg_market_picked_up'])
    return redirect('accounts:picker_order_detail', order_id=order_id)


@role_required('picker')
@require_POST
def picker_mark_vendor_picked_up(request, order_id):
    """Picker confirms they've physically collected an order from its tied
    warehouse/industry — the vendor-fleet equivalent of
    picker_mark_market_picked_up, for orders routed through the ordinary
    B2C picker flow (see market/views.py::_checkout_picker_b2c's vendor
    branch). Only one vendor tie per order, so no id in the URL."""
    from market.vendors import get_order_vendor_tie, mark_vendor_picked_up

    picker_user = request.session['picker_user']
    client = get_client()
    order_resp = client.table('pickker_orders').select('id, picker_id').eq('id', order_id).execute()
    order = order_resp.data[0] if order_resp.data else None
    if not order or order.get('picker_id') != picker_user['id']:
        raise Http404
    if not get_order_vendor_tie(order_id):
        raise Http404
    mark_vendor_picked_up(order_id)
    messages.success(request, get_t(request)['msg_market_picked_up'])
    return redirect('accounts:picker_order_detail', order_id=order_id)


@role_required('picker')
@require_POST
def picker_send_message(request, order_id):
    picker_user = request.session['picker_user']
    client = get_client()
    resp = client.table('pickker_orders').select('*').eq('id', order_id).execute()
    order = resp.data[0] if resp.data else None
    if order and order['picker_id'] == picker_user['id']:
        message = send_message(order_id, picker_user['id'], request.POST.get('message', ''))
        if message:
            mark_messages_seen(picker_user['id'], order_id)
            notify_new_message(order, 'picker', message['message'])
            push_new_message(order, 'picker', message['message'])
    if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
        return JsonResponse({'messages': get_messages(order_id)})
    return redirect('accounts:picker_order_detail', order_id=order_id)


@role_required('picker')
@require_POST
def picker_fleet_message_send(request):
    from market.fleet_messaging import mark_fleet_messages_seen, send_fleet_message

    driver_id = request.session['picker_user']['id']
    profile_resp = get_client().table('pickker_picker_profiles').select('company_id').eq('user_id', driver_id).execute()
    company_id = profile_resp.data[0].get('company_id') if profile_resp.data else None
    if not company_id:
        raise Http404
    message = send_fleet_message(company_id, driver_id, driver_id, request.POST.get('message', ''))
    if message:
        mark_fleet_messages_seen(driver_id, company_id, driver_id)
    if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
        from market.fleet_messaging import get_fleet_messages
        return JsonResponse({'messages': get_fleet_messages(company_id, driver_id)})
    return redirect('accounts:picker_home')


@role_required('picker')
def picker_fleet_message_poll(request):
    from market.fleet_messaging import get_fleet_messages, mark_fleet_messages_seen

    driver_id = request.session['picker_user']['id']
    profile_resp = get_client().table('pickker_picker_profiles').select('company_id').eq('user_id', driver_id).execute()
    company_id = profile_resp.data[0].get('company_id') if profile_resp.data else None
    if not company_id:
        raise Http404
    mark_fleet_messages_seen(driver_id, company_id, driver_id)
    return JsonResponse({'messages': get_fleet_messages(company_id, driver_id)})


@role_required('picker')
def picker_order_messages_poll(request, order_id):
    picker_user = request.session['picker_user']
    client = get_client()
    resp = client.table('pickker_orders').select('picker_id, status').eq('id', order_id).execute()
    if not resp.data or resp.data[0]['picker_id'] != picker_user['id']:
        raise Http404
    mark_messages_seen(picker_user['id'], order_id)
    return JsonResponse({
        'messages': get_messages(order_id),
        'status': resp.data[0]['status'],
        'status_label': get_status_label(get_t(request), resp.data[0]['status']),
    })


@role_required('picker')
@ensure_csrf_cookie
def picker_live_navigation(request, order_id):
    from market.geo import get_multi_stop_route
    from market.live_tracking import get_live_location
    from market.shop_payments import get_remaining_pickup_waypoints, get_required_markets_with_status

    picker_user = request.session['picker_user']
    client = get_client()
    resp = client.table('pickker_orders').select('*').eq('id', order_id).execute()
    order = resp.data[0] if resp.data else None
    if not order or order.get('picker_id') != picker_user['id']:
        raise Http404

    from market.truck_types import get_picker_vehicle_display
    vehicle_display = get_picker_vehicle_display(picker_user['id'])
    picker_vehicle_type = vehicle_display['vehicle_type']
    picker_truck_icon = vehicle_display['truck_icon']
    picker_truck_icon_url = vehicle_display['truck_icon_url']

    live = get_live_location(order_id)
    start_lat = live['lat'] if live else order['picker_location_lat']
    start_lng = live['lng'] if live else order['picker_location_lng']
    remaining_waypoints = get_remaining_pickup_waypoints(order_id)
    route = get_multi_stop_route(float(start_lat), float(start_lng), remaining_waypoints, float(order['delivery_lat']), float(order['delivery_lng']))
    remaining_markets = [m for m in get_required_markets_with_status(order_id) if not m['picked_up']]

    return render(request, 'accounts/picker_live_navigation.html', {
        'order': order,
        'picker_vehicle_type': picker_vehicle_type,
        'picker_truck_icon': picker_truck_icon,
        'picker_truck_icon_url': picker_truck_icon_url,
        'start_lat': start_lat,
        'start_lng': start_lng,
        'remaining_markets': remaining_markets,
        'route_distance_km': route['distance_km'] if route else order.get('distance_km'),
        'route_duration_min': route['duration_min'] if route else None,
        'route_geometry_json': json.dumps(route['geometry']) if route and route.get('geometry') else None,
    })


MAPPABLE_ORDER_STATUSES = ['confirmed', 'picking', 'in_transit', 'delivered']


@role_required('admin')
def admin_delivered_map(request):
    """Every order with a captured delivery location plotted on one map —
    delivery_lat/lng is set automatically at checkout, so this just surfaces
    it in aggregate (green = delivered, blue = not yet delivered), with
    status/date filters and a live-route link per order (admin_track_order).
    Customers are identified only by "Customer #<id>", never by name."""
    client = get_client()

    status_filter = [s for s in request.GET.getlist('status') if s in MAPPABLE_ORDER_STATUSES]
    if not status_filter:
        status_filter = list(MAPPABLE_ORDER_STATUSES)
    date_from = request.GET.get('date_from', '').strip()
    date_to = request.GET.get('date_to', '').strip()

    query = (
        client.table('pickker_orders')
        .select('id, customer_id, delivery_address, delivery_lat, delivery_lng, delivered_at, delivery_date, status')
        .in_('status', status_filter)
        .not_.is_('delivery_lat', 'null')
    )
    if date_from:
        query = query.gte('delivery_date', date_from)
    if date_to:
        query = query.lte('delivery_date', date_to)
    orders = query.order('delivery_date', desc=True).execute().data

    all_counts = {s: 0 for s in MAPPABLE_ORDER_STATUSES}
    all_status_resp = client.table('pickker_orders').select('status').in_('status', MAPPABLE_ORDER_STATUSES).not_.is_('delivery_lat', 'null').execute()
    for row in all_status_resp.data:
        all_counts[row['status']] = all_counts.get(row['status'], 0) + 1

    t = get_t(request)
    points = []
    for o in orders:
        points.append({
            'order_id': o['id'],
            'lat': float(o['delivery_lat']),
            'lng': float(o['delivery_lng']),
            'address': o.get('delivery_address') or '',
            'customer_label': f"Customer #{o['customer_id']}",
            'status': o['status'],
            'status_label': get_status_label(t, o['status']),
            'is_delivered': o['status'] == 'delivered',
            'date': o.get('delivered_at') or o.get('delivery_date') or '',
            'track_url': reverse('accounts:admin_track_order', args=[o['id']]),
        })

    return render(request, 'accounts/admin_delivered_map.html', {
        'points_json': json.dumps(points, default=str),
        'point_count': len(points),
        'status_options': [
            {'key': s, 'label': get_status_label(t, s), 'count': all_counts.get(s, 0), 'checked': s in status_filter}
            for s in MAPPABLE_ORDER_STATUSES
        ],
        'date_from': date_from,
        'date_to': date_to,
    })


@role_required('admin')
def admin_track_order(request, order_id):
    """The same live route/tracking view a picker sees for their own
    delivery (picker_live_navigation), but read-only and open to any order
    — so admin can check exactly what the picker sees, watch their live
    position, and see the moment an order reaches 'delivered'."""
    from market.geo import get_multi_stop_route
    from market.live_tracking import get_live_location
    from market.shop_payments import get_remaining_pickup_waypoints, get_required_markets_with_status

    client = get_client()
    resp = client.table('pickker_orders').select('*').eq('id', order_id).execute()
    order = resp.data[0] if resp.data else None
    if not order or not order.get('picker_id') or order.get('delivery_lat') is None:
        raise Http404

    picker_resp = client.table('pickker_users').select('first_name, last_name').eq('id', order['picker_id']).execute()
    picker_name = None
    if picker_resp.data:
        pu = picker_resp.data[0]
        picker_name = f"{pu['first_name']} {pu['last_name']}".strip()

    from market.truck_types import get_picker_vehicle_display
    vehicle_display = get_picker_vehicle_display(order['picker_id'])
    picker_vehicle_type = vehicle_display['vehicle_type']
    picker_truck_icon = vehicle_display['truck_icon']
    picker_truck_icon_url = vehicle_display['truck_icon_url']

    picker_profile_resp = client.table('pickker_picker_profiles').select('photo_url').eq('user_id', order['picker_id']).execute()
    picker_photo_url = picker_profile_resp.data[0].get('photo_url') if picker_profile_resp.data else None

    live = get_live_location(order_id)
    start_lat = live['lat'] if live else order.get('picker_location_lat')
    start_lng = live['lng'] if live else order.get('picker_location_lng')
    route = None
    if start_lat is not None:
        remaining_waypoints = get_remaining_pickup_waypoints(order_id)
        route = get_multi_stop_route(float(start_lat), float(start_lng), remaining_waypoints, float(order['delivery_lat']), float(order['delivery_lng']))

    # Time + place accountability for the picking phase: how long they spent
    # picking, and whether the position they set off from is anywhere near
    # one of the platform's registered markets — a real signal (not proof)
    # that they may have sourced the order somewhere else instead, worth a
    # manual look rather than blocking anything automatically.
    from market.markets import get_nearest_market

    picking_duration_min = None
    if order.get('picking_started_at') and order.get('delivery_started_at'):
        started = datetime.fromisoformat(order['picking_started_at'].replace('Z', '+00:00'))
        left = datetime.fromisoformat(order['delivery_started_at'].replace('Z', '+00:00'))
        picking_duration_min = round((left - started).total_seconds() / 60, 1)

    nearest_market = get_nearest_market(float(start_lat), float(start_lng)) if start_lat is not None else None
    OFF_MARKET_THRESHOLD_KM = 2.0
    far_from_market = bool(nearest_market and nearest_market['distance_km'] > OFF_MARKET_THRESHOLD_KM)
    remaining_markets = [m for m in get_required_markets_with_status(order_id) if not m['picked_up']]

    return render(request, 'accounts/admin_track_order.html', {
        'order': order,
        'picker_name': picker_name,
        'picker_vehicle_type': picker_vehicle_type,
        'picker_truck_icon': picker_truck_icon,
        'picker_truck_icon_url': picker_truck_icon_url,
        'picker_photo_url': picker_photo_url,
        'start_lat': start_lat,
        'start_lng': start_lng,
        'remaining_markets': remaining_markets,
        'route_distance_km': route['distance_km'] if route else order.get('distance_km'),
        'route_duration_min': route['duration_min'] if route else None,
        'route_geometry_json': json.dumps(route['geometry']) if route and route.get('geometry') else None,
        'picking_duration_min': picking_duration_min,
        'nearest_market': nearest_market,
        'far_from_market': far_from_market,
    })


@role_required('admin')
def admin_track_order_data(request, order_id):
    """Polled from the admin tracking map — returns the picker's current
    live position, a freshly recomputed real road route to the destination
    (not a straight line — matches what the picker's own live-navigation
    page shows), and the order's status, so the page can show the moment it
    flips to 'delivered' without a manual refresh."""
    from market.geo import get_multi_stop_route
    from market.live_tracking import get_live_location
    from market.shop_payments import get_remaining_pickup_waypoints

    client = get_client()
    resp = client.table('pickker_orders').select('status, delivery_lat, delivery_lng').eq('id', order_id).execute()
    order = resp.data[0] if resp.data else None
    if not order:
        raise Http404

    location = get_live_location(order_id)
    route = None
    if location and order.get('delivery_lat') is not None:
        remaining_waypoints = get_remaining_pickup_waypoints(order_id)
        route = get_multi_stop_route(location['lat'], location['lng'], remaining_waypoints, float(order['delivery_lat']), float(order['delivery_lng']))

    return JsonResponse({
        'location': location,
        'route': {
            'distance_km': route['distance_km'],
            'duration_min': route['duration_min'],
            'geometry': route['geometry'],
        } if route else None,
        'status': order['status'],
        'status_label': get_status_label(get_t(request), order['status']),
        'delivered': order['status'] == 'delivered',
    })


@role_required('picker')
@require_POST
def picker_update_location(request, order_id):
    """Called every few seconds from the live navigation page as the
    picker's browser reports a new GPS fix. Saves the position and returns a
    freshly recomputed route to the destination, so the suggested route
    keeps up with where the picker actually is instead of staying pinned to
    their position when the order was first placed."""
    from market.geo import get_multi_stop_route
    from market.live_tracking import update_picker_location
    from market.shop_payments import get_remaining_pickup_waypoints

    picker_user = request.session['picker_user']
    try:
        lat = float(request.POST.get('lat'))
        lng = float(request.POST.get('lng'))
    except (TypeError, ValueError):
        return JsonResponse({'ok': False}, status=400)

    client = get_client()
    resp = client.table('pickker_orders').select('delivery_lat, delivery_lng').eq('id', order_id).execute()
    order = resp.data[0] if resp.data else None
    if not order:
        raise Http404

    saved = update_picker_location(order_id, picker_user['id'], lat, lng)
    if not saved:
        return JsonResponse({'ok': False}, status=403)

    remaining_waypoints = get_remaining_pickup_waypoints(order_id)
    route = get_multi_stop_route(lat, lng, remaining_waypoints, float(order['delivery_lat']), float(order['delivery_lng']))
    return JsonResponse({
        'ok': True,
        'route': {
            'distance_km': route['distance_km'],
            'duration_min': route['duration_min'],
            'geometry': route['geometry'],
        } if route else None,
    })


def _picker_current_position(picker_id):
    """Prefers the picker's most recent live-tracked position (from an
    active in_transit delivery) over their registered base location, since
    that's a much better anchor for "which of my other orders is nearest
    from where I am right now"."""
    client = get_client()
    live_resp = (
        client.table('pickker_orders')
        .select('picker_live_lat, picker_live_lng, picker_live_updated_at')
        .eq('picker_id', picker_id)
        .eq('status', 'in_transit')
        .order('picker_live_updated_at', desc=True)
        .limit(1)
        .execute()
    )
    if live_resp.data and live_resp.data[0].get('picker_live_lat') is not None:
        row = live_resp.data[0]
        return float(row['picker_live_lat']), float(row['picker_live_lng'])

    profile_resp = client.table('pickker_picker_profiles').select('location_lat, location_lng').eq('user_id', picker_id).execute()
    if profile_resp.data and profile_resp.data[0].get('location_lat') is not None:
        row = profile_resp.data[0]
        return float(row['location_lat']), float(row['location_lng'])
    return None, None


def _picker_route_orders(picker_id, t, delivered_limit=20):
    """The picker's whole recent delivery picture on one map — every still-
    active order (confirmed/picking/in_transit) plus their most recently
    delivered ones, not just what's left to do, so a picker can evaluate
    and track everything in one place and not just their current to-do
    list. Active stops are additionally run through suggest_delivery_order()
    for a real fuel-efficient visiting sequence (not just "closest to my
    start point", which only accounts for the first leg of the trip)."""
    from market.route_optimizer import suggest_delivery_order

    client = get_client()
    orders_resp = (
        client.table('pickker_orders')
        .select('*')
        .eq('picker_id', picker_id)
        .in_('status', ['confirmed', 'picking', 'in_transit', 'delivered'])
        .order('updated_at', desc=True)
        .execute()
    )
    all_orders = orders_resp.data
    active_orders = [o for o in all_orders if o['status'] != 'delivered']
    delivered_orders = [o for o in all_orders if o['status'] == 'delivered'][:delivered_limit]
    orders = active_orders + delivered_orders

    customer_ids = [o['customer_id'] for o in orders]
    customers_by_id = {}
    if customer_ids:
        cust_resp = client.table('pickker_users').select('id, first_name, last_name').in_('id', customer_ids).execute()
        customers_by_id = {c['id']: c for c in cust_resp.data}

    cur_lat, cur_lng = _picker_current_position(picker_id)
    for order in orders:
        customer = customers_by_id.get(order['customer_id'])
        order['customer_name'] = f"{customer['first_name']} {customer['last_name']}".strip() if customer else 'Customer'
        order['next_action_label'] = get_action_label(t, order['status'])
        order['status_label'] = get_status_label(t, order['status'])
        order['is_delivered'] = order['status'] == 'delivered'
        if cur_lat is not None and order.get('delivery_lat') is not None:
            order['distance_from_here_km'] = round(
                haversine_km(cur_lat, cur_lng, float(order['delivery_lat']), float(order['delivery_lng'])), 2,
            )
        else:
            order['distance_from_here_km'] = None

    total_distance_km = None
    total_duration_min = None
    if cur_lat is not None and active_orders:
        stops = [
            {'id': o['id'], 'lat': float(o['delivery_lat']), 'lng': float(o['delivery_lng'])}
            for o in active_orders if o.get('delivery_lat') is not None
        ]
        ordered_stops, total_distance_km, total_duration_min = suggest_delivery_order(cur_lat, cur_lng, stops)
        by_id = {o['id']: o for o in active_orders}
        for i, stop in enumerate(ordered_stops):
            order = by_id[stop['id']]
            order['route_position'] = i + 1
            order['leg_distance_km'] = stop['leg_distance_km']
            order['leg_duration_min'] = stop['leg_duration_min']
            order['is_route_start'] = stop['is_start']
            order['steps'] = stop.get('steps')
            order['leg_geometry'] = stop.get('geometry')
        active_orders.sort(key=lambda o: o.get('route_position', 999))
    else:
        active_orders.sort(key=lambda o: (o['distance_from_here_km'] is None, o['distance_from_here_km'] or 0))

    orders = active_orders + delivered_orders
    return orders, cur_lat, cur_lng, total_distance_km, total_duration_min


@role_required('picker')
def picker_delivery_route(request):
    """The picker's whole delivery picture plotted on one global map — every
    active order in a fuel-efficient suggested visiting order, plus recent
    delivered ones shown as completed — so a picker can batch same-direction
    deliveries into one trip instead of guessing which to do next."""
    picker_user = request.session['picker_user']
    orders, cur_lat, cur_lng, total_distance_km, total_duration_min = _picker_route_orders(
        picker_user['id'], get_t(request),
    )
    return render(request, 'accounts/picker_delivery_route.html', {
        'orders': orders,
        'current_lat': cur_lat,
        'current_lng': cur_lng,
        'total_distance_km': total_distance_km,
        'total_duration_min': total_duration_min,
    })


@role_required('picker')
def picker_delivery_route_data(request):
    """Polled from the route map so a stop marked delivered elsewhere (the
    normal advance-order action) moves into the delivered group on its own,
    without the picker needing to manually refresh."""
    picker_user = request.session['picker_user']
    orders, cur_lat, cur_lng, total_distance_km, total_duration_min = _picker_route_orders(
        picker_user['id'], get_t(request),
    )
    return JsonResponse({
        'orders': [
            {
                'id': o['id'],
                'lat': float(o['delivery_lat']) if o.get('delivery_lat') is not None else None,
                'lng': float(o['delivery_lng']) if o.get('delivery_lng') is not None else None,
                'address': o.get('delivery_address'),
                'customer_name': o['customer_name'],
                'status': o['status'],
                'status_label': o['status_label'],
                'distance_from_here_km': o['distance_from_here_km'],
                'is_quick': bool(o.get('is_quick')),
                'is_delivered': o['is_delivered'],
                'route_position': o.get('route_position'),
                'is_route_start': bool(o.get('is_route_start')),
                'leg_distance_km': o.get('leg_distance_km'),
                'steps': o.get('steps'),
                'leg_geometry': o.get('leg_geometry'),
            }
            for o in orders
        ],
        'current_lat': cur_lat,
        'current_lng': cur_lng,
        'total_distance_km': total_distance_km,
        'total_duration_min': total_duration_min,
    })


@role_required('admin')
def admin_edit_user(request, user_id):
    client = get_client()
    user_resp = client.table('pickker_users').select('*').eq('id', user_id).execute()
    edited_user = user_resp.data[0] if user_resp.data else None
    if not edited_user:
        raise Http404

    customer_profile = None
    picker_profile = None
    if edited_user['role'] == Role.CUSTOMER:
        cp = client.table('pickker_customer_profiles').select('*').eq('user_id', user_id).execute()
        customer_profile = cp.data[0] if cp.data else None
    elif edited_user['role'] == Role.PICKER:
        pp = client.table('pickker_picker_profiles').select('*').eq('user_id', user_id).execute()
        picker_profile = pp.data[0] if pp.data else None

    if request.method == 'POST':
        client.table('pickker_users').update({
            'first_name': request.POST.get('first_name', '').strip(),
            'last_name': request.POST.get('last_name', '').strip(),
            'phone_number': request.POST.get('phone_number', '').strip(),
        }).eq('id', user_id).execute()

        if customer_profile:
            client.table('pickker_customer_profiles').update({
                'account_type': request.POST.get('account_type', customer_profile['account_type']),
                'business_name': request.POST.get('business_name', '').strip(),
                'address': request.POST.get('address', '').strip(),
                'city': request.POST.get('city', '').strip(),
                'area': request.POST.get('area', '').strip(),
            }).eq('user_id', user_id).execute()
        elif picker_profile:
            # The lat/lng hidden fields are always present in the form (even
            # when empty, e.g. after admin clicks "Reset location"), so an
            # empty value here is a deliberate reset to no location — not a
            # missing field to fall back on the old value for.
            lat = request.POST.get('location_lat', '')
            lng = request.POST.get('location_lng', '')
            from market.regions import get_or_create_region
            region = get_or_create_region(request.POST.get('region_name', ''))
            update_row = {
                'market_location': request.POST.get('market_location', '').strip(),
                'regions_served': request.POST.get('regions_served', '').strip(),
                'region_id': region['id'] if region else None,
                'location_lat': float(lat) if lat else None,
                'location_lng': float(lng) if lng else None,
            }
            license_number = request.POST.get('license_number')
            if license_number is not None:
                update_row['license_number'] = license_number.strip()
            if request.FILES.get('photo'):
                from .picker_documents import upload_picker_document
                update_row['photo_url'] = upload_picker_document(request.FILES['photo'], 'photos')
            if picker_profile.get('picker_segment') == 'b2b':
                truck_type_id = request.POST.get('truck_type_id')
                update_row['truck_type_id'] = int(truck_type_id) if truck_type_id else picker_profile.get('truck_type_id')
                # Admin-side tie for a standalone B2B picker (one truck, no
                # fleet company) to the specific warehouse/industry/market(s)
                # they serve -- the same tie a B2B picker can already set for
                # themselves at picker_profile_edit, now also settable by
                # admin (e.g. onboarding a picker who hasn't logged in yet,
                # or correcting it). Mirrors set_truck_warehouses' own
                # admin-side use on the Fleet Trucks page exactly.
                from market.fleet import set_picker_warehouses
                set_picker_warehouses(user_id, request.POST.getlist('warehouses'))
            else:
                market_id = request.POST.get('market_id')
                truck_type_id = request.POST.get('truck_type_id')
                update_row.update({
                    'truck_type_id': int(truck_type_id) if truck_type_id else picker_profile.get('truck_type_id'),
                    'license_plate': request.POST.get('license_plate', '').strip(),
                    'source_type': request.POST.get('source_type', picker_profile['source_type']),
                    'market_id': int(market_id) if market_id else None,
                })
                # An admin correcting this picker's pin (search/current-location/
                # drag) on a real, live position is treated as ground-truth for
                # their assigned market's own registered coordinate — so future
                # distance/fee/routing for every picker on that market benefits
                # from the correction, not just this one. Deliberately admin-only:
                # a picker editing their own profile never triggers this (see
                # picker_profile_edit), and a bare "Reset location" (empty
                # lat/lng) never does either — there's nothing to correct from.
                if lat and lng and update_row['market_id']:
                    from market.markets import get_market, update_market
                    market = get_market(update_row['market_id'])
                    if market:
                        update_market(market['id'], market['region'], market['name'], market.get('notes', ''), float(lat), float(lng))
            client.table('pickker_picker_profiles').update(update_row).eq('user_id', user_id).execute()

        messages.success(request, 'User updated.')
        return redirect('accounts:admin_users')

    from market.markets import get_active_markets
    from market.regions import get_region
    from market.truck_types import get_active_truck_types

    if picker_profile and picker_profile.get('region_id'):
        region = get_region(picker_profile['region_id'])
        picker_profile['region_name'] = region['name_en'] if region else ''

    vendor_tie_options = []
    picker_vendor_tie_keys = set()
    if picker_profile and picker_profile.get('picker_segment') == 'b2b':
        from market.fleet import get_picker_warehouses, get_vendor_tie_options
        vendor_tie_options = get_vendor_tie_options()
        picker_vendor_tie_keys = {f"{w['vendor_type']}:{w['vendor_id']}" for w in get_picker_warehouses(user_id)}

    return render(request, 'accounts/admin_user_edit.html', {
        'edited_user': edited_user,
        'customer_profile': customer_profile,
        'picker_profile': picker_profile,
        'account_type_choices': AccountType.CHOICES,
        'source_choices': SourceType.CHOICES,
        'truck_types': get_active_truck_types(segment=picker_profile.get('picker_segment')) if picker_profile else [],
        'markets': get_active_markets(),
        'vendor_tie_options': vendor_tie_options,
        'picker_vendor_tie_keys': picker_vendor_tie_keys,
        'center': DAR_ES_SALAAM_CENTER,
    })


def _parse_dispersion_factor(raw):
    try:
        value = float(raw)
        return value if value > 0 else 1.0
    except (TypeError, ValueError):
        return 1.0


@role_required('admin')
def admin_add_category(request):
    if request.method == 'POST':
        from market.categories import create_category
        name = request.POST.get('new_category_name')
        name_sw = request.POST.get('new_category_name_sw')
        dispersion_factor = _parse_dispersion_factor(request.POST.get('new_category_dispersion_factor'))
        if name and name.strip():
            create_category(name, name_sw, dispersion_factor)
            notify_admin('New product category added', f"Category \"{name.strip()}\" was added to the catalog.")
            messages.success(request, f'Category "{name.strip()}" added.')
        else:
            messages.error(request, 'Enter a category name.')
    return redirect(request.POST.get('next') or 'accounts:product_create')


@role_required('admin')
@require_POST
def admin_delete_category(request, category_id):
    from market.categories import delete_category
    delete_category(category_id)
    messages.success(request, 'Category deleted.')
    return redirect(request.POST.get('next') or 'accounts:product_create')


@role_required('admin')
@require_POST
def admin_edit_category(request, category_id):
    from market.categories import edit_category
    new_name = request.POST.get('new_name')
    new_name_sw = request.POST.get('new_name_sw')
    dispersion_factor = _parse_dispersion_factor(request.POST.get('new_dispersion_factor'))
    if new_name:
        edit_category(category_id, new_name, new_name_sw, dispersion_factor)
        messages.success(request, f'Category updated to "{new_name}".')
    return redirect(request.POST.get('next') or 'accounts:product_create')


@role_required('admin')
def admin_add_measure_unit(request):
    if request.method == 'POST':
        from market.measure_units import create_measure_unit
        key = request.POST.get('new_unit_key')
        label_en = request.POST.get('new_unit_label_en')
        label_sw = request.POST.get('new_unit_label_sw')
        if key and key.strip() and label_en and label_en.strip():
            create_measure_unit(key, label_en, label_sw)
            messages.success(request, f'Unit "{label_en.strip()}" registered.')
        else:
            messages.error(request, 'Enter a key and an English label for the unit.')
    return redirect(request.POST.get('next') or 'accounts:product_create')


@role_required('admin')
@require_POST
def admin_delete_measure_unit(request, unit_id):
    from market.measure_units import delete_measure_unit
    delete_measure_unit(unit_id)
    messages.success(request, 'Unit deleted.')
    return redirect(request.POST.get('next') or 'accounts:product_create')


@role_required('admin')
@require_POST
def admin_edit_measure_unit(request, unit_id):
    from market.measure_units import edit_measure_unit
    new_label_en = request.POST.get('new_label_en')
    new_label_sw = request.POST.get('new_label_sw')
    if new_label_en:
        edit_measure_unit(unit_id, new_label_en, new_label_sw)
        messages.success(request, f'Unit updated to "{new_label_en}".')
    return redirect(request.POST.get('next') or 'accounts:product_create')


@role_required('admin')
def product_create(request):
    return _product_form(request, product=None)


@role_required('admin')
def product_edit(request, pk):
    resp = get_client().table('pickker_products').select('*').eq('id', pk).execute()
    product = resp.data[0] if resp.data else None
    return _product_form(request, product=product)


@role_required('admin')
@require_POST
def product_delete(request, pk):
    """A product that's actually been ordered can't be hard-deleted — order
    items reference it by product_id, and losing that would corrupt real
    order history. So: if it's never been ordered, remove it (and its own
    cart-line/measure/regional-price rows, which are safe to drop) for
    real; otherwise just hide it from the storefront (is_active=False) and
    say so, rather than silently doing something different from what was
    asked."""
    client = get_client()
    resp = client.table('pickker_products').select('name').eq('id', pk).execute()
    if not resp.data:
        raise Http404
    name = resp.data[0]['name']

    ever_ordered = client.table('pickker_order_items').select('id').eq('product_id', pk).limit(1).execute()
    if ever_ordered.data:
        client.table('pickker_products').update({'is_active': False}).eq('id', pk).execute()
        messages.success(request, f'{name} has order history, so it was hidden from the store instead of deleted.')
    else:
        client.table('pickker_cart_items').delete().eq('product_id', pk).execute()
        client.table('pickker_product_measures').delete().eq('product_id', pk).execute()
        client.table('pickker_regional_prices').delete().eq('product_id', pk).execute()
        client.table('pickker_vendor_stock').delete().eq('product_id', pk).execute()
        # A never-ordered product can still have stock_movements (e.g. an
        # admin/company "received" entry with no order behind it) — safe to
        # drop along with everything else once the product itself is gone.
        client.table('pickker_stock_movements').delete().eq('product_id', pk).execute()
        client.table('pickker_products').delete().eq('id', pk).execute()
        messages.success(request, f'{name} was permanently deleted.')
    return redirect('accounts:admin_portal')


@role_required('admin')
@require_POST
def product_restore(request, pk):
    client = get_client()
    resp = client.table('pickker_products').select('name').eq('id', pk).execute()
    if not resp.data:
        raise Http404
    client.table('pickker_products').update({'is_active': True}).eq('id', pk).execute()
    messages.success(request, f"{resp.data[0]['name']} restored to the store.")
    return redirect('accounts:admin_portal')


def _save_uploaded_image(image_file, subdir):
    """Admin-uploaded content images (product photos, homepage video
    thumbnail, etc.) — stored in Supabase Storage rather than local disk, so
    they survive a cPanel redeploy (which replaces the app directory but
    doesn't carry over anything written to local disk at runtime)."""
    from .site_images import upload_site_image
    return upload_site_image(image_file, subdir)


def _save_product_image(image_file):
    return _save_uploaded_image(image_file, 'products')


def _save_market_logo(image_file):
    return _save_uploaded_image(image_file, 'markets')


def _save_truck_icon(image_file):
    return _save_uploaded_image(image_file, 'truck-icons')


def _build_product_row(form):
    """Plain-field + image-save + vendor-priority-dispatch row dict shared
    by every product create/edit path — admin's own _product_form below and
    the company's self-service market.views::fleet_create_product. Caller
    is responsible for is_approved/created_by and the actual insert/update.
    form must already be validated (form.is_valid() called)."""
    data = form.cleaned_data
    row = {
        'name': data['name'],
        'name_sw': data.get('name_sw', ''),
        'category': data['category'],
        'unit': data['unit'],
        'farm_price': str(data['farm_price']),
        'market_price': str(data['market_price']),
        'b2b_price': str(data['b2b_price']) if data.get('b2b_price') is not None else None,
        'min_purchase_qty': data['min_purchase_qty'],
        'b2b_min_qty': data['b2b_min_qty'],
        'stock_qty': data['stock_qty'],
        'is_trending': data['is_trending'],
        'quality_grade': data.get('quality_grade', '').strip(),
    }
    # Warehouse/Industry take priority over Market when set — a
    # product is sourced from exactly one of the three. vendor_type/
    # vendor_id (see market/vendors.py) is the generalized column;
    # market_id stays the authoritative one for the market case so
    # every existing market_id read site keeps working unchanged.
    warehouse_id = data.get('warehouse_id')
    industry_id = data.get('industry_id')
    market_id = data.get('market_id')
    if warehouse_id:
        row.update({'market_id': None, 'vendor_type': 'warehouse', 'vendor_id': int(warehouse_id)})
    elif industry_id:
        row.update({'market_id': None, 'vendor_type': 'industry', 'vendor_id': int(industry_id)})
    elif market_id:
        row.update({'market_id': int(market_id), 'vendor_type': 'market', 'vendor_id': int(market_id)})
    else:
        row.update({'market_id': None, 'vendor_type': None, 'vendor_id': None})
    if data.get('image'):
        row['image_url'] = _save_product_image(data['image'])
    return row


def _product_form(request, product):
    if request.method == 'POST':
        form = ProductForm(request.POST, request.FILES)
        if form.is_valid():
            row = _build_product_row(form)
            # is_approved isn't a ProductForm field (see product_form.html —
            # it's a plain checkbox rendered only on the edit page, outside
            # the form-field loop, so it can never appear on the create page
            # and a future ProductForm field addition can't silently start
            # inserting unapproved products). Admin's own create always
            # inserts approved; editing reads whatever the checkbox says.
            row['is_approved'] = (request.POST.get('is_approved') == 'on') if product else True

            client = get_client()
            picker_user = request.session.get('picker_user')
            if product:
                client.table('pickker_products').update(row).eq('id', product['id']).execute()
                messages.success(request, f'{row["name"]} updated.')
            else:
                row['created_by'] = picker_user['id']
                client.table('pickker_products').insert(row).execute()
                messages.success(request, f'{row["name"]} added to the catalog.')
            return redirect('accounts:admin_portal')
    else:
        initial = product.copy() if product else {}
        if initial.get('vendor_type') == 'warehouse':
            initial['warehouse_id'] = initial.get('vendor_id')
        elif initial.get('vendor_type') == 'industry':
            initial['industry_id'] = initial.get('vendor_id')
        form = ProductForm(initial=initial)

    from market.categories import get_all_categories
    from market.measure_units import get_active_measure_units
    return render(request, 'accounts/product_form.html', {
        'form': form,
        'product': product,
        'categories': get_all_categories(),
        'measure_units': get_active_measure_units(),
    })


@role_required('admin')
def admin_product_measures(request, product_id):
    from market.product_measures import get_measures_for_product, save_measure, deactivate_measure
    from market.measure_units import get_active_measure_units, create_measure_unit

    product_resp = get_client().table('pickker_products').select('*').eq('id', product_id).execute()
    product = product_resp.data[0] if product_resp.data else None
    if not product:
        raise Http404

    if request.method == 'POST':
        new_unit_key = request.POST.get('new_unit_key', '').strip()
        if new_unit_key:
            create_measure_unit(new_unit_key, request.POST.get('new_unit_label_en', '').strip(), request.POST.get('new_unit_label_sw', '').strip())
            messages.success(request, 'Unit registered — set its price below and save.')
            return redirect('accounts:admin_product_measures', product_id=product_id)

        units = get_active_measure_units()
        default_type = request.POST.get('default_measure_type')
        saved_any = False
        for unit in units:
            measure_type = unit['key']
            if request.POST.get(f'enable_{measure_type}') != '1':
                deactivate_measure(product_id, measure_type)
                continue
            price = request.POST.get(f'price_{measure_type}', '').strip()
            kg_equivalent = request.POST.get(f'kg_equivalent_{measure_type}', '').strip()
            min_qty = request.POST.get(f'min_qty_{measure_type}', '').strip()
            if not price or not kg_equivalent:
                messages.error(request, f"{unit['label_en']} needs both a price and a kg-equivalent.")
                continue
            save_measure(
                product_id, measure_type,
                price=price, kg_equivalent=kg_equivalent,
                is_default=(measure_type == default_type),
                min_qty=int(min_qty) if min_qty else 1,
            )
            saved_any = True
        if saved_any:
            messages.success(request, 'Measures saved.')
        return redirect('accounts:admin_product_measures', product_id=product_id)

    existing = {m['measure_type']: m for m in get_measures_for_product(product_id)}
    measure_rows = [
        {
            'type': unit['key'],
            'label': unit['label_en'] + (f" ({unit['label_sw']})" if unit.get('label_sw') else ''),
            'existing': existing.get(unit['key']),
        }
        for unit in get_active_measure_units()
    ]

    return render(request, 'accounts/admin_product_measures.html', {
        'product': product,
        'measure_rows': measure_rows,
    })


@role_required('admin')
@require_POST
def admin_toggle_product_measure(request, measure_id):
    from market.product_measures import toggle_measure_active
    now_active = toggle_measure_active(measure_id)
    if now_active is not None:
        messages.success(request, 'Measure activated.' if now_active else 'Measure deactivated.')
    return redirect(request.POST.get('next') or 'accounts:admin_portal')


@role_required('admin')
def admin_price_trends(request):
    client = get_client()
    products = client.table('pickker_products').select('id, name, unit').order('name').execute().data

    if request.method == 'POST':
        form = RegionalPriceForm(request.POST, products=products)
        if form.is_valid():
            record_price(
                product_id=int(form.cleaned_data['product_id']),
                region=form.cleaned_data['region'],
                farm_price=form.cleaned_data['farm_price'],
                market_price=form.cleaned_data['market_price'],
                recorded_by=request.session['picker_user']['id'],
            )
            messages.success(request, f"Price recorded for {form.cleaned_data['region']}.")
            return redirect('accounts:admin_price_trends')
    else:
        form = RegionalPriceForm(products=products)

    products_with_trends = get_all_products_with_trends(
        client.table('pickker_products').select('*').order('name').execute().data
    )

    return render(request, 'accounts/admin_price_trends.html', {
        'form': form,
        'products': products_with_trends,
    })
