import React, { createContext, useContext, useEffect, useRef, useState } from 'react';
import { Alert, AppState, Platform, Vibration } from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
import * as Notifications from 'expo-notifications';
import { apiFetch, API_URL } from '../services/api';
import { parseOrderAddress } from '../utils/helpers';
import { playScanSound } from '../utils/sound';
import { printOrderLocally, fetchSenderDetails, buildLocalLabelData, scanForNetworkPrinters as scanNetworkPrinters } from '../services/print';
import { useAuth } from './AuthContext';
import { useSettings } from '../hooks/useSettings';

const AppContext = createContext(null);

export function useApp() {
  const ctx = useContext(AppContext);
  if (!ctx) throw new Error('useApp must be used within an AppProvider');
  return ctx;
}

export function AppProvider({ children }) {
  const {
    authToken, authCompany, currentUser, handleSessionExpired,
    appScreen, appScreenRef, tokenRef,
    staffName, tempName, setTempName, showLogin, setShowLogin,
    securityMode, setSecurityMode,
    isMasterUser, isSecondaryUser,
    effectiveCanPick, effectiveCanPack, effectiveCanPrint,
  } = useAuth();

  const { settings, updateSetting, syncFromServer: syncSettings, loaded: settingsLoaded } = useSettings();

  // ── Order / pick state ─────────────────────────────────────────────────────
  const packedOrderIds = useRef(new Set());
  const [orders, setOrders] = useState([]);
  const [packieStocks, setPackieStocks] = useState([]);
  const [livePackieStocks, setLivePackieStocks] = useState([]);
  const [selectedIds, setSelectedIds] = useState([]);
  const [showPickList, setShowPickList] = useState(false);
  const [loading, setLoading] = useState(false);
  const [selectedOrder, setSelectedOrder] = useState(null);
  const [lockInfo, setLockInfo] = useState(null);
  const [showLockOverrideModal, setShowLockOverrideModal] = useState(false);
  const [lockCountdown, setLockCountdown] = useState(10);
  const [lockOverridePending, setLockOverridePending] = useState(false);
  const [editingNote, setEditingNote] = useState('');
  const [globalPicks, setGlobalPicks] = useState({});
  const [localOrderPicks, setLocalOrderPicks] = useState({});
  const [fetchError, setFetchError] = useState(null); // Improvement 9

  // ── Staff management state ──────────────────────────────────────────────────
  const [staffList, setStaffList] = useState([]);
  const [staffDirty, setStaffDirty] = useState({});
  const [newStaffName, setNewStaffName] = useState('');
  const [secondaryPassword, setSecondaryPassword] = useState('');

  // ── Provider / settings state ───────────────────────────────────────────────
  const [shippingProviders, setShippingProviders] = useState([]);
  const [cmsProviders, setCmsProviders] = useState([]);
  const [selectedShipProvider, setSelectedShipProvider] = useState('packie');
  const [selectedEcomProvider, setSelectedEcomProvider] = useState('shopify');
  const [providerFieldInputs, setProviderFieldInputs] = useState({});
  const [providerConfigs, setProviderConfigs] = useState({});
  const [showProviderInputs, setShowProviderInputs] = useState({ shipping: false, ecommerce: false });
  const [forceUpdatingCourier, setForceUpdatingCourier] = useState(false);
  const [packageMappingVisible, setPackageMappingVisible] = useState(false);
  const [packageMappingProvider, setPackageMappingProvider] = useState('');
  const [packageMapping, setPackageMapping] = useState([]);
  const [packageStockOptions, setPackageStockOptions] = useState([]);
  const [packageMappingLoading, setPackageMappingLoading] = useState(false);
  const [packageMappingSaving, setPackageMappingSaving] = useState(false);

  // ── UI / modal visibility state ───────────────────────────────────────────────
  const [settingsVisible, setSettingsVisible] = useState(false);
  const [settingsTab, setSettingsTab] = useState('general');
  const [accountVisible, setAccountVisible] = useState(false);
  const [adminVisible, setAdminVisible] = useState(false);
  const [colorPickerVisible, setColorPickerVisible] = useState(false);
  const [tempR, setTempR] = useState(0);
  const [tempG, setTempG] = useState(122);
  const [tempB, setTempB] = useState(88);

  // ── Print / label / quote state (needed by listed functions) ────────────────
  const [labelModalVisible, setLabelModalVisible] = useState(false);
  const [selectedStockId, setSelectedStockId] = useState(null);
  const [isCustomMode, setIsCustomMode] = useState(false);
  const [customDims, setCustomDims] = useState({ weight: '', l: '', w: '', h: '' });
  const [customQuotes, setCustomQuotes] = useState([]);
  const [satchelQuotes, setSatchelQuotes] = useState([]);
  const [satchelQuoteErrors, setSatchelQuoteErrors] = useState([]);
  const [satchelQuoteProviders, setSatchelQuoteProviders] = useState([]);
  const [isFetchingQuotes, setIsFetchingQuotes] = useState(false);
  const [isFetchingSatchelQuotes, setIsFetchingSatchelQuotes] = useState(false);
  const [labelPrintData, setLabelPrintData] = useState(null);
  const [labelPreviewVisible, setLabelPreviewVisible] = useState(false);
  const [discoveredPrinters, setDiscoveredPrinters] = useState([]);
  const [isScanningPrinters, setIsScanningPrinters] = useState(false);

  // ── Scanner state ─────────────────────────────────────────────────────────────
  const [isScanning, setIsScanning] = useState(false);
  const [cameraFacing, setCameraFacing] = useState('back');
  const [qtyModalVisible, setQtyModalVisible] = useState(false);
  const [scannedItemData, setScannedItemData] = useState(null);
  const [scannedQty, setScannedQty] = useState(1);

  // ── Address modal state ───────────────────────────────────────────────────────
  const [addressModalVisible, setAddressModalVisible] = useState(false);
  const [addressSearch, setAddressSearch] = useState('');
  const [addressResults, setAddressResults] = useState([]);
  const [isSearchingAddress, setIsSearchingAddress] = useState(false);
  const [parsedAddress, setParsedAddress] = useState(null);

  // ── Refs ─────────────────────────────────────────────────────────────────────
  const highestOrderId = useRef(0);
  const forceOverrideRef = useRef(false);
  const viewShotRef = useRef(null);
  const appStateRef = useRef(AppState.currentState);
  const scanCooldownRef = useRef(0);
  const selectedOrderRef = useRef(null);
  const staffNameRef = useRef('');
  const lockInfoRef = useRef(null);

  useEffect(() => { selectedOrderRef.current = selectedOrder; }, [selectedOrder]);
  useEffect(() => { staffNameRef.current = staffName; }, [staffName]);
  useEffect(() => { lockInfoRef.current = lockInfo; }, [lockInfo]);

  // ═══════════════════════════════════════════════════════════════════════════
  // FUNCTIONS
  // ═══════════════════════════════════════════════════════════════════════════

  // ── fetchStaff (lines 561-587) ──────────────────────────────────────────────
  const fetchStaff = async (tokenOverride = null) => {
    try {
      const token = tokenOverride || tokenRef.current || authToken;
      const staffUrl = token
        ? `${API_URL}?action=get_staff&_token=${encodeURIComponent(token)}`
        : `${API_URL}?action=get_staff`;
      const reqOptions = tokenOverride
        ? { headers: { 'X-Auth-Token': tokenOverride } }
        : {};
      const res = await apiFetch(staffUrl, reqOptions);
      if (res.status === 401) { handleSessionExpired(); return; }
      if (res.status === 403) {
        Alert.alert('Users', 'Admin access is required to manage staff.');
        return;
      }
      const data = await res.json();
      if (data?.success === false) {
        console.log('Failed to fetch staff:', data.error || 'Unknown error');
        Alert.alert('Users', data.error || 'Could not load users.');
        return;
      }
      setStaffList(data.staff || []);
    } catch (e) {
      console.log('Failed to fetch staff', e);
    }
  };

  // ── fetchProviders (lines 588-664) ──────────────────────────────────────────
  const fetchProviders = async () => {
    if (currentUser?.role !== 'master') {
      Alert.alert('Settings', 'Sign in as Admin to view courier and ecommerce settings.');
      return;
    }

    let shipping = [];
    let ecommerce = [];

    try {
      const [res1, res2] = await Promise.all([
        apiFetch(`${API_URL}?action=list_shipping_providers`),
        apiFetch(`${API_URL}?action=list_cms_providers`),
      ]);
      const [data1, data2] = await Promise.all([res1.json(), res2.json()]);
      if (data1.success) {
        shipping = (data1.providers || []).filter(p => !['courierit1'].includes(String(p?.slug || '').toLowerCase()));
      }
      if (data2.success) {
        ecommerce = (data2.providers || []).filter(p => !['bigcommerce'].includes(String(p?.slug || '').toLowerCase()));
      }
      setShippingProviders(shipping);
      setCmsProviders(ecommerce);
    } catch (e) {
      console.log('Failed to load provider catalogues', e);
    }

    try {
      const resCfg = await apiFetch(`${API_URL}?action=get_provider_config`);
      if (resCfg.status === 401) { handleSessionExpired(); return; }
      if (resCfg.status === 403) {
        Alert.alert('Settings', 'Admin access is required to view provider settings.');
        return;
      }
      const cfgData = await resCfg.json();
      if (!cfgData.success) return;

      const configs = cfgData.configs || {};
      setProviderConfigs(configs);

      const general = configs['general/app'] || {};
      const savedShip = general.active_shipping_provider || selectedShipProvider;
      const activeShip = shipping.some(p => p.slug === savedShip) ? savedShip : (shipping[0]?.slug || 'packie');
      const savedEcom = (general.active_ecom_provider === '' || general.active_ecom_provider === 'none')
        ? 'none'
        : (general.active_ecom_provider || ecommerce[0]?.slug || 'none');
      const activeEcom = savedEcom === 'none' || ecommerce.some(p => p.slug === savedEcom) ? savedEcom : (ecommerce[0]?.slug || 'none');
      setSelectedShipProvider(activeShip);
      setSelectedEcomProvider(activeEcom);

      const nextInputs = {};
      const isSensitive = (f) => {
        const key = String(f?.key || '').toLowerCase();
        const label = String(f?.label || '').toLowerCase();
        const type = String(f?.type || '').toLowerCase();
        return key.includes('api') || key.includes('token') || key.includes('secret') || key.includes('password')
          || key.includes('bearer')
          || label.includes('api') || label.includes('token') || label.includes('secret') || label.includes('password')
          || type === 'password';
      };

      shipping.forEach((p) => {
        const conf = configs[`shipping/${p.slug}`] || {};
        (p.required_fields || []).forEach((f, idx) => {
          if (isSensitive(f)) return;
          if (typeof conf[f.key] === 'string') nextInputs[providerInputKey('shipping', p.slug, idx)] = conf[f.key];
        });
      });

      ecommerce.forEach((p) => {
        const conf = configs[`ecommerce/${p.slug}`] || {};
        (p.required_fields || []).forEach((f, idx) => {
          if (isSensitive(f)) return;
          if (typeof conf[f.key] === 'string') nextInputs[providerInputKey('ecommerce', p.slug, idx)] = conf[f.key];
        });
      });

      setProviderFieldInputs(prev => ({ ...prev, ...nextInputs }));
    } catch (e) {
      console.log('Failed to load provider config', e);
    }
  };

  // ── Provider input helpers (lines 665-680) ──────────────────────────────────
  const providerInputKey = (category, provider, idx) => `${category}:${provider}:${idx}`;
  const getProviderInput = (category, provider, idx) => providerFieldInputs[providerInputKey(category, provider, idx)] || '';
  const setProviderInput = (category, provider, idx, value) => {
    const key = providerInputKey(category, provider, idx);
    setProviderFieldInputs(prev => ({ ...prev, [key]: value }));
  };

  const getProviderConfig = (category, provider) => providerConfigs[`${category}/${provider}`] || {};
  const isSensitiveProviderField = (fieldDef) => {
    const key = String(fieldDef?.key || '').toLowerCase();
    const label = String(fieldDef?.label || '').toLowerCase();
    const type = String(fieldDef?.type || '').toLowerCase();
    return key.includes('api') || key.includes('token') || key.includes('secret') || key.includes('password')
      || key.includes('bearer')
      || label.includes('api') || label.includes('token') || label.includes('secret') || label.includes('password')
      || type === 'password';
  };

  // ── saveStaffChanges (lines 701-718) ────────────────────────────────────────
  const saveStaffChanges = async (id) => {
    const changes = staffDirty[id];
    if (!changes) return;
    try {
      const res = await apiFetch(API_URL, {
        method: 'POST',
        body: JSON.stringify({ action: 'update_staff_perms', id, ...changes }),
      });
      const data = await res.json();
      if (!data.success) {
        return Alert.alert('Error', data.error || 'Could not save staff changes.');
      }
      setStaffDirty(prev => { const n = { ...prev }; delete n[id]; return n; });
      await fetchStaff();
    } catch (e) {
      Alert.alert('Error', 'Could not save staff changes.');
    }
  };

  // ── saveProviderConfig (lines 720-758) ──────────────────────────────────────
  const saveProviderConfig = async (category) => {
    // category: 'shipping' or 'ecommerce'
    const provider_slug = category === 'shipping' ? selectedShipProvider : selectedEcomProvider;
    let defs = category === 'shipping'
      ? (shippingProviders.find(p => p.slug === provider_slug)?.required_fields || [])
      : (cmsProviders.find(p => p.slug === provider_slug)?.required_fields || []);
    if (category === 'shipping' && provider_slug === 'zappy') {
      const hasClientId = defs.some(f => String(f?.key || '').toLowerCase() === 'client_id');
      defs = defs.map(f => (
        String(f?.key || '').toLowerCase() === 'api_key'
          ? { ...f, label: 'Zappy Password', type: 'password' }
          : f
      ));
      if (!hasClientId) defs = [...defs, { key: 'client_id', label: 'Zappy Client ID', type: 'text', example: '2879' }];
    }

    // Build payload from per-input values so duplicate field keys do not mirror in UI.
    const payloadFields = {};
    defs.forEach((f, idx) => {
      const raw = getProviderInput(category, provider_slug, idx);
      if (!raw) return;
      const label = String(f.label || '').toLowerCase();
      let key = f.key;
      if (payloadFields[key] !== undefined) {
        if (label.includes('user')) key = 'username';
        else if (label.includes('pass') || label.includes('api')) key = 'api_key';
        else key = `${f.key}_${idx + 1}`;
      }
      payloadFields[key] = raw;
    });

    if (category === 'shipping' && provider_slug === 'zappy') {
      defs.forEach((f, idx) => {
        const raw = getProviderInput(category, provider_slug, idx);
        if (!raw) return;
        const key = String(f?.key || '').toLowerCase();
        const label = String(f?.label || '').toLowerCase();
        if (!payloadFields.username && (key.includes('user') || label.includes('user'))) {
          payloadFields.username = raw;
        }
        if (!payloadFields.api_key && (key.includes('pass') || label.includes('pass') || key.includes('api') || label.includes('api'))) {
          payloadFields.api_key = raw;
        }
        if (!payloadFields.client_id && (key.includes('client') || label.includes('client'))) {
          payloadFields.client_id = raw;
        }
      });
    }

    try {
      const res = await apiFetch(API_URL, {
        method: 'POST',
        body: JSON.stringify({ action: 'save_provider_config', category, provider_slug, fields: payloadFields }),
      });
      if (res.status === 401) { handleSessionExpired(); return; }
      const data = await res.json();
      if (!data.success) {
        Alert.alert('Error', data.error || 'Could not save provider settings.');
        return;
      }
      await fetchProviders();
      if (category === 'shipping' && provider_slug === 'zappy' && data.zappy_saved) {
        const missing = [];
        if (!data.zappy_saved.username) missing.push('username');
        if (!data.zappy_saved.password) missing.push('password');
        if (missing.length) {
          Alert.alert('Saved', `Zappy settings saved, but no ${missing.join(' or ')} field was sent from the form.`);
        } else {
          Alert.alert('Saved', 'Zappy username/password saved.');
        }
      } else {
        Alert.alert('Saved', 'Provider settings saved.');
      }
    } catch (e) {
      Alert.alert('Error', 'Could not save provider settings.');
    }
  };

  // ── forceUpdateCourier (lines 760-785) ──────────────────────────────────────
  const forceUpdateCourier = async () => {
    if (forceUpdatingCourier) return;
    try {
      setForceUpdatingCourier(true);
      const res = await apiFetch(API_URL, {
        method: 'POST',
        body: JSON.stringify({ action: 'force_update_courier', provider_slug: selectedShipProvider }),
      });
      if (res.status === 401) { handleSessionExpired(); return; }
      const data = await res.json();
      if (!data.success) {
        Alert.alert('Update failed', data.error || 'Could not force update courier data.');
        return;
      }

      await Promise.all([fetchStocks(), fetchOrders(true), fetchProviders()]);
      await fetchPackageMapping(selectedShipProvider, true);
      const stockCount = data?.result?.stocks?.count;
      Alert.alert('Update complete', stockCount !== undefined
        ? `Courier refresh completed. Stocks synced: ${stockCount}. Set the stock mappings below.`
        : 'Courier refresh completed. Set the stock mappings below.');
    } catch (e) {
      Alert.alert('Error', 'Could not force update courier data.');
    } finally {
      setForceUpdatingCourier(false);
    }
  };

  const fetchPackageMapping = async (provider = selectedShipProvider, showPanel = true) => {
    try {
      setPackageMappingLoading(true);
      const res = await apiFetch(`${API_URL}?action=get_package_mapping&provider_slug=${encodeURIComponent(provider)}`);
      if (res.status === 401) { handleSessionExpired(); return null; }
      const data = await res.json();
      if (!data?.success) {
        Alert.alert('Stock mapping', data?.error || 'Could not load stock mappings.');
        return null;
      }
      setPackageMappingProvider(data.provider_slug || provider);
      setPackageMapping(data.packages || []);
      setPackageStockOptions(data.stock_options || []);
      if (showPanel) setPackageMappingVisible(true);
      return data;
    } catch (_e) {
      Alert.alert('Stock mapping', 'Could not load stock mappings.');
      return null;
    } finally {
      setPackageMappingLoading(false);
    }
  };

  const setPackageMappingStock = (packageCode, optionId) => {
    const option = packageStockOptions.find(o => String(o.id) === String(optionId));
    setPackageMapping(prev => prev.map(pkg => {
      if (pkg.code !== packageCode) return pkg;
      return {
        ...pkg,
        provider_stock_id: option ? String(option.id) : '',
        provider_service_type: option?.serviceType !== undefined ? String(option.serviceType) : '',
        provider_label: option?.label || '',
        length: option?.length ?? pkg.length,
        width: option?.width ?? pkg.width,
        height: option?.height ?? pkg.height,
        weight: option?.weight ?? pkg.weight,
      };
    }));
  };

  const setPackageStockSetting = (stockId, patch) => {
    setPackageStockOptions(prev => prev.map(stock => {
      if (String(stock.id) !== String(stockId)) return stock;
      const next = { ...stock, ...patch };
      if (patch.quick_pick_enabled === false) {
        next.quick_pick_is_default = false;
      }
      if (patch.quick_pick_enabled && !next.quick_pick_display_name) {
        next.quick_pick_display_name = stock.display_label || stock.display_name || stock.label || stock.name || '';
      }
      if (patch.quick_pick_enabled && !next.quick_pick_carrier && Array.isArray(stock.quick_pick_carrier_options) && stock.quick_pick_carrier_options.length >= 1) {
        next.quick_pick_carrier = stock.quick_pick_carrier_options[0];
      }
      return next;
    }).map(stock => {
      if (!patch.quick_pick_is_default || String(stock.id) === String(stockId)) return stock;
      return { ...stock, quick_pick_is_default: false };
    }));
  };

  const savePackageMapping = async () => {
    if (packageMappingSaving) return;
    try {
      setPackageMappingSaving(true);
      const res = await apiFetch(API_URL, {
        method: 'POST',
        body: JSON.stringify({
          action: 'save_package_mapping',
          provider_slug: packageMappingProvider || selectedShipProvider,
          mappings: packageMapping,
          stock_settings: packageStockOptions,
        }),
      });
      if (res.status === 401) { handleSessionExpired(); return; }
      const data = await res.json();
      if (!data?.success) {
        Alert.alert('Stock mapping', data?.error || 'Could not save stock mappings.');
        return;
      }
      if (data.mapping) {
        setPackageMappingProvider(data.mapping.provider_slug || packageMappingProvider || selectedShipProvider);
        setPackageMapping(data.mapping.packages || packageMapping);
        setPackageStockOptions(data.mapping.stock_options || packageStockOptions);
      }
      await fetchStocks();
      Alert.alert('Saved', 'Stock mappings saved.');
    } catch (_e) {
      Alert.alert('Stock mapping', 'Could not save stock mappings.');
    } finally {
      setPackageMappingSaving(false);
    }
  };

  // ── addStaff (lines 787-803) ────────────────────────────────────────────────
  const addStaff = async (name) => {
    const trimmed = name.trim();
    if (!trimmed) return Alert.alert('Required', 'Please enter a name.');
    try {
      const res = await apiFetch(API_URL, {
        method: 'POST',
        body: JSON.stringify({ action: 'add_staff', name: trimmed }),
      });
      if (res.status === 401) { handleSessionExpired(); return; }
      const data = await res.json();
      if (!data.success) return Alert.alert('Error', data.error || 'Could not add staff');
      setNewStaffName('');
      await fetchStaff();
    } catch (e) {
      Alert.alert('Error', 'Could not add staff member.');
    }
  };

  // ── saveSecondaryPassword (lines 805-828) ───────────────────────────────────
  const saveSecondaryPassword = async () => {
    if (currentUser?.role !== 'master') return;
    const trimmed = secondaryPassword.trim();
    if (trimmed.length < 4) {
      Alert.alert('Secondary Password', 'Please enter at least 4 characters.');
      return;
    }
    try {
      const res = await apiFetch(API_URL, {
        method: 'POST',
        body: JSON.stringify({ action: 'set_secondary_password', password: trimmed }),
      });
      if (res.status === 401) { handleSessionExpired(); return; }
      const data = await res.json();
      if (!data.success) {
        Alert.alert('Secondary Password', data.error || 'Could not update password.');
        return;
      }
      setSecondaryPassword('');
      Alert.alert('Secondary Password', 'Shared secondary password updated.');
    } catch (e) {
      Alert.alert('Secondary Password', 'Could not update password.');
    }
  };

  // ── deleteStaff (lines 830-844) ─────────────────────────────────────────────
  const deleteStaff = async (id) => {
    try {
      const res = await apiFetch(API_URL, {
        method: 'POST',
        body: JSON.stringify({ action: 'delete_staff', id }),
      });
      if (res.status === 401) { handleSessionExpired(); return; }
      if (res.status === 403) { Alert.alert('Forbidden', 'Master access required'); return; }
      const data = await res.json();
      if (!data.success) { Alert.alert('Error', data.error || 'Could not remove staff'); return; }
      await fetchStaff();
    } catch (e) {
      Alert.alert('Error', 'Could not remove staff member.');
    }
  };

  // ── updateStaffPermission (lines 846-858) ───────────────────────────────────
  const updateStaffPermission = async (id, field, value) => {
    setStaffList(prev => prev.map(s => s.id === id ? { ...s, [field]: value } : s));
    try {
      await apiFetch(API_URL, {
        method: 'POST',
        body: JSON.stringify({ action: 'update_staff_perms', id, [field]: value ? 1 : 0 }),
      });
    } catch (e) {
      // Revert on error
      setStaffList(prev => prev.map(s => s.id === id ? { ...s, [field]: !value } : s));
      Alert.alert('Error', 'Could not update permission.');
    }
  };

  // ── fetchStocks (lines 1670-1680) ───────────────────────────────────────────
  const fetchStocks = async () => {
    if ((appScreenRef.current || appScreen) !== 'app' || !(tokenRef.current || authToken)) return;
    try {
      const quickRes = await apiFetch(`${API_URL}?action=get_stocks`);
      if (quickRes.status === 401) { handleSessionExpired(); return; }
      const quickData = await quickRes.json();
      const quickStocks = Array.isArray(quickData) ? quickData : [];
      setPackieStocks(quickStocks);

      try {
        const liveRes = await apiFetch(`${API_URL}?action=get_live_stocks`);
        if (liveRes.status === 401) { handleSessionExpired(); return; }
        const liveData = await liveRes.json();
        setLivePackieStocks(Array.isArray(liveData) ? liveData : quickStocks);
      } catch (_e) {
        setLivePackieStocks(quickStocks);
      }
    } catch (e) {
      console.log(e);
    }
  };

  // ── fetchOrders (lines 1682-1729) + Improvement 9: fetchError ───────────────
  const fetchOrders = async (isBackground = false) => {
    if ((appScreenRef.current || appScreen) !== 'app' || !(tokenRef.current || authToken)) {
      if (!isBackground) setLoading(false);
      return;
    }
    if (!isBackground) {
      // Cache-first: show last-known order list instantly while network loads
      try {
        const cached = await AsyncStorage.getItem('cachedOrders');
        if (cached) setOrders(JSON.parse(cached).filter(o => !packedOrderIds.current.has(String(o.id))));
      } catch (_) {}
      setLoading(true);
    }
    try {
      const res = await apiFetch(`${API_URL}?action=get_orders`);
      if (res.status === 401) { handleSessionExpired(); setLoading(false); return; }
      const text = await res.text();
      let data;
      try {
        data = JSON.parse(text);
      } catch (_) {
        if (!isBackground) setLoading(false);
        return;
      }
      const newOrders = (data || []).filter(o => !packedOrderIds.current.has(String(o.id)));

      let foundNewer = false;
      newOrders.forEach(o => {
        const idNum = parseInt(o.id);
        if (idNum > highestOrderId.current && highestOrderId.current !== 0) foundNewer = true;
        if (idNum > highestOrderId.current) highestOrderId.current = idNum;
      });

      if (foundNewer) {
        Vibration.vibrate([0, 500, 200, 500, 200, 500]);
        await Notifications.scheduleNotificationAsync({
          content: { title: '\uD83D\uDCE6 New Order!', body: 'Refresh to see new items.', sound: true },
          trigger: null,
        });
      }
      setOrders(newOrders);
      setFetchError(null);
      // Persist fresh data so next load is instant
      try { await AsyncStorage.setItem('cachedOrders', JSON.stringify(newOrders)); } catch (_) {}
    } catch (e) {
      console.log(e);
      setFetchError(e?.message || 'Failed to load orders');
    }
    setLoading(false);
  };

  // ── fetchGlobalPicks (lines 1731-1751) + Improvement 9: fetchError ──────────
  const fetchGlobalPicks = async () => {
    if ((appScreenRef.current || appScreen) !== 'app' || !(tokenRef.current || authToken)) return;
    try {
      const res = await apiFetch(`${API_URL}?action=get_picks`);
      if (res.status === 401) { handleSessionExpired(); return; }
      const text = await res.text();
      let data;
      try { data = JSON.parse(text); } catch (_) { return; }
      setGlobalPicks(prev => {
        const merged = { ...(data || {}) };
        for (const orderId in merged) {
          for (const sku in merged[orderId]) {
            merged[orderId][sku].ts = prev[orderId]?.[sku]?.ts || Date.now();
          }
        }
        return merged;
      });
      setFetchError(null);
    } catch (e) {
      console.log('Error syncing picks', e);
      setFetchError(e?.message || 'Failed to load picks');
    }
  };

  // ── checkLock (lines 1753-1764) -- Bug 4: encodeURIComponent on staffName AND order_id
  const checkLock = async (id) => {
    // Don't fire if staffName isn't set yet -- sending staff='' would match
    // stale lock rows and block the button for no reason.
    if (!staffName) return;
    try {
      const res = await apiFetch(`${API_URL}?action=check_lock&order_id=${encodeURIComponent(id)}&staff=${encodeURIComponent(staffName)}`);
      const data = await res.json();
      setLockInfo(data.locked_by ?? null);
    } catch (e) {
      console.log('checkLock error', e);
    }
  };

  // ── closeOrder (lines 1766-1778) -- Bug 4: encodeURIComponent on staffName AND order_id
  const closeOrder = async () => {
    if (!lockInfo && selectedOrder) {
      try {
        await apiFetch(`${API_URL}?action=release_lock&order_id=${encodeURIComponent(selectedOrder.id)}&staff=${encodeURIComponent(staffName)}`);
      } catch (e) {
        console.log(e);
      }
    }
    setSelectedOrder(null);
    setLockInfo(null);
    setEditingNote('');
    setLocalOrderPicks({});
  };

  // ── saveNote (lines 1780-1792) ──────────────────────────────────────────────
  const saveNote = async () => {
    try {
      await apiFetch(API_URL, {
        method: 'POST',
        body: JSON.stringify({ action: 'add_note', order_id: selectedOrder.id, note: editingNote }),
      });
      setOrders(prev => prev.map(o => (o.id === selectedOrder.id ? { ...o, note: editingNote } : o)));
      setSelectedOrder(prev => (prev ? { ...prev, note: editingNote } : prev));
      Vibration.vibrate(70);
      Alert.alert('Saved', 'Note updated.');
      return true;
    } catch (e) {
      Alert.alert('Error', 'Could not save note.');
      return false;
    }
  };

  // ── confirmPack (lines 1803-1844) ───────────────────────────────────────────
  const confirmPack = () => {
    if (!selectedOrder) return;

    // If order is locked by someone else, show override modal
    if (lockInfo) {
      setShowLockOverrideModal(true);
      setLockOverridePending(false);
      return;
    }

    const allPacked = selectedOrder.items.every(item => (localOrderPicks[item.sku]?.qty || 0) >= item.qty);

    console.log('[confirmPack] entry', {
      orderId: selectedOrder?.id,
      orderName: selectedOrder?.name,
      allPacked,
      packieStatus: selectedOrder?.packie_status,
      isPickup: !!selectedOrder?.is_pickup,
      canPrint: currentUser?.role === 'master' || !!currentUser?.can_print,
      staff: staffName,
    });

    if (!allPacked && settings.missingItemsCheck !== false) {
      // Expo web doesn't reliably show Alert.alert; use window.confirm on web.
      if (Platform.OS === 'web') {
        const ok = window.confirm("You haven't checked off everything in this order. Are you absolutely sure you want to mark it as packed?");
        if (ok) checkPrintAndPack();
        return;
      }

      Alert.alert(
        '\u26A0\uFE0F Missing Items!',
        "You haven't checked off everything in this order. Are you absolutely sure you want to mark it as packed?",
        [
          { text: 'Cancel', style: 'cancel' },
          { text: 'Yes, Proceed', onPress: checkPrintAndPack, style: 'destructive' },
        ]
      );
    } else {
      checkPrintAndPack();
    }
  };

  // ── checkPrintAndPack (lines 1846-1873) ─────────────────────────────────────
  const checkPrintAndPack = (forceOverride = false) => {
    forceOverrideRef.current = forceOverride;
    // Master users always have print access -- can_print is a secondary-user restriction.
    const userCanPrint = currentUser?.role === 'master' || !!currentUser?.can_print;
    const allowRdPrinting = settings.allowRdPrinting !== false;
    const isRural = selectedOrder?.packie_status === 'RURAL';
    const isInternational = isInternationalOrder(selectedOrder);
    const shouldOpenLabelModal = !selectedOrder.is_pickup
      && (isInternational || selectedOrder.packie_status === 'READY' || (isRural && allowRdPrinting))
      && userCanPrint;

    console.log('[checkPrintAndPack] decision', {
      orderId: selectedOrder?.id,
      orderName: selectedOrder?.name,
      packieStatus: selectedOrder?.packie_status,
      allowRdPrinting,
      isPickup: !!selectedOrder?.is_pickup,
      isInternational,
      canPrint: userCanPrint,
      shouldOpenLabelModal,
      availableStocks: packieStocks?.length || 0,
      forceOverride,
    });

    // Show the label/print modal for READY or RURAL orders when user has print permission
    if (shouldOpenLabelModal) {
      const quickStocks = getSortedStocks();
      const liveStocks = getSortedLiveStocks();
      const defaultQuickPick = quickStocks.find(s => s.quick_pick_is_default);
      const defaultA4 = quickStocks.find(s => (s.code || s.package_code) === 'A4' || s.name === 'Satchel A4');
      const fallbackStock = quickStocks[0] || liveStocks[0] || null;
      setSelectedStockId(defaultQuickPick ? defaultQuickPick.id : (defaultA4 ? defaultA4.id : fallbackStock?.id || null));
      setSatchelQuotes([]);
      setLabelModalVisible(true);
    } else {
      handleFinalPackAndPrint(null, null, forceOverride);
    }
  };

  // ── detectShipStatusForOrder (lines 1176-1194) ──────────────────────────────
  const detectShipStatusForOrder = async (order) => {
    const parsed = parseOrderAddress(order);
    try {
      await apiFetch(API_URL, {
        method: 'POST',
        body: JSON.stringify({
          action: 'detect_ship_status',
          order_name: order?.name || '',
          street: parsed.street,
          suburb: parsed.suburb,
          city: parsed.city,
          postcode: parsed.postcode,
          country: parsed.country,
        }),
      });
    } catch (e) {
      console.log('detect_ship_status failed', e);
    }
  };

  // ── handleFinalPackAndPrint (lines 1875-1967) ───────────────────────────────
  const isInternationalOrder = order => {
    const country = String(order?.country || '').trim().toUpperCase();
    return country !== '' && country !== 'NZ';
  };

  const isNonThermalPrintPath = () => {
    const method = String(settings.preferredPrintMethod || 'packie_app');
    const driver = String(settings.localPrinterDriver || '').toLowerCase();
    const printerType = String(settings.printerType || '').toUpperCase();
    return method === 'local' && (driver === 'system' || (driver === 'direct' && printerType === 'LASER'));
  };

  const handleFinalPackAndPrint = async (stockIdToPrint, customRateId = null, forceOverride = false, internationalDetails = null) => {
    setLabelModalVisible(false);
    const orderForSubmit = selectedOrder;
    const oid = orderForSubmit.id;
    const preferredPrintMethod = String(settings.preferredPrintMethod || 'packie_app');
    const isPrintAndPackAttempt = stockIdToPrint !== null && stockIdToPrint !== undefined;
    const userCanPrint = currentUser?.role === 'master' || !!currentUser?.can_print;
    const isInternational = isInternationalOrder(orderForSubmit);

    if (isPrintAndPackAttempt && isInternational && !isNonThermalPrintPath()) {
      Alert.alert(
        'International Label',
        'International labels must be printed on a non-thermal printer. Set Printing to LOCAL PRINTER with SYSTEM or DIRECT LASER before printing this order.'
      );
      setLabelModalVisible(true);
      return;
    }

    let stockIdPayload = stockIdToPrint;
    let customRatePayload = customRateId;
    let customRateProvider = '';
    if (customRateId && typeof customRateId === 'object') {
      customRatePayload = customRateId.id || customRateId.rate_id || customRateId.rateId || null;
      customRateProvider = customRateId.provider || customRateId.provider_slug || '';
    }

    console.log('[handleFinalPackAndPrint] submit', {
      orderId: oid,
      orderName: orderForSubmit?.name,
      stockIdToPrint,
      customRateId,
      preferredPrintMethod,
      staff: staffName,
      customDims,
      forceOverride,
    });

    if (isPrintAndPackAttempt && preferredPrintMethod === 'packie_app') {
      // Do not block packing flow on preflight status refresh.
      detectShipStatusForOrder(orderForSubmit).catch((e) => {
        console.log('detectShipStatus preflight failed', e);
      });
    }

    let markPackedData = null;
    try {
      const res = await apiFetch(API_URL, {
        method: 'POST',
        body: JSON.stringify({
          action: 'mark_packed',
          order_id: oid,
          order_name: orderForSubmit.name,
          staff: staffName,
          stock_id: stockIdPayload,
          rate_id: customRatePayload,
          rate_provider: customRateProvider,
          ui_packie_status: orderForSubmit?.packie_status || '',
          ui_is_pickup: orderForSubmit?.is_pickup ? 1 : 0,
          ui_user_role: currentUser?.role || '',
          ui_user_can_print: userCanPrint ? 1 : 0,
          ui_preferred_print_method: preferredPrintMethod,
          ui_local_printer_driver: settings.localPrinterDriver || '',
          ui_local_printer_type: settings.printerType || '',
          ui_local_print_background: preferredPrintMethod === 'local' ? 1 : 0,
          ui_is_print_attempt: isPrintAndPackAttempt ? 1 : 0,
          w: customDims.w,
          l: customDims.l,
          h: customDims.h,
          weight: customDims.weight,
          force_override: forceOverride ? 1 : 0,
          is_international: isInternational ? 1 : 0,
          international_details: internationalDetails,
        }),
      });

      const text = await res.text();
      try {
        markPackedData = JSON.parse(text);
      } catch (_parseErr) {
        console.error('[mark_packed] non-JSON response:', text);
        // Show the raw server response so user can see HTML/errors coming from server
        Alert.alert('Pack Error', 'Server error:\n' + (text || 'Empty response'));
        return;
      }

      console.log('[mark_packed] response:', JSON.stringify(markPackedData));
      if (!markPackedData?.success) {
        console.warn('[mark_packed] API returned non-success:', JSON.stringify(markPackedData));
        if (markPackedData?.relisted) fetchOrders(true);
        Alert.alert(
          'Label Error',
          markPackedData?.error || 'Courier label creation failed. The order was left in the pick/pack list.'
        );
        return;
      }
    } catch (e) {
      console.error('[mark_packed] fetch error:', e);
      Alert.alert('Pack Error', 'Could not reach server: ' + (e?.message || String(e)));
      return;
    }

    // Move UI back to orders immediately after backend success.
    setIsCustomMode(false);
    setCustomDims({ weight: '', l: '', w: '', h: '' });
    setCustomQuotes([]);
    setSatchelQuotes([]);

    setGlobalPicks(prev => {
      const next = { ...prev };
      delete next[oid];
      return next;
    });
    setSelectedOrder(null);
    setLockInfo(null);
    setLocalOrderPicks({});
    setSelectedIds(prev => prev.filter(id => id !== oid));
    forceOverrideRef.current = false;
    packedOrderIds.current.add(String(oid));
    setOrders(prev => prev.filter(o => String(o.id) !== String(oid)));
    fetchOrders(true);

    // After the consignment is created on the backend, also print locally if configured.
    if (isPrintAndPackAttempt && preferredPrintMethod === 'local') {
      const localPrintOrder = markPackedData?.local_print_label
        ? {
            ...orderForSubmit,
            __packie_label_payload: markPackedData.local_print_label.payload || '',
            __packie_label_encoding: markPackedData.local_print_label.encoding || 'base64',
            __packie_label_pdf_url: markPackedData.local_print_label.pdf_url || '',
            __packie_label_content_type: markPackedData.local_print_label.content_type || 'application/pdf',
          }
        : {
            ...orderForSubmit,
            __packie_label_error: markPackedData?.local_print_error || '',
          };
      printOrderLocally({
        order: localPrintOrder,
        stockId: stockIdToPrint,
        settings,
        viewShotRef,
        setLabelPrintData,
        fetchSenderDetailsFn: () => fetchSenderDetails(handleSessionExpired),
        isTest: false,
        packieStocks,
      }).catch((e) => {
        const message = e?.message || 'Packing succeeded but local print failed.';
        apiFetch(API_URL, {
          method: 'POST',
          body: JSON.stringify({
            action: 'report_label_print_failure',
            order_id: oid,
            order_name: orderForSubmit.name,
            provider: customRateProvider || 'Packie',
            message,
          }),
        })
          .then(() => fetchOrders(true))
          .catch(() => {});
        Alert.alert('Print Error', `${message}\n\nThe order will be re-listed with a note to use desktop printing.`);
      });
    }
  };

  // ── handleLockOverrideConfirm (lines 1969-1972) ─────────────────────────────
  const handleLockOverrideConfirm = () => {
    setShowLockOverrideModal(false);
    checkPrintAndPack(true); // Force override the lock and proceed
  };

  // ── handleGetQuotes (lines 1974-1989) ───────────────────────────────────────
  const handleGetQuotes = async (internationalDetails = null) => {
    if (!customDims.weight || !customDims.l || !customDims.w || !customDims.h) {
      return Alert.alert('Missing Info', 'Please fill in all box dimensions and weight.');
    }
    setIsFetchingQuotes(true);
    try {
      const params = new URLSearchParams({
        action: 'get_custom_quote',
        order_name: selectedOrder.name,
        w: String(customDims.w),
        l: String(customDims.l),
        h: String(customDims.h),
        weight: String(customDims.weight),
      });
      const res = await apiFetch(`${API_URL}?${params.toString()}`, {
        method: internationalDetails ? 'POST' : 'GET',
        body: internationalDetails ? JSON.stringify({ international_details: internationalDetails }) : undefined,
      });
      const data = await res.json();
      const rates = Array.isArray(data) ? data : (data?.rates || data?.result?.rates || []);
      const errorMessage = data?.success === false
        ? (data.error || data.message || 'Packie could not fetch live quotes.')
        : '';
      if (!Array.isArray(rates) || rates.length === 0) {
        setCustomQuotes([]);
        Alert.alert('Quotes', errorMessage || 'Packie returned no live quotes for this box.');
      } else {
        setCustomQuotes(rates);
      }
    } catch (e) {
      Alert.alert('Error', e?.message || 'Could not fetch quotes.');
    }
    setIsFetchingQuotes(false);
  };

  const handleGetSatchelQuotes = async (internationalDetails = null) => {
    if (!selectedStockId) {
      return Alert.alert('Missing Info', 'Please select a stock first.');
    }
    setIsFetchingSatchelQuotes(true);
    try {
      const params = new URLSearchParams({
        action: 'get_stock_quote',
        order_name: selectedOrder.name,
        stock_id: String(selectedStockId),
      });
      const res = await apiFetch(`${API_URL}?${params.toString()}`, {
        method: internationalDetails ? 'POST' : 'GET',
        body: internationalDetails ? JSON.stringify({ international_details: internationalDetails }) : undefined,
      });
      const data = await res.json();
      const rates = Array.isArray(data) ? data : (data?.rates || []);
      const errors = data?.success === false ? [data.error || 'Packie could not fetch live pricing.'] : [];
      setSatchelQuoteProviders(['Packie']);
      setSatchelQuoteErrors(errors);
      if (rates.length === 0) {
        setSatchelQuotes([]);
        Alert.alert('Quotes', errors[0] || 'Could not fetch live pricing.');
      } else {
        setSatchelQuotes(rates);
      }
    } catch (_e) {
      setSatchelQuoteErrors([]);
      setSatchelQuoteProviders([]);
      Alert.alert('Error', 'Could not fetch live pricing.');
    }
    setIsFetchingSatchelQuotes(false);
  };

  // ── handlePickSingle (lines 1991-2011) -- Bug 6: logic inside updater! ──────
  const handlePickSingle = (sku, max, amount = 1) => {
    if (lockInfo) return Alert.alert('Locked', `${lockInfo} is packing this.`);
    setLocalOrderPicks(prev => {
      const current = prev[sku]?.qty || 0;
      if (current >= max) return prev;
      const newQty = Math.min(max, current + amount);
      Vibration.vibrate(70);
      const next = { ...prev, [sku]: { qty: newQty, ts: Date.now() } };
      // Check if order is complete using next state (not stale closure)
      const allItemsComplete = (selectedOrder?.items || []).every(item => {
        const picked = item.sku === sku ? newQty : (next[item.sku]?.qty || 0);
        return picked >= item.qty;
      });
      if (allItemsComplete) {
        playScanSound('complete', settings.enableScanSounds);
      }
      return next;
    });
  };

  // ── handleResetSingle (lines 2013-2021) ─────────────────────────────────────
  const handleResetSingle = (sku) => {
    if (lockInfo) return;
    Vibration.vibrate(50);
    setLocalOrderPicks(prev => {
      const next = { ...prev };
      delete next[sku];
      return next;
    });
  };

  // ── getSortedSingle (lines 2023-2035) ───────────────────────────────────────
  const getSortedSingle = (items) => {
    return [...items].sort((a, b) => {
      const aPick = localOrderPicks[a.sku] || { qty: 0, ts: 0 };
      const bPick = localOrderPicks[b.sku] || { qty: 0, ts: 0 };
      const aDone = aPick.qty >= a.qty;
      const bDone = bPick.qty >= b.qty;

      if (!aDone && bDone) return -1;
      if (aDone && !bDone) return 1;
      if (aDone && bDone) return bPick.ts - aPick.ts;
      return a.name.localeCompare(b.name);
    });
  };

  // ── getSortedStocks (lines 2037-2045) ───────────────────────────────────────
  const getSortedStocks = () => {
    const orderMap = { DL: 1, A5: 2, A4: 3, A3: 4, A2: 5 };
    return [...packieStocks].sort((a, b) => {
      const aCode = a.code || a.package_code || String(a.name || '').match(/\b(DL|A5|A4|A3|A2)\b/i)?.[1]?.toUpperCase();
      const bCode = b.code || b.package_code || String(b.name || '').match(/\b(DL|A5|A4|A3|A2)\b/i)?.[1]?.toUpperCase();
      const aVal = orderMap[aCode] || a.sort_order || 99;
      const bVal = orderMap[bCode] || b.sort_order || 99;
      if (aVal !== bVal) return aVal - bVal;
      return a.name.localeCompare(b.name);
    });
  };

  const getSortedLiveStocks = () => {
    const orderMap = { DL: 1, A5: 2, A4: 3, A3: 4, A2: 5 };
    return [...livePackieStocks].sort((a, b) => {
      const aName = a.name || a.label || a.display_name || '';
      const bName = b.name || b.label || b.display_name || '';
      const aCode = a.code || a.package_code || String(aName).match(/\b(DL|A5|A4|A3|A2)\b/i)?.[1]?.toUpperCase();
      const bCode = b.code || b.package_code || String(bName).match(/\b(DL|A5|A4|A3|A2)\b/i)?.[1]?.toUpperCase();
      const aVal = orderMap[aCode] || a.sort_order || 99;
      const bVal = orderMap[bCode] || b.sort_order || 99;
      if (aVal !== bVal) return aVal - bVal;
      return aName.localeCompare(bName);
    });
  };

  // ── getOutstandingScanItems ──────────────────────────────────────────────────
  const getOutstandingScanItems = () => {
    if (selectedOrder) {
      return getSortedSingle(selectedOrder.items || []).filter(item => {
        const picked = localOrderPicks[item.sku]?.qty || 0;
        return picked < item.qty;
      });
    }
    if (showPickList) {
      return getPickList().filter(item => item.picked < item.qty);
    }
    return [];
  };

  // ── handleBarcodeScanned ─────────────────────────────────────────────────────
  const handleBarcodeScanned = result => {
    const now = Date.now();
    if (now - scanCooldownRef.current < 900) return;
    scanCooldownRef.current = now;

    const scannedSku = String(result?.data || '').trim();
    if (!scannedSku) return;

    let item = null;
    let type = null;

    if (selectedOrder) {
      item = selectedOrder.items.find(i => String(i.sku).trim() === scannedSku);
      type = 'single';
    } else if (showPickList) {
      item = getPickList().find(i => String(i.sku).trim() === scannedSku);
      type = 'master';
    }

    if (!item) {
      Vibration.vibrate(120);
      Alert.alert('Not found', `SKU ${scannedSku} is not in this pick list.`);
      return;
    }

    if (type === 'single') {
      const currentPicked = localOrderPicks[item.sku]?.qty || 0;
      const remaining = Math.max(0, parseInt(item.qty || 1) - currentPicked);
      if (remaining <= 0) { Alert.alert('Already done', `${item.name} is already fully picked.`); return; }
      if (remaining === 1) { handlePickSingle(item.sku, item.qty, 1); return; }
      setScannedItemData({ item, type, remaining });
      setScannedQty(1);
      setQtyModalVisible(true);
      return;
    }

    if (type === 'master') {
      const remaining = Math.max(0, parseInt(item.qty || 1) - parseInt(item.picked || 0));
      if (remaining <= 0) { Alert.alert('Already done', `${item.name} is already fully picked.`); return; }
      if (remaining === 1) { handlePickMaster(item.sku, 1); return; }
      setScannedItemData({ item, type, remaining });
      setScannedQty(1);
      setQtyModalVisible(true);
    }
  };

  // ── handleSearchAddress ──────────────────────────────────────────────────────
  const handleSearchAddress = async () => {
    if (addressSearch.length < 3) return;
    setIsSearchingAddress(true);
    try {
      const res = await apiFetch(`${API_URL}?action=address_autocomplete&term=${encodeURIComponent(addressSearch)}`);
      const data = await res.json();
      const results = data.result || data.results || data.data || data.addresses || [];
      setAddressResults(Array.isArray(results) ? results : []);
    } catch (e) {
      setAddressResults([]);
    }
    setIsSearchingAddress(false);
  };

  // ── handleSelectAddress ──────────────────────────────────────────────────────
  const handleSelectAddress = item => {
    const fullStr     = typeof item === 'string' ? item : (item.full_address || '');
    const apiSuburb   = typeof item === 'object' ? (item.suburb   || '') : '';
    const apiCity     = typeof item === 'object' ? (item.city     || '') : '';
    const apiPostcode = typeof item === 'object' ? (item.postcode || item.postCode || '') : '';

    const parts = fullStr.split(',').map(p => p.trim()).filter(p => p !== '');
    let unit = '', street = '', suburb = apiSuburb, city = apiCity, zip = apiPostcode;

    const lastPart = parts[parts.length - 1] || '';
    if (!zip)  { const m = lastPart.match(/\d{4}$/); zip = m ? m[0] : ''; }
    if (!city) city = lastPart.replace(zip, '').trim();

    const part0 = parts[0] || '';
    const part1 = parts[1] || '';
    const part2 = parts[2] || '';
    const isUnit = /flat|unit|level|shop|u\d|apt|apartment/i.test(part0) || part0.includes('/');

    if (isUnit) {
      unit = part0; street = part1;
      if (!suburb) suburb = part2.toLowerCase() !== city.toLowerCase() ? part2 : '';
    } else {
      street = part0;
      if (!suburb) suburb = part1.toLowerCase() !== city.toLowerCase() ? part1 : '';
    }

    setParsedAddress({
      contactName: selectedOrder?.customer || '',
      company: selectedOrder?.receiver_company || selectedOrder?.company || '',
      building: selectedOrder?.receiver_building || selectedOrder?.building || '',
      phone: selectedOrder?.phone || selectedOrder?.receiver_phone || '',
      email: selectedOrder?.email || selectedOrder?.receiver_email || '',
      unit,
      address1: street,
      address2: suburb,
      city,
      zip,
      notes: selectedOrder?.delivery_notes || selectedOrder?.receiver_notes || '',
    });
    setAddressResults([]);
  };

  // ── handleSaveAddress ────────────────────────────────────────────────────────
  const handleSaveAddress = async () => {
    if (!parsedAddress || !selectedOrder) return;
    setAddressModalVisible(false);
    setParsedAddress(null);
    setAddressSearch('');
    try {
      const res = await apiFetch(API_URL, {
        method: 'POST',
        body: JSON.stringify({
          action: 'update_address',
          order_id: selectedOrder.id,
          order_name: selectedOrder.name,
          ...parsedAddress,
        }),
      });
      const data = await res.json();
      const corrected = data?.corrected_address || '';
      const updatedOrder = {
        ...selectedOrder,
        packie_status: 'PENDING',
        needs_address_check: false,
        corrected_address: corrected,
        address: corrected || selectedOrder.address,
        customer: parsedAddress.contactName || selectedOrder.customer,
        receiver_company: parsedAddress.company || '',
        receiver_building: parsedAddress.building || '',
        phone: parsedAddress.phone || '',
        email: parsedAddress.email || '',
        delivery_notes: parsedAddress.notes || '',
      };
      setSelectedOrder(updatedOrder);
      setOrders(prev => prev.map(o => (o.id === updatedOrder.id ? updatedOrder : o)));
    } catch (e) {
      Alert.alert('Error', 'Could not update address.');
    }
  };

  const getSamplePrintOrder = () => ({
      id: 'FC33E93FCD5A',
      name: '#17115',
      customer: 'Nicole Donaldson',
      original_address: '10B Rambler Crescent, Beach Haven, Beach Haven 0626',
      postcode: '0626',
      country: 'NZ',
      note: '',
      phone: '+64274167766',
  });

  // ── printLocalTestLabel ──────────────────────────────────────────────────────
  const printLocalTestLabel = async () => {
    const sampleOrder = getSamplePrintOrder();
    const ok = await printOrderLocally({ order: sampleOrder, stockId: selectedStockId || null, settings, viewShotRef, setLabelPrintData, fetchSenderDetailsFn: () => fetchSenderDetails(handleSessionExpired), isTest: true, packieStocks });
    if (ok) Alert.alert('Test Print', 'Test label sent to printer.');
  };

  const previewLocalTestLabel = async () => {
    const sampleOrder = getSamplePrintOrder();
    const labelData = await buildLocalLabelData({
      order: sampleOrder,
      stockId: selectedStockId || null,
      fetchSenderDetailsFn: () => fetchSenderDetails(handleSessionExpired),
      isTest: true,
      packieStocks,
    });
    if (!labelData) return;
    setLabelPrintData(labelData);
    setLabelPreviewVisible(true);
  };

  // ── scanForNetworkPrinters ───────────────────────────────────────────────────
  const scanForNetworkPrinters = async () => {
    await scanNetworkPrinters({ settings, setIsScanningPrinters, setDiscoveredPrinters });
  };

  // ── updatePickToServer (lines 2047-2057) ────────────────────────────────────
  const updatePickToServer = async (orderId, sku, newQty) => {
    const now = Date.now();
    setGlobalPicks(prev => ({
      ...prev,
      [orderId]: { ...(prev[orderId] || {}), [sku]: { qty: newQty, by: staffName, ts: now } },
    }));
    await apiFetch(API_URL, {
      method: 'POST',
      body: JSON.stringify({ action: 'update_pick', order_id: orderId, sku: sku, qty: newQty, staff: staffName }),
    });
  };

  // ── handlePickMaster (lines 2059-2089) ──────────────────────────────────────
  const handlePickMaster = (sku, amount = 1) => {
    let remaining = amount;
    let totalPicked = 0;
    for (let oid of selectedIds) {
      if (remaining <= 0) break;
      const order = orders.find(o => o.id === oid);
      if (!order) continue;
      const item = order.items.find(i => i.sku === sku);
      if (!item) continue;

      const needed = parseInt(item.qty || 0);
      const currentPicked = globalPicks[oid]?.[sku]?.qty || 0;

      if (currentPicked < needed) {
        const room = needed - currentPicked;
        const pickingNow = Math.min(room, remaining);
        Vibration.vibrate(70);
        updatePickToServer(oid, sku, currentPicked + pickingNow);
        remaining -= pickingNow;
        totalPicked += pickingNow;
      }
    }
    // Check if pick list is complete
    if (totalPicked > 0) {
      const pickList = getPickList();
      const allComplete = pickList.every(item => item.picked >= item.qty);
      if (allComplete) {
        playScanSound('complete', settings.enableScanSounds);
      }
    }
  };

  // ── handleResetMaster (lines 2091-2098) ─────────────────────────────────────
  const handleResetMaster = (sku) => {
    Vibration.vibrate(50);
    for (let oid of selectedIds) {
      if ((globalPicks[oid]?.[sku]?.qty || 0) > 0) {
        updatePickToServer(oid, sku, 0);
      }
    }
  };

  // ── getPickList (lines 2100-2141) ───────────────────────────────────────────
  const getPickList = () => {
    let agg = {};
    orders
      .filter(o => selectedIds.includes(o.id))
      .forEach(o => {
        o.items.forEach(i => {
          if (!agg[i.sku]) {
            agg[i.sku] = {
              qty: 0,
              picked: 0,
              img: i.img,
              sku: i.sku,
              name: i.name,
              pickedBy: new Set(),
              lastPickTs: 0,
              stock_level: i.stock_level !== undefined ? i.stock_level : null,
            };
          }
          agg[i.sku].qty += parseInt(i.qty || 0);

          const pickData = globalPicks[o.id]?.[i.sku];
          if (pickData && pickData.qty > 0) {
            agg[i.sku].picked += parseInt(pickData.qty);
            if (pickData.by && pickData.by !== staffName) agg[i.sku].pickedBy.add(pickData.by);
            if (pickData.ts && pickData.ts > agg[i.sku].lastPickTs) {
              agg[i.sku].lastPickTs = pickData.ts;
            }
          }
        });
      });

    return Object.values(agg)
      .map(item => ({ ...item, pickedBy: Array.from(item.pickedBy) }))
      .sort((a, b) => {
        const aDone = a.picked >= a.qty;
        const bDone = b.picked >= b.qty;
        if (!aDone && bDone) return -1;
        if (aDone && !bDone) return 1;
        if (aDone && bDone) return b.lastPickTs - a.lastPickTs;
        return a.name.localeCompare(b.name);
      });
  };

  // ═══════════════════════════════════════════════════════════════════════════
  // USE EFFECTS
  // ═══════════════════════════════════════════════════════════════════════════

  // ── Watch authToken -- when it changes to non-null, trigger initial fetches ──
  useEffect(() => {
    if (authToken && appScreen === 'app') {
      syncSettings();
      fetchStaff();
      fetchOrders();
      fetchGlobalPicks();
      fetchStocks();
    } else if (!authToken && appScreen !== 'loading') {
      // Clear all app data on logout / session expiry
      setOrders([]);
      setPackieStocks([]);
      setGlobalPicks({});
      setLocalOrderPicks({});
      setSelectedIds([]);
      setSelectedOrder(null);
    }
  }, [authToken, appScreen]);

  useEffect(() => {
    const mode = settings.securityMode || settings.security_mode;
    if (['master_only', 'shared_honor', 'individual_pin'].includes(mode) && mode !== securityMode) {
      setSecurityMode(mode);
    }
  }, [settings.securityMode, settings.security_mode, securityMode, setSecurityMode]);

  // ── AppState: on foreground resume, trigger a single foreground refresh ──────
  useEffect(() => {
    const subscription = AppState.addEventListener('change', nextAppState => {
      if (appStateRef.current.match(/inactive|background/) && nextAppState === 'active') {
        fetchOrders();
        fetchGlobalPicks();
        const order = selectedOrderRef.current;
        const staff = staffNameRef.current;
        if (order?.id && staff) {
          checkLock(order.id);
        }
      }

      if (appStateRef.current === 'active' && nextAppState.match(/inactive|background/)) {
        const order = selectedOrderRef.current;
        const staff = staffNameRef.current;
        const lockedByOther = lockInfoRef.current;
        if (order?.id && staff && !lockedByOther) {
          apiFetch(`${API_URL}?action=release_lock&order_id=${encodeURIComponent(order.id)}&staff=${encodeURIComponent(staff)}`)
            .catch(() => {});
        }
      }
      appStateRef.current = nextAppState;
    });
    return () => subscription.remove();
  }, []);

  // Keep the order lock fresh with very low network load while the order is open.
  useEffect(() => {
    if (!selectedOrder?.id || !staffName || lockInfo) return undefined;
    const interval = setInterval(() => {
      checkLock(selectedOrder.id);
    }, 120000);
    return () => clearInterval(interval);
  }, [selectedOrder?.id, staffName, lockInfo]);

  // ── Pick list polling (lines 1620-1626) ─────────────────────────────────────
  useEffect(() => {
    let pickInterval;
    if (showPickList === true) {
      pickInterval = setInterval(() => fetchGlobalPicks(), 5000);
    }
    return () => clearInterval(pickInterval);
  }, [showPickList]);

  // ── Fetch providers when settings modal opens ───────────────────────────────
  useEffect(() => {
    if (settingsVisible && (settingsTab === 'courier' || settingsTab === 'ecommerce')) {
      fetchProviders();
    }
  }, [settingsVisible, settingsTab]);

  // ── Selected order effects (lines 1628-1634) ───────────────────────────────
  useEffect(() => {
    if (selectedOrder) {
      setEditingNote(selectedOrder.note || '');
      checkLock(selectedOrder.id);
      setLocalOrderPicks({});
      // Kick off address validation immediately when order is opened if not yet resolved
      const resolved = ['READY', 'RURAL', 'INVALID'];
      if (!selectedOrder.is_pickup && !resolved.includes(selectedOrder.packie_status)) {
        detectShipStatusForOrder(selectedOrder).catch(() => {});
      }
    }
  }, [selectedOrder?.id]);

  // ── Packie status polling (lines 1636-1653) ────────────────────────────────
  useEffect(() => {
    let pollInterval;
    const resolved = ['READY', 'RURAL', 'INVALID'];
    if (selectedOrder && !selectedOrder.is_pickup && !resolved.includes(selectedOrder.packie_status)) {
      pollInterval = setInterval(async () => {
        try {
          const res = await apiFetch(`${API_URL}?action=check_status&order_name=${encodeURIComponent(selectedOrder.name)}`);
          const data = await res.json();
          if (data.packie_status && data.packie_status !== 'PENDING') {
            setSelectedOrder(prev => ({ ...prev, packie_status: data.packie_status }));
            setOrders(prev =>
              prev.map(o => (o.id === selectedOrder.id ? { ...o, packie_status: data.packie_status } : o))
            );
          }
        } catch (e) {}
      }, 5000);
    }
    return () => clearInterval(pollInterval);
  }, [selectedOrder?.packie_status, selectedOrder?.id]);

  // ── Lock override countdown (lines 1655-1668) ──────────────────────────────
  useEffect(() => {
    if (!showLockOverrideModal) return;
    setLockCountdown(10);
    const interval = setInterval(() => {
      setLockCountdown(prev => {
        if (prev <= 1) {
          clearInterval(interval);
          return 0;
        }
        return prev - 1;
      });
    }, 1000);
    return () => clearInterval(interval);
  }, [showLockOverrideModal]);

  // ── Staff list: keep populated for lock screen ──────────────────────────────
  useEffect(() => {
    if (!(tokenRef.current || authToken)) return;
    fetchStaff();
  }, [authToken]);

  // ── Derived / alias values for component compatibility ──────────────────────
  const themeColor = settings.themeColor || '#007a58';
  const masterSettings = settings;
  const updateMasterSetting = updateSetting;

  // ── Derived order values ─────────────────────────────────────────────────────
  const duplicateNames = {};
  const duplicateAddresses = {};
  orders.forEach(o => {
    const nameKey = (o.customer || '').trim().toLowerCase();
    const addrKey = (o.address || '').trim().toLowerCase();
    if (nameKey.length > 2) duplicateNames[nameKey] = (duplicateNames[nameKey] || 0) + 1;
    if (addrKey.length > 5 && !o.is_pickup) duplicateAddresses[addrKey] = (duplicateAddresses[addrKey] || 0) + 1;
  });

  const isPending = selectedOrder?.packie_status === 'PENDING' && !selectedOrder?.is_pickup;
  const isAUOrder = masterSettings.showInternationalWarning !== false && selectedOrder?.country === 'AU';

  const isMissingSku = skuValue => {
    const sku = String(skuValue || '').trim().toLowerCase();
    return sku === '' || sku.startsWith('no sku #') || sku === 'no sku';
  };
  const selectedOrderHasMissingSku = (selectedOrder?.items || []).some(i => isMissingSku(i?.sku));
  const selectedOrderScannerAllowed = selectedOrder?.scanner_allowed !== false && !selectedOrderHasMissingSku;
  const masterPickListHasMissingSku = getPickList().some(item => isMissingSku(item?.sku));
  const masterPickScannerAllowed = !masterPickListHasMissingSku;

  const activeShippingConfig = getProviderConfig('shipping', selectedShipProvider);
  const activeEcommerceConfig = getProviderConfig('ecommerce', selectedEcomProvider);
  const activeShippingDefs = (() => {
    const defs = shippingProviders.find(p => p.slug === selectedShipProvider)?.required_fields || [];
    if (selectedShipProvider !== 'zappy') return defs;
    const hasKey = key => defs.some(f => String(f?.key || '').toLowerCase() === key);
    const next = defs.map(f => {
      if (String(f?.key || '').toLowerCase() === 'api_key') {
        return { ...f, label: 'Zappy Password', type: 'password', example: '' };
      }
      return f;
    });
    if (!hasKey('client_id')) {
      next.push({ key: 'client_id', label: 'Zappy Client ID', type: 'text', example: '2879' });
    }
    return next;
  })();
  const activeEcommerceDefs = cmsProviders.find(p => p.slug === selectedEcomProvider)?.required_fields || [];
  const hasShippingSecret = !!(activeShippingConfig.__has_api_key || activeShippingConfig.__has_api_secret || activeShippingConfig.__has_bearer_token);
  const hasEcommerceSecret = !!(activeEcommerceConfig.__has_api_key || activeEcommerceConfig.__has_api_secret || activeEcommerceConfig.__has_bearer_token);

  const openSettingsTab = (tabId) => {
    if ((tabId === 'printing' || tabId === 'courier' || tabId === 'ecommerce') && !isMasterUser) {
      Alert.alert('Settings', 'Sign in as Admin to view printing, courier and ecommerce settings.');
      return;
    }
    setSettingsTab(tabId);
  };

  const settingsTabs = [
    ['general', 'General'],
    ['users', 'Users'],
    ...(isMasterUser ? [['printing', 'Printing'], ['courier', 'Courier'], ['ecommerce', 'Ecommerce']] : []),
  ];

  // ═══════════════════════════════════════════════════════════════════════════
  // CONTEXT VALUE
  // ═══════════════════════════════════════════════════════════════════════════

  const value = {
    // Order data
    orders, setOrders,
    packieStocks, setPackieStocks,
    livePackieStocks, setLivePackieStocks,
    selectedIds, setSelectedIds,
    showPickList, setShowPickList,
    loading, setLoading,
    selectedOrder, setSelectedOrder,
    globalPicks, setGlobalPicks,
    localOrderPicks, setLocalOrderPicks,
    fetchError, setFetchError,
    // Lock
    lockInfo, setLockInfo,
    showLockOverrideModal, setShowLockOverrideModal,
    lockCountdown, setLockCountdown,
    lockOverridePending, setLockOverridePending,
    // Note
    editingNote, setEditingNote,
    // Staff
    staffList, setStaffList,
    staffDirty, setStaffDirty,
    newStaffName, setNewStaffName,
    secondaryPassword, setSecondaryPassword,
    // Provider / settings
    shippingProviders, setShippingProviders,
    cmsProviders, setCmsProviders,
    selectedShipProvider, setSelectedShipProvider,
    selectedEcomProvider, setSelectedEcomProvider,
    providerFieldInputs, setProviderFieldInputs,
    providerConfigs, setProviderConfigs,
    showProviderInputs, setShowProviderInputs,
    forceUpdatingCourier, setForceUpdatingCourier,
    packageMappingVisible, setPackageMappingVisible,
    packageMappingProvider, setPackageMappingProvider,
    packageMapping, setPackageMapping,
    packageStockOptions, setPackageStockOptions,
    packageMappingLoading, setPackageMappingLoading,
    packageMappingSaving, setPackageMappingSaving,
    fetchPackageMapping,
    setPackageMappingStock,
    setPackageStockSetting,
    savePackageMapping,
    // Print / label / quote
    discoveredPrinters, setDiscoveredPrinters,
    isScanningPrinters, setIsScanningPrinters,
    printLocalTestLabel, previewLocalTestLabel, scanForNetworkPrinters,
    labelPreviewVisible, setLabelPreviewVisible,
    labelModalVisible, setLabelModalVisible,
    selectedStockId, setSelectedStockId,
    isCustomMode, setIsCustomMode,
    customDims, setCustomDims,
    customQuotes, setCustomQuotes,
    satchelQuotes, setSatchelQuotes,
    satchelQuoteErrors, setSatchelQuoteErrors,
    satchelQuoteProviders, setSatchelQuoteProviders,
    isFetchingQuotes, setIsFetchingQuotes,
    isFetchingSatchelQuotes, setIsFetchingSatchelQuotes,
    labelPrintData, setLabelPrintData,
    // Settings (from useSettings hook)
    settings, updateSetting, syncSettings, settingsLoaded,
    // Auth pass-throughs (for components that import only useApp)
    staffName, tempName, setTempName, showLogin, setShowLogin, securityMode, authCompany,
    isMasterUser, isSecondaryUser, effectiveCanPick, effectiveCanPack, effectiveCanPrint,
    currentUser, handleSessionExpired,
    // Derived / aliases
    themeColor, masterSettings, updateMasterSetting,
    // UI modal state
    settingsVisible, setSettingsVisible,
    settingsTab, setSettingsTab,
    openSettingsTab, settingsTabs,
    accountVisible, setAccountVisible,
    adminVisible, setAdminVisible,
    colorPickerVisible, setColorPickerVisible,
    tempR, setTempR,
    tempG, setTempG,
    tempB, setTempB,
    // Refs
    highestOrderId,
    forceOverrideRef,
    viewShotRef,
    // Functions
    fetchStaff,
    fetchProviders,
    providerInputKey,
    getProviderInput,
    setProviderInput,
    getProviderConfig,
    isSensitiveProviderField,
    activeShippingConfig, activeEcommerceConfig,
    activeShippingDefs, activeEcommerceDefs,
    hasShippingSecret, hasEcommerceSecret,
    saveStaffChanges,
    saveProviderConfig,
    forceUpdateCourier,
    addStaff,
    saveSecondaryPassword,
    deleteStaff,
    updateStaffPermission,
    fetchStocks,
    fetchOrders,
    fetchGlobalPicks,
    checkLock,
    closeOrder,
    saveNote,
    confirmPack,
    checkPrintAndPack,
    handleFinalPackAndPrint,
    handleLockOverrideConfirm,
    handleGetQuotes,
    handleGetSatchelQuotes,
    handlePickSingle,
    handleResetSingle,
    getSortedSingle,
    getSortedStocks,
    getSortedLiveStocks,
    updatePickToServer,
    handlePickMaster,
    handleResetMaster,
    getPickList,
    detectShipStatusForOrder,
    // Scanner
    isScanning, setIsScanning,
    cameraFacing, setCameraFacing,
    qtyModalVisible, setQtyModalVisible,
    scannedItemData, setScannedItemData,
    scannedQty, setScannedQty,
    getOutstandingScanItems,
    handleBarcodeScanned,
    selectedOrderScannerAllowed,
    masterPickScannerAllowed,
    // Address modal
    addressModalVisible, setAddressModalVisible,
    addressSearch, setAddressSearch,
    addressResults, setAddressResults,
    isSearchingAddress, setIsSearchingAddress,
    parsedAddress, setParsedAddress,
    handleSearchAddress, handleSelectAddress, handleSaveAddress,
    // Derived order values
    duplicateNames, duplicateAddresses,
    isPending, isAUOrder,
  };

  return <AppContext.Provider value={value}>{children}</AppContext.Provider>;
}
