BEGIN;

-- =====================================================================================
-- Delivery homecare action RPCs
-- Purpose: �?delivery 端补�?rpc_delivery_* 契约，优先兼�?ec/hc 新链，回退 hss 旧链�?
-- =====================================================================================

CREATE OR REPLACE FUNCTION public.delivery_current_user_id()
RETURNS UUID
LANGUAGE sql
STABLE
SECURITY DEFINER
SET search_path = public
AS $$
    SELECT u.id
    FROM public.ak_users u
    WHERE u.auth_id = auth.uid()
    LIMIT 1
$$;

CREATE OR REPLACE FUNCTION public.delivery_current_staff_id()
RETURNS UUID
LANGUAGE sql
STABLE
SECURITY DEFINER
SET search_path = public
AS $$
    SELECT s.id
    FROM public.ml_delivery_staff s
    WHERE s.uid = public.delivery_current_user_id()
      AND s.deleted_at IS NULL
    ORDER BY COALESCE(s.updated_at, s.created_at) DESC, s.created_at DESC
    LIMIT 1
$$;

CREATE OR REPLACE FUNCTION public.delivery_table_exists(p_table_name TEXT)
RETURNS BOOLEAN
LANGUAGE sql
STABLE
SET search_path = public
AS $$
    SELECT to_regclass('public.' || p_table_name) IS NOT NULL
$$;

CREATE OR REPLACE FUNCTION public.delivery_front_status(p_normalized_status TEXT, p_raw JSONB)
RETURNS TEXT
LANGUAGE plpgsql
IMMUTABLE
AS $$
BEGIN
    IF p_normalized_status = 'assigned' THEN
        RETURN 'pending_assignment';
    END IF;
    IF p_normalized_status = 'accepted' THEN
        IF COALESCE(p_raw ->> 'departed_at', '') <> '' THEN
            RETURN 'departed';
        END IF;
        RETURN 'accepted';
    END IF;
    IF p_normalized_status = 'departed' THEN
        RETURN 'departed';
    END IF;
    IF p_normalized_status = 'arrived' THEN
        RETURN 'arrived';
    END IF;
    IF p_normalized_status = 'in_service' THEN
        RETURN 'in_service';
    END IF;
    IF p_normalized_status = 'pending_acceptance' THEN
        RETURN 'pending_acceptance';
    END IF;
    IF p_normalized_status IN ('reviewed', 'accepted_by_user', 'settled', 'completed') THEN
        RETURN 'completed';
    END IF;
    IF p_normalized_status = 'rejected' THEN
        RETURN 'rejected';
    END IF;
    IF p_normalized_status = 'cancelled' THEN
        RETURN 'cancelled';
    END IF;
    IF p_normalized_status = 'exception' THEN
        RETURN 'abnormal';
    END IF;
    RETURN 'pending_assignment';
END;
$$;

CREATE OR REPLACE FUNCTION public.delivery_status_text(p_front_status TEXT)
RETURNS TEXT
LANGUAGE plpgsql
IMMUTABLE
AS $$
BEGIN
    CASE p_front_status
        WHEN 'pending_assignment' THEN RETURN '待接�?;
        WHEN 'pending_accept' THEN RETURN '待接�?;
        WHEN 'accepted' THEN RETURN '待出�?;
        WHEN 'waiting_departure' THEN RETURN '待出�?;
        WHEN 'departed' THEN RETURN '已出�?;
        WHEN 'on_the_way' THEN RETURN '途中';
        WHEN 'arrived' THEN RETURN '待签�?;
        WHEN 'checked_in' THEN RETURN '已签�?;
        WHEN 'in_service' THEN RETURN '服务�?;
        WHEN 'serving' THEN RETURN '服务�?;
        WHEN 'pending_confirm' THEN RETURN '待确�?;
        WHEN 'pending_submit' THEN RETURN '待提�?;
        WHEN 'pending_acceptance' THEN RETURN '待验�?;
        WHEN 'completed' THEN RETURN '已完�?;
        WHEN 'rejected' THEN RETURN '已拒�?;
        WHEN 'abnormal' THEN RETURN '异常处理�?;
        WHEN 'cancelled' THEN RETURN '已取�?;
        WHEN 'settled' THEN RETURN '已结�?;
        WHEN 'archived' THEN RETURN '已归�?;
        ELSE RETURN '待接�?;
    END CASE;
END;
$$;

CREATE OR REPLACE FUNCTION public.delivery_status_tone(p_front_status TEXT)
RETURNS TEXT
LANGUAGE plpgsql
IMMUTABLE
AS $$
BEGIN
    IF p_front_status IN ('pending_assignment', 'pending_accept', 'pending_acceptance', 'pending_confirm', 'pending_submit') THEN
        RETURN 'warning';
    END IF;
    IF p_front_status IN ('accepted', 'waiting_departure', 'departed', 'on_the_way', 'arrived', 'checked_in', 'in_service', 'serving') THEN
        RETURN 'primary';
    END IF;
    IF p_front_status IN ('completed', 'settled', 'archived') THEN
        RETURN 'success';
    END IF;
    IF p_front_status IN ('rejected', 'cancelled', 'abnormal', 'exception_pending', 'terminated') THEN
        RETURN 'danger';
    END IF;
    RETURN 'warning';
END;
$$;

CREATE OR REPLACE FUNCTION public.delivery_default_service_items(p_order_id TEXT, p_service_name TEXT)
RETURNS JSONB
LANGUAGE sql
IMMUTABLE
AS $$
    SELECT jsonb_build_array(
        jsonb_build_object('id', p_order_id || '-item-1', 'name', '上门签到确认', 'required', true, 'completed', false, 'incompleteReason', '', 'remark', '', 'updatedAt', ''),
        jsonb_build_object('id', p_order_id || '-item-2', 'name', CASE WHEN position('护理' IN COALESCE(p_service_name, '')) > 0 THEN '基础护理执行' WHEN position('随访' IN COALESCE(p_service_name, '')) > 0 THEN '慢病指标采集' ELSE '服务项目执行' END, 'required', true, 'completed', false, 'incompleteReason', '', 'remark', '', 'updatedAt', ''),
        jsonb_build_object('id', p_order_id || '-item-3', 'name', '服务过程记录', 'required', true, 'completed', false, 'incompleteReason', '', 'remark', '', 'updatedAt', ''),
        jsonb_build_object('id', p_order_id || '-item-4', 'name', '家属沟通反�?, 'required', true, 'completed', false, 'incompleteReason', '', 'remark', '', 'updatedAt', '')
    )
$$;

CREATE OR REPLACE FUNCTION public.delivery_build_timeline(p_logs JSONB)
RETURNS JSONB
LANGUAGE sql
IMMUTABLE
AS $$
    SELECT COALESCE(
        (
            SELECT jsonb_agg(
                jsonb_build_object(
                    'id', COALESCE(item ->> 'id', md5(COALESCE(item::TEXT, ''))),
                    'title', public.delivery_status_text(
                        public.delivery_front_status(
                            CASE
                                WHEN COALESCE(item ->> 'to_status', item ->> 'status', '') IN ('ORDER_ACCEPTED') THEN 'accepted'
                                WHEN COALESCE(item ->> 'to_status', item ->> 'status', '') IN ('ORDER_CHECKED_IN') THEN 'arrived'
                                WHEN COALESCE(item ->> 'to_status', item ->> 'status', '') IN ('ORDER_IN_SERVICE') THEN 'in_service'
                                WHEN COALESCE(item ->> 'to_status', item ->> 'status', '') IN ('ACCEPTANCE_PENDING') THEN 'pending_acceptance'
                                WHEN COALESCE(item ->> 'to_status', item ->> 'status', '') IN ('ORDER_EXCEPTION') THEN 'exception'
                                WHEN COALESCE(item ->> 'to_status', item ->> 'status', '') IN ('ORDER_REJECTED') THEN 'rejected'
                                ELSE lower(COALESCE(item ->> 'to_status', item ->> 'status', 'assigned'))
                            END,
                            item
                        )
                    ),
                    'time', COALESCE(item ->> 'created_at', ''),
                    'description', COALESCE(item ->> 'remark', '')
                )
                ORDER BY COALESCE(item ->> 'created_at', '') DESC
            )
            FROM jsonb_array_elements(COALESCE(p_logs, '[]'::jsonb)) item
        ),
        '[]'::jsonb
    )
$$;

CREATE OR REPLACE FUNCTION public.delivery_build_status_logs(p_logs JSONB, p_order_id TEXT)
RETURNS JSONB
LANGUAGE sql
IMMUTABLE
AS $$
    SELECT COALESCE(
        (
            SELECT jsonb_agg(
                jsonb_build_object(
                    'id', COALESCE(item ->> 'id', md5(COALESCE(item::TEXT, ''))),
                    'orderId', p_order_id,
                    'fromStatus', public.delivery_front_status(lower(COALESCE(item ->> 'from_status', 'assigned')), item),
                    'toStatus', public.delivery_front_status(
                        CASE
                            WHEN COALESCE(item ->> 'to_status', item ->> 'status', '') IN ('ORDER_ACCEPTED') THEN 'accepted'
                            WHEN COALESCE(item ->> 'to_status', item ->> 'status', '') IN ('ORDER_CHECKED_IN') THEN 'arrived'
                            WHEN COALESCE(item ->> 'to_status', item ->> 'status', '') IN ('ORDER_IN_SERVICE') THEN 'in_service'
                            WHEN COALESCE(item ->> 'to_status', item ->> 'status', '') IN ('ACCEPTANCE_PENDING') THEN 'pending_acceptance'
                            WHEN COALESCE(item ->> 'to_status', item ->> 'status', '') IN ('ORDER_EXCEPTION') THEN 'exception'
                            WHEN COALESCE(item ->> 'to_status', item ->> 'status', '') IN ('ORDER_REJECTED') THEN 'rejected'
                            ELSE lower(COALESCE(item ->> 'to_status', item ->> 'status', 'assigned'))
                        END,
                        item
                    ),
                    'operatorRole', COALESCE(item ->> 'operator_role', item ->> 'actor_role', ''),
                    'operatorId', COALESCE(item ->> 'operator_id', item ->> 'actor_id', ''),
                    'remark', COALESCE(item ->> 'remark', ''),
                    'createdAt', COALESCE(item ->> 'created_at', '')
                )
                ORDER BY COALESCE(item ->> 'created_at', '') DESC
            )
            FROM jsonb_array_elements(COALESCE(p_logs, '[]'::jsonb)) item
        ),
        '[]'::jsonb
    )
$$;

CREATE OR REPLACE FUNCTION public.delivery_build_evidence(p_evidence JSONB, p_order_id TEXT)
RETURNS JSONB
LANGUAGE sql
IMMUTABLE
AS $$
    SELECT COALESCE(
        (
            SELECT jsonb_agg(
                jsonb_build_object(
                    'id', COALESCE(item ->> 'id', md5(COALESCE(item::TEXT, ''))),
                    'orderId', p_order_id,
                    'phase', COALESCE(item ->> 'phase', 'service'),
                    'fileType', COALESCE(NULLIF(item ->> 'file_type', ''), 'image'),
                    'name', COALESCE(NULLIF(item ->> 'storage_path', ''), item ->> 'file_url', ''),
                    'url', COALESCE(item ->> 'file_url', ''),
                    'localPath', '',
                    'status', 'success',
                    'progress', 100,
                    'retryable', false,
                    'createdAt', COALESCE(item ->> 'created_at', '')
                )
                ORDER BY COALESCE(item ->> 'created_at', '') DESC
            )
            FROM jsonb_array_elements(COALESCE(p_evidence, '[]'::jsonb)) item
        ),
        '[]'::jsonb
    )
$$;

CREATE OR REPLACE FUNCTION public.delivery_build_abnormal(p_exception JSONB, p_order_id TEXT)
RETURNS JSONB
LANGUAGE sql
IMMUTABLE
AS $$
    SELECT CASE
        WHEN p_exception IS NULL THEN NULL
        ELSE jsonb_build_object(
            'id', COALESCE(p_exception ->> 'id', ''),
            'orderId', p_order_id,
            'type', COALESCE(p_exception ->> 'exception_type', 'other'),
            'description', COALESCE(p_exception ->> 'description', p_exception ->> 'remark', ''),
            'occurredAt', COALESCE(p_exception ->> 'occurred_at', ''),
            'locationText', COALESCE(p_exception ->> 'location_text', ''),
            'images', COALESCE(p_exception -> 'images_json', '[]'::jsonb),
            'needPlatformIntervention', COALESCE((p_exception ->> 'need_platform_intervention')::BOOLEAN, false),
            'requestCancelOrder', COALESCE((p_exception ->> 'request_cancel_order')::BOOLEAN, false),
            'requestReschedule', COALESCE((p_exception ->> 'request_reschedule')::BOOLEAN, false),
            'createdAt', COALESCE(p_exception ->> 'created_at', '')
        )
    END
$$;

CREATE OR REPLACE FUNCTION public.delivery_build_order_json(
    p_raw JSONB,
    p_logs JSONB DEFAULT '[]'::jsonb,
    p_records JSONB DEFAULT '[]'::jsonb,
    p_evidence JSONB DEFAULT '[]'::jsonb,
    p_exception JSONB DEFAULT NULL,
    p_source TEXT DEFAULT 'legacy'
)
RETURNS JSONB
LANGUAGE plpgsql
IMMUTABLE
AS $$
DECLARE
    v_service JSONB := COALESCE(p_raw -> 'service_snapshot_json', jsonb_build_object('category', COALESCE(p_raw ->> 'service_category', ''), 'price', COALESCE((p_raw ->> 'service_price')::NUMERIC, 0)));
    v_address JSONB := COALESCE(p_raw -> 'address_snapshot_json', '{}'::jsonb);
    v_raw_status TEXT := COALESCE(p_raw ->> 'status', 'assigned');
    v_normalized_status TEXT;
    v_front_status TEXT;
    v_checkin_record JSONB;
    v_service_record JSONB;
    v_service_items JSONB;
    v_record_json JSONB;
    v_timeline JSONB;
    v_status_logs JSONB;
    v_evidence_list JSONB;
BEGIN
    IF p_source = 'care' THEN
        IF COALESCE(p_raw ->> 'accepted_by_family_at', '') <> '' THEN
            v_normalized_status := 'completed';
        ELSIF COALESCE(p_raw ->> 'acceptance_pending_at', '') <> '' THEN
            v_normalized_status := 'pending_acceptance';
        ELSIF COALESCE(p_raw ->> 'service_started_at', '') <> '' THEN
            v_normalized_status := 'in_service';
        ELSIF COALESCE(p_raw ->> 'checked_in_at', '') <> '' THEN
            v_normalized_status := 'arrived';
        ELSIF COALESCE(p_raw ->> 'departed_at', '') <> '' THEN
            v_normalized_status := 'departed';
        ELSIF COALESCE(p_raw ->> 'accepted_at', '') <> '' THEN
            v_normalized_status := 'accepted';
        ELSE
            v_normalized_status := CASE v_raw_status
                WHEN 'ORDER_ACCEPTED' THEN 'accepted'
                WHEN 'ORDER_CHECKED_IN' THEN 'arrived'
                WHEN 'ORDER_IN_SERVICE' THEN 'in_service'
                WHEN 'ACCEPTANCE_PENDING' THEN 'pending_acceptance'
                WHEN 'ORDER_EXCEPTION' THEN 'exception'
                WHEN 'ORDER_REJECTED' THEN 'rejected'
                WHEN 'ORDER_CANCELLED' THEN 'cancelled'
                WHEN 'ORDER_COMPLETED' THEN 'completed'
                ELSE 'assigned'
            END;
        END IF;
    ELSE
        v_normalized_status := lower(v_raw_status);
    END IF;

    v_front_status := public.delivery_front_status(v_normalized_status, p_raw);
    v_timeline := public.delivery_build_timeline(p_logs);
    v_status_logs := public.delivery_build_status_logs(p_logs, COALESCE(p_raw ->> 'id', ''));
    v_evidence_list := public.delivery_build_evidence(p_evidence, COALESCE(p_raw ->> 'id', ''));

    SELECT item INTO v_checkin_record
    FROM jsonb_array_elements(COALESCE(p_records, '[]'::jsonb)) item
    WHERE COALESCE(item ->> 'record_type', item ->> 'care_record_type', '') = 'checkin'
    ORDER BY COALESCE(item ->> 'created_at', '') DESC
    LIMIT 1;

    SELECT item INTO v_service_record
    FROM jsonb_array_elements(COALESCE(p_records, '[]'::jsonb)) item
    WHERE COALESCE(item ->> 'record_type', item ->> 'care_record_type', '') <> 'checkin'
      AND COALESCE(item ->> 'record_type', item ->> 'care_record_type', '') <> 'review'
    ORDER BY COALESCE(item ->> 'created_at', '') DESC
    LIMIT 1;

    IF v_service_record IS NULL THEN
        v_service_record := v_checkin_record;
    END IF;

    v_service_items := COALESCE(v_service_record -> 'service_items_json', public.delivery_default_service_items(COALESCE(p_raw ->> 'id', ''), COALESCE(p_raw ->> 'service_name', '')));

    IF v_service_record IS NULL THEN
        v_record_json := NULL;
    ELSE
        v_record_json := jsonb_build_object(
            'id', COALESCE(v_service_record ->> 'id', ''),
            'orderId', COALESCE(p_raw ->> 'id', ''),
            'startTime', COALESCE(v_service_record ->> 'started_at', v_service_record ->> 'service_started_at', ''),
            'endTime', COALESCE(v_service_record ->> 'finished_at', v_service_record ->> 'service_finished_at', ''),
            'actualDurationMinutes', COALESCE((v_service_record ->> 'duration_minutes')::INTEGER, (v_service_record ->> 'actual_duration_minutes')::INTEGER, 0),
            'serviceItems', COALESCE(v_service_items, '[]'::jsonb),
            'serviceContent', COALESCE(
                (
                    SELECT jsonb_agg(elem ->> 'name')
                    FROM jsonb_array_elements(COALESCE(v_service_items, '[]'::jsonb)) elem
                    WHERE COALESCE((elem ->> 'completed')::BOOLEAN, false)
                ),
                '[]'::jsonb
            ),
            'processNote', COALESCE(v_service_record ->> 'summary', v_service_record ->> 'content', ''),
            'elderStatus', '',
            'healthMetrics', jsonb_build_object('bloodPressure', '', 'heartRate', '', 'bloodSugar', '', 'bloodOxygen', ''),
            'materialsUsed', '',
            'abnormalNote', '',
            'photos', COALESCE(
                (
                    SELECT jsonb_agg(item ->> 'file_url')
                    FROM jsonb_array_elements(COALESCE(p_evidence, '[]'::jsonb)) item
                    WHERE COALESCE(item ->> 'phase', '') = 'service'
                ),
                '[]'::jsonb
            ),
            'staffRemark', COALESCE(v_service_record ->> 'remark', ''),
            'familyConfirmation', jsonb_build_object('method', 'none', 'code', '', 'signatureName', '', 'signatureUrl', '', 'confirmedAt', ''),
            'createdAt', COALESCE(v_service_record ->> 'created_at', ''),
            'updatedAt', COALESCE(v_service_record ->> 'updated_at', '')
        );
    END IF;

    RETURN jsonb_build_object(
        'id', COALESCE(p_raw ->> 'id', ''),
        'orderNo', COALESCE(NULLIF(p_raw ->> 'task_no', ''), p_raw ->> 'order_no', ''),
        'serviceType', COALESCE(NULLIF(v_service ->> 'category', ''), '居家服务'),
        'serviceName', COALESCE(p_raw ->> 'service_name', ''),
        'serviceCategory', COALESCE(v_service ->> 'category', ''),
        'serviceItems', COALESCE(v_service_items, '[]'::jsonb),
        'elderId', COALESCE(p_raw ->> 'elder_id', p_raw ->> 'user_id', ''),
        'elderName', COALESCE(NULLIF(p_raw ->> 'recipient_name', ''), p_raw ->> 'elder_name', ''),
        'elderNameMasked', COALESCE(NULLIF(p_raw ->> 'recipient_name', ''), p_raw ->> 'elder_name', ''),
        'elderGender', '',
        'elderAge', 0,
        'elderPhone', COALESCE(NULLIF(p_raw ->> 'recipient_phone', ''), p_raw ->> 'elder_phone', ''),
        'elderPhoneMasked', COALESCE(NULLIF(p_raw ->> 'recipient_phone', ''), p_raw ->> 'elder_phone', ''),
        'fullElderName', COALESCE(NULLIF(p_raw ->> 'recipient_name', ''), p_raw ->> 'elder_name', ''),
        'fullPhone', COALESCE(NULLIF(p_raw ->> 'recipient_phone', ''), p_raw ->> 'elder_phone', ''),
        'contactRelation', '家属',
        'addressSummary', COALESCE(NULLIF(v_address ->> 'fullAddress', ''), NULLIF(v_address ->> 'full_address', ''), NULLIF(v_address ->> 'address', ''), ''),
        'address', COALESCE(NULLIF(v_address ->> 'fullAddress', ''), NULLIF(v_address ->> 'full_address', ''), NULLIF(v_address ->> 'address', ''), ''),
        'addressDetail', COALESCE(NULLIF(v_address ->> 'detailAddress', ''), NULLIF(v_address ->> 'detail_address', ''), ''),
        'fullAddress', COALESCE(NULLIF(v_address ->> 'fullAddress', ''), NULLIF(v_address ->> 'full_address', ''), NULLIF(v_address ->> 'address', ''), ''),
        'latitude', COALESCE((v_address ->> 'latitude')::NUMERIC, 0),
        'longitude', COALESCE((v_address ->> 'longitude')::NUMERIC, 0),
        'appointmentTime', COALESCE(NULLIF(p_raw ->> 'appointment_time', ''), p_raw ->> 'scheduled_at', ''),
        'appointmentStartTime', COALESCE(NULLIF(p_raw ->> 'appointment_time', ''), p_raw ->> 'scheduled_at', ''),
        'appointmentEndTime', COALESCE(NULLIF(p_raw ->> 'appointment_time', ''), p_raw ->> 'scheduled_at', ''),
        'duration', COALESCE((p_raw ->> 'duration_minutes')::INTEGER, 90),
        'estimatedDuration', COALESCE((p_raw ->> 'duration_minutes')::INTEGER, 90),
        'price', COALESCE((v_service ->> 'price')::NUMERIC, 0),
        'staffIncome', COALESCE((v_service ->> 'price')::NUMERIC, 0),
        'distance', '',
        'actualStartTime', COALESCE(p_raw ->> 'service_started_at', ''),
        'actualEndTime', COALESCE(NULLIF(p_raw ->> 'completed_at', ''), p_raw ->> 'service_completed_at', ''),
        'status', v_front_status,
        'statusText', public.delivery_status_text(v_front_status),
        'statusTone', public.delivery_status_tone(v_front_status),
        'riskTags', '[]'::jsonb,
        'healthTags', '[]'::jsonb,
        'careLevel', COALESCE(v_service ->> 'category', ''),
        'needFamilyPresent', false,
        'needMaterials', false,
        'remark', COALESCE(p_raw ->> 'remark', ''),
        'merchantId', COALESCE(p_raw ->> 'merchant_id', ''),
        'merchantName', COALESCE(p_raw ->> 'merchant_name', ''),
        'deliveryStaffId', COALESCE(NULLIF(p_raw ->> 'current_staff_id', ''), p_raw ->> 'assigned_to', ''),
        'deliveryStaffName', COALESCE(p_raw ->> 'delivery_staff_name', ''),
        'acceptTime', COALESCE(p_raw ->> 'accepted_at', ''),
        'departTime', COALESCE(p_raw ->> 'departed_at', ''),
        'arriveTime', COALESCE(NULLIF(p_raw ->> 'arrived_at', ''), p_raw ->> 'checked_in_at', ''),
        'checkinTime', COALESCE(NULLIF(p_raw ->> 'checked_in_at', ''), p_raw ->> 'arrived_at', ''),
        'startServiceTime', COALESCE(p_raw ->> 'service_started_at', ''),
        'finishTime', COALESCE(NULLIF(p_raw ->> 'completed_at', ''), p_raw ->> 'service_completed_at', ''),
        'cancelReason', COALESCE(p_raw ->> 'cancel_reason', ''),
        'exceptionType', COALESCE(p_exception ->> 'exception_type', ''),
        'exceptionDesc', COALESCE(p_exception ->> 'description', p_exception ->> 'remark', ''),
        'evidenceList', COALESCE(v_evidence_list, '[]'::jsonb),
        'signatureUrl', '',
        'signatureName', '',
        'satisfactionStatus', CASE WHEN v_front_status = 'pending_acceptance' THEN '待验�? WHEN v_front_status = 'completed' THEN '已验�? ELSE '待评�? END,
        'settlementStatus', CASE WHEN v_front_status = 'completed' THEN '待结�? ELSE '待确�? END,
        'archiveStatus', '未归�?,
        'createdAt', COALESCE(p_raw ->> 'created_at', ''),
        'updatedAt', COALESCE(p_raw ->> 'updated_at', ''),
        'contactName', COALESCE(p_raw ->> 'contact_name', ''),
        'contactPhone', COALESCE(p_raw ->> 'contact_phone', ''),
        'notices', '[]'::jsonb,
        'timeline', COALESCE(v_timeline, '[]'::jsonb),
        'statusLog', COALESCE(v_status_logs, '[]'::jsonb),
        'serviceSummary', COALESCE(v_service_record ->> 'summary', v_service_record ->> 'content', ''),
        'progressNote', COALESCE(v_service_record ->> 'remark', ''),
        'distanceKm', '',
        'allowCheckinRadiusMeters', 100,
        'lastLocation', CASE
            WHEN v_checkin_record IS NULL THEN NULL
            ELSE jsonb_build_object(
                'latitude', COALESCE((v_checkin_record ->> 'latitude')::NUMERIC, (v_checkin_record ->> 'checkin_latitude')::NUMERIC, 0),
                'longitude', COALESCE((v_checkin_record ->> 'longitude')::NUMERIC, (v_checkin_record ->> 'checkin_longitude')::NUMERIC, 0),
                'address', COALESCE(v_checkin_record ->> 'location_text', v_checkin_record ->> 'checkin_address', ''),
                'time', COALESCE(v_checkin_record ->> 'checked_in_at', v_checkin_record ->> 'checkin_time', '')
            )
        END,
        'trackPoints', COALESCE(v_service_record -> 'track_points_json', '[]'::jsonb),
        'serviceRecord', v_record_json,
        'abnormalReport', public.delivery_build_abnormal(p_exception, COALESCE(p_raw ->> 'id', ''))
    );
END;
$$;

CREATE OR REPLACE FUNCTION public.delivery_get_legacy_order_json(p_order_id TEXT)
RETURNS JSONB
LANGUAGE plpgsql
STABLE
SECURITY DEFINER
SET search_path = public
AS $$
DECLARE
    v_staff_id UUID := public.delivery_current_staff_id();
    v_raw JSONB;
    v_logs JSONB := '[]'::jsonb;
    v_records JSONB := '[]'::jsonb;
    v_evidence JSONB := '[]'::jsonb;
BEGIN
    SELECT to_jsonb(o)
    INTO v_raw
    FROM public.hss_service_orders o
    WHERE o.id = p_order_id
      AND o.deleted_at IS NULL
      AND (v_staff_id IS NULL OR o.current_staff_id = v_staff_id)
    LIMIT 1;

    IF v_raw IS NULL THEN
        RETURN NULL;
    END IF;

    SELECT COALESCE(jsonb_agg(to_jsonb(l) ORDER BY l.created_at DESC), '[]'::jsonb)
    INTO v_logs
    FROM public.hss_service_order_status_logs l
    WHERE l.order_id = p_order_id;

    SELECT COALESCE(jsonb_agg(to_jsonb(r) ORDER BY r.created_at DESC), '[]'::jsonb)
    INTO v_records
    FROM public.hss_service_execution_records r
    WHERE r.order_id = p_order_id;

    SELECT COALESCE(jsonb_agg(to_jsonb(e) ORDER BY e.created_at DESC), '[]'::jsonb)
    INTO v_evidence
    FROM public.hss_service_evidence_files e
    WHERE e.order_id = p_order_id;

    RETURN public.delivery_build_order_json(v_raw, v_logs, v_records, v_evidence, NULL, 'legacy');
END;
$$;

CREATE OR REPLACE FUNCTION public.delivery_get_care_order_json(p_order_id TEXT)
RETURNS JSONB
LANGUAGE plpgsql
STABLE
SECURITY DEFINER
SET search_path = public
AS $$
DECLARE
    v_user_id UUID := public.delivery_current_user_id();
    v_raw JSONB;
    v_logs JSONB := '[]'::jsonb;
    v_records JSONB := '[]'::jsonb;
    v_evidence JSONB := '[]'::jsonb;
    v_exception JSONB;
BEGIN
    IF NOT public.delivery_table_exists('ec_care_tasks') THEN
        RETURN NULL;
    END IF;

    BEGIN
        EXECUTE 'SELECT to_jsonb(t) FROM public.ec_care_tasks t WHERE t.id = $1::uuid AND ($2 IS NULL OR t.assigned_to = $2) LIMIT 1'
        INTO v_raw
        USING p_order_id, v_user_id;
    EXCEPTION WHEN OTHERS THEN
        RETURN NULL;
    END;

    IF v_raw IS NULL THEN
        RETURN NULL;
    END IF;

    BEGIN
        IF public.delivery_table_exists('hc_work_order_events') THEN
            EXECUTE 'SELECT COALESCE(jsonb_agg(to_jsonb(e) ORDER BY e.created_at DESC), ''[]''::jsonb) FROM public.hc_work_order_events e WHERE e.task_id = $1::uuid'
            INTO v_logs
            USING p_order_id;
        END IF;
        IF public.delivery_table_exists('ec_care_records') THEN
            EXECUTE 'SELECT COALESCE(jsonb_agg(to_jsonb(r) ORDER BY r.created_at DESC), ''[]''::jsonb) FROM public.ec_care_records r WHERE r.task_id = $1::uuid'
            INTO v_records
            USING p_order_id;
        END IF;
        IF public.delivery_table_exists('hc_evidence_files') THEN
            EXECUTE 'SELECT COALESCE(jsonb_agg(to_jsonb(f) ORDER BY f.created_at DESC), ''[]''::jsonb) FROM public.hc_evidence_files f WHERE f.task_id = $1::uuid'
            INTO v_evidence
            USING p_order_id;
        END IF;
        IF public.delivery_table_exists('hc_work_order_exceptions') THEN
            EXECUTE 'SELECT to_jsonb(x) FROM public.hc_work_order_exceptions x WHERE x.task_id = $1::uuid ORDER BY x.created_at DESC LIMIT 1'
            INTO v_exception
            USING p_order_id;
        END IF;
    EXCEPTION WHEN OTHERS THEN
        NULL;
    END;

    RETURN public.delivery_build_order_json(v_raw, v_logs, v_records, v_evidence, v_exception, 'care');
END;
$$;

CREATE OR REPLACE FUNCTION public.delivery_get_order_json(p_order_id TEXT)
RETURNS JSONB
LANGUAGE plpgsql
STABLE
SECURITY DEFINER
SET search_path = public
AS $$
DECLARE
    v_order JSONB;
BEGIN
    v_order := public.delivery_get_care_order_json(p_order_id);
    IF v_order IS NOT NULL THEN
        RETURN v_order;
    END IF;
    RETURN public.delivery_get_legacy_order_json(p_order_id);
END;
$$;

CREATE OR REPLACE FUNCTION public.delivery_order_matches(p_order JSONB, p_tab TEXT, p_keyword TEXT)
RETURNS BOOLEAN
LANGUAGE plpgsql
IMMUTABLE
AS $$
DECLARE
    v_status TEXT := COALESCE(p_order ->> 'status', 'pending_assignment');
    v_keyword TEXT := btrim(COALESCE(p_keyword, ''));
BEGIN
    IF COALESCE(p_tab, '') NOT IN ('', 'all') THEN
        CASE p_tab
            WHEN 'pending_accept' THEN
                IF v_status NOT IN ('pending_assignment', 'pending_accept') THEN RETURN FALSE; END IF;
            WHEN 'accepted' THEN
                IF v_status NOT IN ('accepted', 'waiting_departure', 'departed', 'on_the_way', 'arrived') THEN RETURN FALSE; END IF;
            WHEN 'serving' THEN
                IF v_status NOT IN ('in_service', 'serving') THEN RETURN FALSE; END IF;
            WHEN 'pending_submit' THEN
                IF v_status NOT IN ('pending_confirm', 'pending_submit', 'pending_acceptance') THEN RETURN FALSE; END IF;
            WHEN 'completed' THEN
                IF v_status NOT IN ('completed', 'settled', 'archived') THEN RETURN FALSE; END IF;
            WHEN 'exception' THEN
                IF v_status NOT IN ('abnormal', 'exception_pending') THEN RETURN FALSE; END IF;
        END CASE;
    END IF;

    IF v_keyword <> '' THEN
        IF COALESCE(p_order ->> 'orderNo', '') NOT ILIKE '%' || v_keyword || '%'
           AND COALESCE(p_order ->> 'serviceName', '') NOT ILIKE '%' || v_keyword || '%'
           AND COALESCE(p_order ->> 'elderNameMasked', '') NOT ILIKE '%' || v_keyword || '%'
           AND COALESCE(p_order ->> 'addressSummary', '') NOT ILIKE '%' || v_keyword || '%' THEN
            RETURN FALSE;
        END IF;
    END IF;

    RETURN TRUE;
END;
$$;

CREATE OR REPLACE FUNCTION public.delivery_append_if_match(p_result JSONB, p_order JSONB, p_tab TEXT, p_keyword TEXT)
RETURNS JSONB
LANGUAGE plpgsql
IMMUTABLE
AS $$
BEGIN
    IF p_order IS NULL THEN
        RETURN COALESCE(p_result, '[]'::jsonb);
    END IF;
    IF public.delivery_order_matches(p_order, p_tab, p_keyword) THEN
        RETURN COALESCE(p_result, '[]'::jsonb) || jsonb_build_array(p_order);
    END IF;
    RETURN COALESCE(p_result, '[]'::jsonb);
END;
$$;

CREATE OR REPLACE FUNCTION public.delivery_build_staff_info(p_staff_id UUID)
RETURNS JSONB
LANGUAGE sql
STABLE
SECURITY DEFINER
SET search_path = public
AS $$
    SELECT jsonb_build_object(
        'id', s.id,
        'userId', COALESCE(s.uid::TEXT, ''),
        'staffNo', COALESCE(NULLIF(s.staff_no, ''), 'DEL-' || upper(substr(s.id::TEXT, 1, 8))),
        'name', COALESCE(NULLIF(s.nickname, ''), '服务人员'),
        'phone', COALESCE(s.phone, ''),
        'role', 'delivery',
        'status', CASE WHEN s.status = 0 OR COALESCE(s.is_active, true) = false THEN 'disabled' ELSE 'active' END,
        'organizationId', COALESCE(s.station_id::TEXT, ''),
        'organizationName', COALESCE(st.name, COALESCE(s.station_id::TEXT, '')),
        'certificates', '[]'::jsonb,
        'certificateStatus', CASE WHEN COALESCE(s.certificate_status, '') IN ('valid', 'expired') THEN s.certificate_status ELSE 'pending' END,
        'certificateExpireAt', COALESCE(to_char(s.certificate_expire_at, 'YYYY-MM-DD'), ''),
        'onlineStatus', CASE WHEN s.online_status IN ('online', 'busy') THEN s.online_status ELSE 'resting' END,
        'serviceArea', COALESCE(s.service_area, ''),
        'skills', CASE WHEN COALESCE(s.skills, '') = '' THEN '[]'::jsonb ELSE COALESCE(s.skills::jsonb, '[]'::jsonb) END,
        'avatarUrl', COALESCE(s.avatar, ''),
        'todayAccepted', 0,
        'todayServing', 0,
        'todayCompleted', 0,
        'usesMock', false
    )
    FROM public.ml_delivery_staff s
    LEFT JOIN public.ml_delivery_stations st ON st.id = s.station_id
    WHERE s.id = p_staff_id
      AND s.deleted_at IS NULL
    LIMIT 1
$$;

CREATE OR REPLACE FUNCTION public.delivery_assert_staff_access(p_staff_id UUID)
RETURNS VOID
LANGUAGE plpgsql
STABLE
SECURITY DEFINER
SET search_path = public
AS $$
DECLARE
    v_current_staff_id UUID := public.delivery_current_staff_id();
BEGIN
    IF auth.uid() IS NULL THEN
        RAISE EXCEPTION 'Permission denied';
    END IF;
    IF p_staff_id IS NOT NULL AND v_current_staff_id IS NOT NULL AND p_staff_id <> v_current_staff_id THEN
        RAISE EXCEPTION 'Permission denied';
    END IF;
END;
$$;

CREATE OR REPLACE FUNCTION public.delivery_try_update_care_task(p_order_id TEXT, p_status TEXT, p_patch JSONB, p_action TEXT, p_remark TEXT)
RETURNS JSONB
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
AS $$
DECLARE
    v_user_id UUID := public.delivery_current_user_id();
    v_now TIMESTAMPTZ := now();
BEGIN
    IF NOT public.delivery_table_exists('ec_care_tasks') THEN
        RETURN NULL;
    END IF;

    BEGIN
        EXECUTE $sql$
            UPDATE public.ec_care_tasks
            SET status = COALESCE($2, status),
                accepted_at = COALESCE(($3 ->> 'accepted_at')::timestamptz, accepted_at),
                departed_at = COALESCE(($3 ->> 'departed_at')::timestamptz, departed_at),
                checked_in_at = COALESCE(($3 ->> 'checked_in_at')::timestamptz, checked_in_at),
                service_started_at = COALESCE(($3 ->> 'service_started_at')::timestamptz, service_started_at),
                service_completed_at = COALESCE(($3 ->> 'service_completed_at')::timestamptz, service_completed_at),
                acceptance_pending_at = COALESCE(($3 ->> 'acceptance_pending_at')::timestamptz, acceptance_pending_at),
                reject_reason = COALESCE($3 ->> 'reject_reason', reject_reason),
                updated_at = COALESCE(($3 ->> 'updated_at')::timestamptz, $4)
                        WHERE id = $1::uuid
              AND ($5 IS NULL OR assigned_to = $5)
        $sql$
        USING p_order_id, p_status, COALESCE(p_patch, '{}'::jsonb), v_now, v_user_id;
    EXCEPTION WHEN OTHERS THEN
        RETURN NULL;
    END;

    BEGIN
        IF public.delivery_table_exists('hc_work_order_events') THEN
            EXECUTE $sql$
                INSERT INTO public.hc_work_order_events(id, task_id, from_status, to_status, actor_id, actor_role, action, remark, created_at)
                VALUES (md5(random()::text || clock_timestamp()::text), $1::uuid, NULL, $2, $3, 'delivery', $4, $5, $6)
            $sql$
            USING p_order_id, p_status, v_user_id, COALESCE(p_action, ''), COALESCE(p_remark, ''), v_now;
        END IF;
    EXCEPTION WHEN OTHERS THEN
        NULL;
    END;

    RETURN public.delivery_get_care_order_json(p_order_id);
END;
$$;

CREATE OR REPLACE FUNCTION public.rpc_delivery_order_detail(p_order_id TEXT)
RETURNS JSONB
LANGUAGE plpgsql
STABLE
SECURITY DEFINER
SET search_path = public
AS $$
BEGIN
    IF auth.uid() IS NULL THEN
        RAISE EXCEPTION 'Permission denied';
    END IF;
    RETURN public.delivery_get_order_json(p_order_id);
END;
$$;

CREATE OR REPLACE FUNCTION public.rpc_delivery_order_list(
    p_staff_id UUID DEFAULT NULL,
    p_tab TEXT DEFAULT 'all',
    p_keyword TEXT DEFAULT ''
)
RETURNS JSONB
LANGUAGE plpgsql
STABLE
SECURITY DEFINER
SET search_path = public
AS $$
DECLARE
    v_user_id UUID := public.delivery_current_user_id();
    v_staff_id UUID := COALESCE(p_staff_id, public.delivery_current_staff_id());
    v_result JSONB := '[]'::jsonb;
    v_task JSONB;
    v_order JSONB;
    v_order_id TEXT;
BEGIN
    PERFORM public.delivery_assert_staff_access(v_staff_id);

    IF public.delivery_table_exists('ec_care_tasks') THEN
        BEGIN
            FOR v_task IN EXECUTE 'SELECT to_jsonb(t) FROM public.ec_care_tasks t WHERE ($1 IS NULL OR t.assigned_to = $1) ORDER BY t.created_at DESC'
            USING v_user_id
            LOOP
                v_order := public.delivery_get_care_order_json(v_task ->> 'id');
                v_result := public.delivery_append_if_match(v_result, v_order, p_tab, p_keyword);
            END LOOP;
        EXCEPTION WHEN OTHERS THEN
            NULL;
        END;
    END IF;

    FOR v_order_id IN
        SELECT o.id
        FROM public.hss_service_orders o
        WHERE o.deleted_at IS NULL
          AND (v_staff_id IS NULL OR o.current_staff_id = v_staff_id)
        ORDER BY COALESCE(o.updated_at, o.created_at) DESC, o.created_at DESC
    LOOP
        v_order := public.delivery_get_legacy_order_json(v_order_id);
        v_result := public.delivery_append_if_match(v_result, v_order, p_tab, p_keyword);
    END LOOP;

    RETURN COALESCE(v_result, '[]'::jsonb);
END;
$$;

CREATE OR REPLACE FUNCTION public.rpc_delivery_dashboard(p_staff_id UUID DEFAULT NULL)
RETURNS JSONB
LANGUAGE plpgsql
STABLE
SECURITY DEFINER
SET search_path = public
AS $$
DECLARE
    v_staff_id UUID := COALESCE(p_staff_id, public.delivery_current_staff_id());
    v_orders JSONB := public.rpc_delivery_order_list(v_staff_id, 'all', '');
    v_item JSONB;
    v_pending_assignment_count INTEGER := 0;
    v_pending_accept_count INTEGER := 0;
    v_today_order_count INTEGER := 0;
    v_pending_depart_count INTEGER := 0;
    v_serving_count INTEGER := 0;
    v_completed_count INTEGER := 0;
    v_exception_count INTEGER := 0;
    v_next_order JSONB;
BEGIN
    PERFORM public.delivery_assert_staff_access(v_staff_id);

    FOR v_item IN SELECT value FROM jsonb_array_elements(COALESCE(v_orders, '[]'::jsonb))
    LOOP
        IF COALESCE(v_item ->> 'status', '') IN ('pending_assignment', 'pending_accept') THEN
            v_pending_assignment_count := v_pending_assignment_count + 1;
            v_pending_accept_count := v_pending_accept_count + 1;
        END IF;
        IF COALESCE(v_item ->> 'status', '') IN ('accepted', 'waiting_departure', 'departed', 'on_the_way', 'arrived') THEN
            v_pending_depart_count := v_pending_depart_count + 1;
        END IF;
        IF COALESCE(v_item ->> 'status', '') IN ('pending_assignment', 'pending_accept', 'accepted', 'waiting_departure', 'departed', 'on_the_way', 'arrived', 'in_service', 'serving', 'pending_acceptance') THEN
            v_today_order_count := v_today_order_count + 1;
        END IF;
        IF COALESCE(v_item ->> 'status', '') IN ('in_service', 'serving') THEN
            v_serving_count := v_serving_count + 1;
        END IF;
        IF COALESCE(v_item ->> 'status', '') IN ('completed', 'pending_acceptance', 'settled', 'archived') THEN
            v_completed_count := v_completed_count + 1;
        END IF;
        IF COALESCE(v_item ->> 'status', '') IN ('abnormal', 'exception_pending') THEN
            v_exception_count := v_exception_count + 1;
        END IF;
        IF v_next_order IS NULL AND COALESCE(v_item ->> 'status', '') NOT IN ('completed', 'settled', 'archived', 'cancelled', 'rejected', 'abnormal') THEN
            v_next_order := v_item;
        END IF;
    END LOOP;

    RETURN jsonb_build_object(
        'pendingAssignmentCount', v_pending_assignment_count,
        'pendingAcceptCount', v_pending_accept_count,
        'todayOrderCount', v_today_order_count,
        'pendingDepartCount', v_pending_depart_count,
        'servingCount', v_serving_count,
        'completedCount', v_completed_count,
        'exceptionCount', v_exception_count,
        'expectedIncome', 0,
        'onlineStatus', COALESCE(public.delivery_build_staff_info(v_staff_id) ->> 'onlineStatus', 'resting'),
        'nextOrder', v_next_order,
        'recentOrders', COALESCE((SELECT jsonb_agg(value) FROM (SELECT value FROM jsonb_array_elements(COALESCE(v_orders, '[]'::jsonb)) LIMIT 5) q), '[]'::jsonb)
    );
END;
$$;

CREATE OR REPLACE FUNCTION public.rpc_delivery_accept_order(p_order_id TEXT)
RETURNS JSONB
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
AS $$
DECLARE
    v_now TIMESTAMPTZ := now();
    v_staff_id UUID := public.delivery_current_staff_id();
BEGIN
    IF public.delivery_get_care_order_json(p_order_id) IS NOT NULL THEN
        RETURN public.delivery_try_update_care_task(
            p_order_id,
            'ORDER_ACCEPTED',
            jsonb_build_object('accepted_at', v_now, 'updated_at', v_now),
            'accept_task',
            '服务人员接单'
        );
    END IF;

    UPDATE public.hss_service_assignments
    SET status = 'accepted', accepted_at = v_now, updated_at = v_now
    WHERE order_id = p_order_id
      AND (v_staff_id IS NULL OR staff_id = v_staff_id);

    UPDATE public.hss_service_orders
    SET status = 'accepted', accepted_at = v_now, updated_at = v_now
    WHERE id = p_order_id
      AND deleted_at IS NULL
      AND (v_staff_id IS NULL OR current_staff_id = v_staff_id);

    INSERT INTO public.hss_service_order_status_logs(id, order_id, from_status, to_status, operator_id, operator_role, remark, created_at)
    VALUES (md5(random()::text || clock_timestamp()::text), p_order_id, 'assigned', 'accepted', public.delivery_current_user_id(), 'delivery', '服务人员接单', v_now);

    RETURN public.delivery_get_legacy_order_json(p_order_id);
END;
$$;

CREATE OR REPLACE FUNCTION public.rpc_delivery_reject_order(p_order_id TEXT, p_reason TEXT DEFAULT '')
RETURNS JSONB
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
AS $$
DECLARE
    v_now TIMESTAMPTZ := now();
    v_staff_id UUID := public.delivery_current_staff_id();
    v_reason TEXT := COALESCE(p_reason, '');
BEGIN
    IF public.delivery_get_care_order_json(p_order_id) IS NOT NULL THEN
        RETURN public.delivery_try_update_care_task(
            p_order_id,
            'ORDER_REJECTED',
            jsonb_build_object('reject_reason', v_reason, 'updated_at', v_now),
            'reject_task',
            CASE WHEN v_reason = '' THEN '服务人员拒单' ELSE v_reason END
        );
    END IF;

    UPDATE public.hss_service_assignments
    SET status = 'rejected', rejected_at = v_now, reject_reason = v_reason, updated_at = v_now
    WHERE order_id = p_order_id
      AND (v_staff_id IS NULL OR staff_id = v_staff_id);

    UPDATE public.hss_service_orders
    SET status = 'rejected', updated_at = v_now
    WHERE id = p_order_id
      AND deleted_at IS NULL
      AND (v_staff_id IS NULL OR current_staff_id = v_staff_id);

    INSERT INTO public.hss_service_order_status_logs(id, order_id, from_status, to_status, operator_id, operator_role, remark, created_at)
    VALUES (md5(random()::text || clock_timestamp()::text), p_order_id, 'assigned', 'rejected', public.delivery_current_user_id(), 'delivery', CASE WHEN v_reason = '' THEN '服务人员拒单' ELSE v_reason END, v_now);

    RETURN public.delivery_get_legacy_order_json(p_order_id);
END;
$$;

CREATE OR REPLACE FUNCTION public.rpc_delivery_start_depart(p_order_id TEXT, p_location JSONB DEFAULT '{}'::jsonb)
RETURNS JSONB
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
AS $$
DECLARE
    v_now TIMESTAMPTZ := now();
    v_staff_id UUID := public.delivery_current_staff_id();
    v_remark TEXT := CASE WHEN COALESCE(p_location ->> 'address', '') = '' THEN '服务人员出发' ELSE '服务人员出发�? || (p_location ->> 'address') END;
BEGIN
    IF public.delivery_get_care_order_json(p_order_id) IS NOT NULL THEN
        RETURN public.delivery_try_update_care_task(
            p_order_id,
            'ORDER_ACCEPTED',
            jsonb_build_object('departed_at', v_now, 'updated_at', v_now),
            'depart_task',
            v_remark
        );
    END IF;

    UPDATE public.hss_service_orders
    SET status = 'departed', departed_at = v_now, updated_at = v_now
    WHERE id = p_order_id
      AND deleted_at IS NULL
      AND (v_staff_id IS NULL OR current_staff_id = v_staff_id);

    INSERT INTO public.hss_service_order_status_logs(id, order_id, from_status, to_status, operator_id, operator_role, remark, created_at)
    VALUES (md5(random()::text || clock_timestamp()::text), p_order_id, 'accepted', 'departed', public.delivery_current_user_id(), 'delivery', v_remark, v_now);

    RETURN public.delivery_get_legacy_order_json(p_order_id);
END;
$$;

CREATE OR REPLACE FUNCTION public.rpc_delivery_arrive_order(p_order_id TEXT, p_location JSONB DEFAULT '{}'::jsonb)
RETURNS JSONB
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
AS $$
DECLARE
    v_now TIMESTAMPTZ := now();
    v_staff_id UUID := public.delivery_current_staff_id();
    v_remark TEXT := CASE WHEN COALESCE(p_location ->> 'address', '') = '' THEN '服务人员到达' ELSE '服务人员到达�? || (p_location ->> 'address') END;
BEGIN
    IF public.delivery_get_care_order_json(p_order_id) IS NOT NULL THEN
        RETURN public.delivery_try_update_care_task(
            p_order_id,
            'ORDER_CHECKED_IN',
            jsonb_build_object('checked_in_at', v_now, 'updated_at', v_now),
            'arrive_task',
            v_remark
        );
    END IF;

    UPDATE public.hss_service_orders
    SET status = 'arrived', arrived_at = v_now, updated_at = v_now
    WHERE id = p_order_id
      AND deleted_at IS NULL
      AND (v_staff_id IS NULL OR current_staff_id = v_staff_id);

    INSERT INTO public.hss_service_order_status_logs(id, order_id, from_status, to_status, operator_id, operator_role, remark, created_at)
    VALUES (md5(random()::text || clock_timestamp()::text), p_order_id, 'departed', 'arrived', public.delivery_current_user_id(), 'delivery', v_remark, v_now);

    RETURN public.delivery_get_legacy_order_json(p_order_id);
END;
$$;

CREATE OR REPLACE FUNCTION public.rpc_delivery_checkin_order(p_order_id TEXT, p_payload JSONB DEFAULT '{}'::jsonb)
RETURNS JSONB
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
AS $$
DECLARE
    v_now TIMESTAMPTZ := now();
    v_staff_id UUID := public.delivery_current_staff_id();
    v_assignment_id TEXT := '';
    v_record_id TEXT := 'ser-' || replace(gen_random_uuid()::TEXT, '-', '');
    v_file TEXT;
BEGIN
    IF public.delivery_get_care_order_json(p_order_id) IS NOT NULL THEN
        BEGIN
            IF public.delivery_table_exists('ec_care_records') THEN
                EXECUTE $sql$
                    INSERT INTO public.ec_care_records(id, task_id, record_type, started_at, checked_in_at, latitude, longitude, location_text, remark, created_at, updated_at)
                    VALUES ($1, $2::uuid, 'checkin', $3, $3, ($4 -> 'location' ->> 'latitude')::double precision, ($4 -> 'location' ->> 'longitude')::double precision, COALESCE($4 -> 'location' ->> 'address', ''), COALESCE($4 ->> 'note', ''), $3, $3)
                $sql$
                USING 'care-checkin-' || replace(gen_random_uuid()::TEXT, '-', ''), p_order_id, v_now, COALESCE(p_payload, '{}'::jsonb);
            END IF;
            IF public.delivery_table_exists('hc_evidence_files') THEN
                FOR v_file IN SELECT jsonb_array_elements_text(COALESCE(p_payload -> 'photos', '[]'::jsonb))
                LOOP
                    EXECUTE $sql$
                        INSERT INTO public.hc_evidence_files(id, task_id, care_record_id, phase, file_type, storage_path, file_url, latitude, longitude, captured_at, created_at)
                        VALUES ($1, $2::uuid, NULL, 'checkin', 'image', $3, $3, ($4 -> 'location' ->> 'latitude')::double precision, ($4 -> 'location' ->> 'longitude')::double precision, $5, $5)
                    $sql$
                    USING 'hc-ef-' || replace(gen_random_uuid()::TEXT, '-', ''), p_order_id, v_file, COALESCE(p_payload, '{}'::jsonb), v_now;
                END LOOP;
            END IF;
        EXCEPTION WHEN OTHERS THEN
            NULL;
        END;

        RETURN public.delivery_try_update_care_task(
            p_order_id,
            'ORDER_CHECKED_IN',
            jsonb_build_object('checked_in_at', v_now, 'updated_at', v_now),
            'checkin_task',
            COALESCE(NULLIF(p_payload ->> 'note', ''), '到场签到')
        );
    END IF;

    SELECT COALESCE(a.id, '') INTO v_assignment_id
    FROM public.hss_service_assignments a
    WHERE a.order_id = p_order_id
      AND (v_staff_id IS NULL OR a.staff_id = v_staff_id)
    ORDER BY a.created_at DESC
    LIMIT 1;

    INSERT INTO public.hss_service_execution_records(
        id, order_id, assignment_id, checkin_time, checkin_latitude, checkin_longitude, checkin_address, remark, created_at, updated_at
    ) VALUES (
        v_record_id, p_order_id, v_assignment_id, v_now,
        COALESCE((p_payload -> 'location' ->> 'latitude')::double precision, 0),
        COALESCE((p_payload -> 'location' ->> 'longitude')::double precision, 0),
        COALESCE(p_payload -> 'location' ->> 'address', ''),
        COALESCE(p_payload ->> 'note', ''),
        v_now, v_now
    ) ON CONFLICT (id) DO UPDATE SET
        checkin_time = EXCLUDED.checkin_time,
        checkin_latitude = EXCLUDED.checkin_latitude,
        checkin_longitude = EXCLUDED.checkin_longitude,
        checkin_address = EXCLUDED.checkin_address,
        remark = EXCLUDED.remark,
        updated_at = EXCLUDED.updated_at;

    FOR v_file IN SELECT jsonb_array_elements_text(COALESCE(p_payload -> 'photos', '[]'::jsonb))
    LOOP
        INSERT INTO public.hss_service_evidence_files(id, order_id, execution_record_id, phase, file_type, storage_path, file_url, latitude, longitude, captured_at, created_at)
        VALUES (
            'sef-' || replace(gen_random_uuid()::TEXT, '-', ''),
            p_order_id,
            v_record_id,
            'checkin',
            'image',
            v_file,
            v_file,
            COALESCE((p_payload -> 'location' ->> 'latitude')::double precision, 0),
            COALESCE((p_payload -> 'location' ->> 'longitude')::double precision, 0),
            v_now,
            v_now
        );
    END LOOP;

    RETURN public.delivery_get_legacy_order_json(p_order_id);
END;
$$;

CREATE OR REPLACE FUNCTION public.rpc_delivery_start_service(p_order_id TEXT)
RETURNS JSONB
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
AS $$
DECLARE
    v_now TIMESTAMPTZ := now();
    v_staff_id UUID := public.delivery_current_staff_id();
BEGIN
    IF public.delivery_get_care_order_json(p_order_id) IS NOT NULL THEN
        RETURN public.delivery_try_update_care_task(
            p_order_id,
            'ORDER_IN_SERVICE',
            jsonb_build_object('service_started_at', v_now, 'updated_at', v_now),
            'start_service',
            '开始服�?
        );
    END IF;

    UPDATE public.hss_service_execution_records
    SET service_started_at = v_now, updated_at = v_now
    WHERE order_id = p_order_id;

    UPDATE public.hss_service_orders
    SET status = 'in_service', service_started_at = v_now, updated_at = v_now
    WHERE id = p_order_id
      AND deleted_at IS NULL
      AND (v_staff_id IS NULL OR current_staff_id = v_staff_id);

    INSERT INTO public.hss_service_order_status_logs(id, order_id, from_status, to_status, operator_id, operator_role, remark, created_at)
    VALUES (md5(random()::text || clock_timestamp()::text), p_order_id, 'arrived', 'in_service', public.delivery_current_user_id(), 'delivery', '开始服�?, v_now);

    RETURN public.delivery_get_legacy_order_json(p_order_id);
END;
$$;

CREATE OR REPLACE FUNCTION public.rpc_delivery_save_progress(p_order_id TEXT, p_payload JSONB DEFAULT '{}'::jsonb)
RETURNS JSONB
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
AS $$
DECLARE
    v_now TIMESTAMPTZ := now();
    v_staff_id UUID := public.delivery_current_staff_id();
    v_assignment_id TEXT := '';
    v_record_id TEXT := 'progress-' || replace(gen_random_uuid()::TEXT, '-', '');
BEGIN
    IF public.delivery_get_care_order_json(p_order_id) IS NOT NULL THEN
        BEGIN
            IF public.delivery_table_exists('ec_care_records') THEN
                EXECUTE $sql$
                    INSERT INTO public.ec_care_records(id, task_id, record_type, started_at, finished_at, duration_minutes, service_items_json, summary, remark, created_at, updated_at)
                    VALUES ($1, $2::uuid, 'service', now(), NULL, 0, COALESCE($3 -> 'items', '[]'::jsonb), COALESCE($3 ->> 'serviceSummary', ''), COALESCE($3 ->> 'progressNote', ''), $4, $4)
                    ON CONFLICT (id) DO UPDATE SET
                        service_items_json = EXCLUDED.service_items_json,
                        summary = EXCLUDED.summary,
                        remark = EXCLUDED.remark,
                        updated_at = EXCLUDED.updated_at
                $sql$
                USING v_record_id, p_order_id, COALESCE(p_payload, '{}'::jsonb), v_now;
            END IF;
        EXCEPTION WHEN OTHERS THEN
            NULL;
        END;
        RETURN public.delivery_get_care_order_json(p_order_id);
    END IF;

    SELECT COALESCE(a.id, '') INTO v_assignment_id
    FROM public.hss_service_assignments a
    WHERE a.order_id = p_order_id
      AND (v_staff_id IS NULL OR a.staff_id = v_staff_id)
    ORDER BY a.created_at DESC
    LIMIT 1;

    INSERT INTO public.hss_service_execution_records(
        id, order_id, assignment_id, service_started_at, service_finished_at, actual_duration_minutes, service_items_json, summary, remark, created_at, updated_at
    ) VALUES (
        v_record_id, p_order_id, v_assignment_id, v_now, NULL, 0,
        COALESCE(p_payload -> 'items', '[]'::jsonb),
        COALESCE(p_payload ->> 'serviceSummary', ''),
        COALESCE(p_payload ->> 'progressNote', ''),
        v_now, v_now
    ) ON CONFLICT (id) DO UPDATE SET
        service_items_json = EXCLUDED.service_items_json,
        summary = EXCLUDED.summary,
        remark = EXCLUDED.remark,
        updated_at = EXCLUDED.updated_at;

    RETURN public.delivery_get_legacy_order_json(p_order_id);
END;
$$;

CREATE OR REPLACE FUNCTION public.rpc_delivery_upload_evidence(
    p_order_id TEXT,
    p_phase TEXT DEFAULT 'service',
    p_files TEXT[] DEFAULT ARRAY[]::TEXT[]
)
RETURNS JSONB
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
AS $$
DECLARE
    v_now TIMESTAMPTZ := now();
    v_file TEXT;
    v_result JSONB := '[]'::jsonb;
BEGIN
    IF public.delivery_get_care_order_json(p_order_id) IS NOT NULL THEN
        BEGIN
            IF public.delivery_table_exists('hc_evidence_files') THEN
                FOREACH v_file IN ARRAY COALESCE(p_files, ARRAY[]::TEXT[])
                LOOP
                    EXECUTE $sql$
                        INSERT INTO public.hc_evidence_files(id, task_id, care_record_id, phase, file_type, storage_path, file_url, captured_at, created_at)
                        VALUES ($1, $2::uuid, NULL, $3, 'image', $4, $4, $5, $5)
                    $sql$
                    USING 'hc-ef-' || replace(gen_random_uuid()::TEXT, '-', ''), p_order_id, COALESCE(p_phase, 'service'), v_file, v_now;
                END LOOP;
            END IF;
        EXCEPTION WHEN OTHERS THEN
            NULL;
        END;

        v_result := COALESCE(public.delivery_get_care_order_json(p_order_id) -> 'evidenceList', '[]'::jsonb);
        RETURN v_result;
    END IF;

    FOREACH v_file IN ARRAY COALESCE(p_files, ARRAY[]::TEXT[])
    LOOP
        INSERT INTO public.hss_service_evidence_files(id, order_id, execution_record_id, phase, file_type, storage_path, file_url, captured_at, created_at)
        VALUES (
            'sef-' || replace(gen_random_uuid()::TEXT, '-', ''),
            p_order_id,
            NULL,
            COALESCE(p_phase, 'service'),
            'image',
            v_file,
            v_file,
            v_now,
            v_now
        );
    END LOOP;

    RETURN COALESCE(public.delivery_get_legacy_order_json(p_order_id) -> 'evidenceList', '[]'::jsonb);
END;
$$;

CREATE OR REPLACE FUNCTION public.rpc_delivery_retry_evidence(p_order_id TEXT, p_evidence_id TEXT)
RETURNS JSONB
LANGUAGE plpgsql
STABLE
SECURITY DEFINER
SET search_path = public
AS $$
DECLARE
    v_order JSONB := public.delivery_get_order_json(p_order_id);
    v_item JSONB;
BEGIN
    FOR v_item IN SELECT value FROM jsonb_array_elements(COALESCE(v_order -> 'evidenceList', '[]'::jsonb))
    LOOP
        IF COALESCE(v_item ->> 'id', '') = COALESCE(p_evidence_id, '') THEN
            RETURN v_item;
        END IF;
    END LOOP;
    RETURN NULL;
END;
$$;

CREATE OR REPLACE FUNCTION public.rpc_delivery_submit_exception(p_order_id TEXT, p_payload JSONB DEFAULT '{}'::jsonb)
RETURNS JSONB
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
AS $$
DECLARE
    v_now TIMESTAMPTZ := now();
    v_staff_id UUID := public.delivery_current_staff_id();
BEGIN
    IF public.delivery_get_care_order_json(p_order_id) IS NOT NULL THEN
        BEGIN
            IF public.delivery_table_exists('hc_work_order_exceptions') THEN
                EXECUTE $sql$
                    INSERT INTO public.hc_work_order_exceptions(id, task_id, exception_type, description, occurred_at, location_text, images_json, need_platform_intervention, request_cancel_order, request_reschedule, created_by, created_at, updated_at)
                    VALUES ($1, $2::uuid, COALESCE($3 ->> 'exceptionType', 'other'), COALESCE($3 ->> 'description', ''), COALESCE(($3 ->> 'occurredAt')::timestamptz, $4), COALESCE($3 ->> 'locationText', ''), COALESCE($3 -> 'images', '[]'::jsonb), COALESCE(($3 ->> 'needPlatformIntervention')::boolean, false), COALESCE(($3 ->> 'requestCancelOrder')::boolean, false), COALESCE(($3 ->> 'requestReschedule')::boolean, false), $5, $4, $4)
                $sql$
                USING 'hc-ex-' || replace(gen_random_uuid()::TEXT, '-', ''), p_order_id, COALESCE(p_payload, '{}'::jsonb), v_now, public.delivery_current_user_id();
            END IF;
        EXCEPTION WHEN OTHERS THEN
            NULL;
        END;

        RETURN public.delivery_try_update_care_task(
            p_order_id,
            'ORDER_EXCEPTION',
            jsonb_build_object('updated_at', v_now),
            'report_exception',
            COALESCE(NULLIF(p_payload ->> 'description', ''), '异常上报')
        );
    END IF;

    UPDATE public.hss_service_orders
    SET status = 'exception', updated_at = v_now
    WHERE id = p_order_id
      AND deleted_at IS NULL
      AND (v_staff_id IS NULL OR current_staff_id = v_staff_id);

    INSERT INTO public.hss_service_order_status_logs(id, order_id, from_status, to_status, operator_id, operator_role, remark, created_at)
    VALUES (md5(random()::text || clock_timestamp()::text), p_order_id, 'in_service', 'exception', public.delivery_current_user_id(), 'delivery', COALESCE(NULLIF(p_payload ->> 'description', ''), '异常上报'), v_now);

    RETURN public.delivery_get_legacy_order_json(p_order_id);
END;
$$;

CREATE OR REPLACE FUNCTION public.rpc_delivery_finish_service(p_order_id TEXT, p_payload JSONB DEFAULT '{}'::jsonb)
RETURNS JSONB
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
AS $$
DECLARE
    v_now TIMESTAMPTZ := now();
    v_staff_id UUID := public.delivery_current_staff_id();
BEGIN
    IF public.delivery_get_care_order_json(p_order_id) IS NOT NULL THEN
        RETURN public.delivery_try_update_care_task(
            p_order_id,
            'ACCEPTANCE_PENDING',
            jsonb_build_object('service_completed_at', v_now, 'acceptance_pending_at', v_now, 'updated_at', v_now),
            'finish_service',
            '服务完成，等待用户验�?
        );
    END IF;

    UPDATE public.hss_service_execution_records
    SET service_finished_at = v_now, updated_at = v_now
    WHERE order_id = p_order_id;

    UPDATE public.hss_service_orders
    SET status = 'pending_acceptance', completed_at = v_now, pending_acceptance_at = v_now, updated_at = v_now
    WHERE id = p_order_id
      AND deleted_at IS NULL
      AND (v_staff_id IS NULL OR current_staff_id = v_staff_id);

    INSERT INTO public.hss_service_order_status_logs(id, order_id, from_status, to_status, operator_id, operator_role, remark, created_at)
    VALUES (md5(random()::text || clock_timestamp()::text), p_order_id, 'in_service', 'pending_acceptance', public.delivery_current_user_id(), 'delivery', '服务完成，等待用户验�?, v_now);

    RETURN public.delivery_get_legacy_order_json(p_order_id);
END;
$$;

CREATE OR REPLACE FUNCTION public.rpc_delivery_record_list(p_staff_id UUID DEFAULT NULL)
RETURNS JSONB
LANGUAGE plpgsql
STABLE
SECURITY DEFINER
SET search_path = public
AS $$
DECLARE
    v_staff_id UUID := COALESCE(p_staff_id, public.delivery_current_staff_id());
    v_orders JSONB := public.rpc_delivery_order_list(v_staff_id, 'all', '');
    v_result JSONB := '[]'::jsonb;
    v_item JSONB;
BEGIN
    PERFORM public.delivery_assert_staff_access(v_staff_id);

    FOR v_item IN SELECT value FROM jsonb_array_elements(COALESCE(v_orders, '[]'::jsonb))
    LOOP
        IF COALESCE(v_item ->> 'status', '') IN ('pending_acceptance', 'completed', 'settled', 'archived', 'abnormal', 'exception_pending', 'cancelled', 'rejected') THEN
            v_result := v_result || jsonb_build_array(jsonb_build_object(
                'id', 'record-' || COALESCE(v_item ->> 'id', ''),
                'orderId', COALESCE(v_item ->> 'id', ''),
                'orderNo', COALESCE(v_item ->> 'orderNo', ''),
                'serviceName', COALESCE(v_item ->> 'serviceName', ''),
                'elderName', COALESCE(v_item ->> 'elderName', ''),
                'elderNameMasked', COALESCE(v_item ->> 'elderNameMasked', ''),
                'status', COALESCE(v_item ->> 'status', ''),
                'statusText', COALESCE(v_item ->> 'statusText', ''),
                'appointmentStartTime', COALESCE(v_item ->> 'appointmentStartTime', ''),
                'appointmentTime', COALESCE(v_item ->> 'appointmentTime', ''),
                'actualStartTime', COALESCE(v_item ->> 'actualStartTime', ''),
                'actualEndTime', COALESCE(v_item ->> 'actualEndTime', COALESCE(v_item ->> 'updatedAt', '')),
                'staffIncome', COALESCE((v_item ->> 'staffIncome')::NUMERIC, 0),
                'settlementStatus', COALESCE(v_item ->> 'settlementStatus', ''),
                'acceptanceStatus', COALESCE(v_item ->> 'satisfactionStatus', ''),
                'exceptionDesc', COALESCE(v_item ->> 'exceptionDesc', ''),
                'ratingText', CASE WHEN COALESCE(v_item ->> 'status', '') IN ('completed', 'settled', 'archived') THEN '已验�? WHEN COALESCE(v_item ->> 'status', '') = 'pending_acceptance' THEN '待验�? ELSE '待补�? END,
                'hasServiceRecord', (v_item -> 'serviceRecord') IS NOT NULL
            ));
        END IF;
    END LOOP;

    RETURN v_result;
END;
$$;

CREATE OR REPLACE FUNCTION public.rpc_delivery_message_list()
RETURNS JSONB
LANGUAGE plpgsql
STABLE
SECURITY DEFINER
SET search_path = public
AS $$
DECLARE
    v_orders JSONB := public.rpc_delivery_order_list(public.delivery_current_staff_id(), 'all', '');
    v_result JSONB := '[]'::jsonb;
    v_item JSONB;
BEGIN
    FOR v_item IN SELECT value FROM jsonb_array_elements(COALESCE(v_orders, '[]'::jsonb)) LIMIT 8
    LOOP
        v_result := v_result || jsonb_build_array(jsonb_build_object(
            'id', 'msg-' || COALESCE(v_item ->> 'id', ''),
            'title', COALESCE(v_item ->> 'statusText', '工单提醒'),
            'content', COALESCE(v_item ->> 'serviceName', '居家服务') || '，服务对象：' || COALESCE(v_item ->> 'elderNameMasked', '') || '，预约时间：' || COALESCE(v_item ->> 'appointmentStartTime', ''),
            'type', 'order',
            'createdAt', COALESCE(v_item ->> 'updatedAt', v_item ->> 'createdAt', ''),
            'read', false,
            'orderId', COALESCE(v_item ->> 'id', '')
        ));
    END LOOP;

    RETURN v_result;
END;
$$;

CREATE OR REPLACE FUNCTION public.rpc_delivery_set_online_status(p_staff_id UUID, p_status TEXT)
RETURNS JSONB
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
AS $$
DECLARE
    v_staff_id UUID := COALESCE(p_staff_id, public.delivery_current_staff_id());
    v_info JSONB;
BEGIN
    PERFORM public.delivery_assert_staff_access(v_staff_id);

    UPDATE public.ml_delivery_staff
    SET online_status = CASE WHEN p_status IN ('online', 'busy') THEN p_status ELSE 'resting' END,
        updated_at = now()
    WHERE id = v_staff_id
      AND deleted_at IS NULL;

    v_info := public.delivery_build_staff_info(v_staff_id);

    IF v_info IS NULL THEN
        RETURN NULL;
    END IF;

    RETURN v_info || jsonb_build_object('onlineStatus', CASE WHEN p_status IN ('online', 'busy') THEN p_status ELSE 'resting' END);
END;
$$;

REVOKE ALL ON FUNCTION public.rpc_delivery_order_detail(TEXT) FROM PUBLIC;
GRANT EXECUTE ON FUNCTION public.rpc_delivery_order_detail(TEXT) TO authenticated;

REVOKE ALL ON FUNCTION public.rpc_delivery_order_list(UUID, TEXT, TEXT) FROM PUBLIC;
GRANT EXECUTE ON FUNCTION public.rpc_delivery_order_list(UUID, TEXT, TEXT) TO authenticated;

REVOKE ALL ON FUNCTION public.rpc_delivery_dashboard(UUID) FROM PUBLIC;
GRANT EXECUTE ON FUNCTION public.rpc_delivery_dashboard(UUID) TO authenticated;

REVOKE ALL ON FUNCTION public.rpc_delivery_accept_order(TEXT) FROM PUBLIC;
GRANT EXECUTE ON FUNCTION public.rpc_delivery_accept_order(TEXT) TO authenticated;

REVOKE ALL ON FUNCTION public.rpc_delivery_reject_order(TEXT, TEXT) FROM PUBLIC;
GRANT EXECUTE ON FUNCTION public.rpc_delivery_reject_order(TEXT, TEXT) TO authenticated;

REVOKE ALL ON FUNCTION public.rpc_delivery_start_depart(TEXT, JSONB) FROM PUBLIC;
GRANT EXECUTE ON FUNCTION public.rpc_delivery_start_depart(TEXT, JSONB) TO authenticated;

REVOKE ALL ON FUNCTION public.rpc_delivery_arrive_order(TEXT, JSONB) FROM PUBLIC;
GRANT EXECUTE ON FUNCTION public.rpc_delivery_arrive_order(TEXT, JSONB) TO authenticated;

REVOKE ALL ON FUNCTION public.rpc_delivery_checkin_order(TEXT, JSONB) FROM PUBLIC;
GRANT EXECUTE ON FUNCTION public.rpc_delivery_checkin_order(TEXT, JSONB) TO authenticated;

REVOKE ALL ON FUNCTION public.rpc_delivery_start_service(TEXT) FROM PUBLIC;
GRANT EXECUTE ON FUNCTION public.rpc_delivery_start_service(TEXT) TO authenticated;

REVOKE ALL ON FUNCTION public.rpc_delivery_save_progress(TEXT, JSONB) FROM PUBLIC;
GRANT EXECUTE ON FUNCTION public.rpc_delivery_save_progress(TEXT, JSONB) TO authenticated;

REVOKE ALL ON FUNCTION public.rpc_delivery_upload_evidence(TEXT, TEXT, TEXT[]) FROM PUBLIC;
GRANT EXECUTE ON FUNCTION public.rpc_delivery_upload_evidence(TEXT, TEXT, TEXT[]) TO authenticated;

REVOKE ALL ON FUNCTION public.rpc_delivery_retry_evidence(TEXT, TEXT) FROM PUBLIC;
GRANT EXECUTE ON FUNCTION public.rpc_delivery_retry_evidence(TEXT, TEXT) TO authenticated;

REVOKE ALL ON FUNCTION public.rpc_delivery_submit_exception(TEXT, JSONB) FROM PUBLIC;
GRANT EXECUTE ON FUNCTION public.rpc_delivery_submit_exception(TEXT, JSONB) TO authenticated;

REVOKE ALL ON FUNCTION public.rpc_delivery_finish_service(TEXT, JSONB) FROM PUBLIC;
GRANT EXECUTE ON FUNCTION public.rpc_delivery_finish_service(TEXT, JSONB) TO authenticated;

REVOKE ALL ON FUNCTION public.rpc_delivery_record_list(UUID) FROM PUBLIC;
GRANT EXECUTE ON FUNCTION public.rpc_delivery_record_list(UUID) TO authenticated;

REVOKE ALL ON FUNCTION public.rpc_delivery_message_list() FROM PUBLIC;
GRANT EXECUTE ON FUNCTION public.rpc_delivery_message_list() TO authenticated;

REVOKE ALL ON FUNCTION public.rpc_delivery_set_online_status(UUID, TEXT) FROM PUBLIC;
GRANT EXECUTE ON FUNCTION public.rpc_delivery_set_online_status(UUID, TEXT) TO authenticated;

COMMIT;