﻿-- ============================================================
-- 娓呯悊锛氬垹闄?rpc_homecare_auto_dispatch_optimized 鎵€鏈夐噸杞?
-- 鏃ユ湡锛?026-06-10
-- 闂锛歅ostgREST 鎶ラ敊 PGRST203 - 鏃犳硶閫夋嫨姝ｇ‘鐨勫嚱鏁伴噸杞?
-- 鍘熷洜锛氬瓨鍦ㄤ袱涓悓鍚嶅嚱鏁帮紝鍙傛暟鍒楄〃涓嶅悓
-- 瑙ｅ喅锛氬垹闄ゆ墍鏈夐噸杞斤紝鍙繚鐣欎慨澶嶅悗鐨勬柊鐗堟湰
-- ============================================================

-- 鏌ョ湅褰撳墠鎵€鏈夐噸杞?
SELECT routine_name, argument_list, data_type
FROM information_schema.routines 
WHERE routine_name = 'rpc_homecare_auto_dispatch_optimized';

-- 鍒犻櫎鎵€鏈夊彲鑳界殑閲嶈浇锛堟寜鍙傛暟鏁伴噺浠庡鍒板皯锛?
DROP FUNCTION IF EXISTS public.rpc_homecare_auto_dispatch_optimized(TEXT, TEXT);
DROP FUNCTION IF EXISTS public.rpc_homecare_auto_dispatch_optimized(TEXT);
DROP FUNCTION IF EXISTS public.rpc_homecare_auto_dispatch_optimized(UUID, TEXT);
DROP FUNCTION IF EXISTS public.rpc_homecare_auto_dispatch_optimized(UUID);

-- 纭宸插垹闄?
SELECT routine_name 
FROM information_schema.routines 
WHERE routine_name = 'rpc_homecare_auto_dispatch_optimized';
-- 搴旇杩斿洖 0 琛?

-- 鐜板湪鍒涘缓淇鍚庣殑鏂扮増鏈?
CREATE OR REPLACE FUNCTION public.rpc_homecare_auto_dispatch_optimized(
    p_order_id TEXT,
    p_operator_type TEXT DEFAULT 'system'
)
RETURNS JSONB
LANGUAGE plpgsql
AS $$
DECLARE
    v_order RECORD;
    v_assignment RECORD;
    v_assignment_id TEXT;
    v_staff_id UUID;
    v_current_user_id UUID;
    v_now TIMESTAMPTZ;
    v_failure_code TEXT;
    v_failure_message TEXT;
    v_retryable BOOLEAN;
    v_status_log_id TEXT;
    v_attempt_log_id TEXT;
    v_distance_km NUMERIC;
    v_order_lat NUMERIC;
    v_order_lng NUMERIC;
    v_staff_lat NUMERIC;
    v_staff_lng NUMERIC;
BEGIN
    v_now := now();
    
    BEGIN
        v_current_user_id := gen_random_uuid();
    EXCEPTION WHEN OTHERS THEN
        v_current_user_id := NULL;
    END;

    -- 鏌ヨ璁㈠崟淇℃伅
    SELECT * INTO v_order
    FROM public.hss_service_orders
    WHERE id = p_order_id
      AND deleted_at IS NULL
      AND status = 'paid'
      AND dispatch_status = 'pending';

    IF NOT FOUND THEN
        RETURN jsonb_build_object(
            'success', FALSE,
            'code', 'ORDER_NOT_FOUND_OR_NOT_ELIGIBLE',
            'message', '璁㈠崟涓嶅瓨鍦ㄦ垨涓嶇鍚堟淳鍗曟潯浠?,
            'display_type', 'toast',
            'retryable', FALSE
        );
    END IF;

    -- 鎻愬彇璁㈠崟鍧愭爣
    v_order_lat := v_order.service_lat;
    v_order_lng := v_order.service_lng;

    IF v_order_lat IS NULL OR v_order_lng IS NULL THEN
        RETURN jsonb_build_object(
            'success', FALSE,
            'code', 'ORDER_MISSING_COORDINATES',
            'message', '璁㈠崟缂哄皯浣嶇疆淇℃伅锛岃鑱旂郴瀹㈡湇',
            'display_type', 'toast',
            'retryable', FALSE
        );
    END IF;

    -- 鏍规嵁璋冨害绔欑偣鏌ユ壘鍚堥€備汉鍛?
    IF v_order.dispatch_station_id IS NOT NULL THEN
        SELECT * INTO v_assignment
        FROM (
            SELECT 
                s.id AS staff_id,
                s.staff_name,
                s.online_status,
                s.station_id,
                s.latitude AS staff_lat,
                s.longitude AS staff_lng,
                distance_calc.distance_km(s.latitude, s.longitude, v_order_lat, v_order_lng) AS distance_km,
                ROW_NUMBER() OVER (ORDER BY distance_calc.distance_km(s.latitude, s.longitude, v_order_lat, v_order_lng) ASC) AS rn
            FROM public.ml_delivery_staff s
            WHERE s.station_id::TEXT = v_order.dispatch_station_id
              AND s.online_status = 'online'
              AND s.latitude IS NOT NULL
              AND s.longitude IS NOT NULL
              AND (
                  v_order.required_qualification_code IS NULL
                  OR EXISTS (
                      SELECT 1
                      FROM public.hc_worker_qualifications q
                      WHERE q.staff_id::TEXT = s.id::TEXT
                        AND q.qualification_code = v_order.required_qualification_code
                        AND q.deleted_at IS NULL
                        AND q.status::TEXT IN ('1', 'active', 'approved', 'valid')
                        AND (q.valid_from IS NULL OR q.valid_from <= v_now)
                        AND (q.valid_until IS NULL OR q.valid_until >= v_now)
                  )
              )
              AND distance_calc.distance_km(s.latitude, s.longitude, v_order_lat, v_order_lng) <= COALESCE(s.dispatch_radius_km, 20)
              AND NOT EXISTS (
                  SELECT 1
                  FROM public.hss_service_assignments sa
                  WHERE sa.staff_id = s.id
                    AND sa.status IN ('assigned', 'accepted', 'in_progress')
                    AND sa.deleted_at IS NULL
                    AND sa.start_time <= v_now
                    AND sa.end_time > v_now
              )
        ) AS ranked_candidates
        WHERE rn = 1
        LIMIT 1;
    ELSE
        SELECT * INTO v_assignment
        FROM (
            SELECT 
                s.id AS staff_id,
                s.staff_name,
                s.online_status,
                s.station_id,
                s.latitude AS staff_lat,
                s.longitude AS staff_lng,
                distance_calc.distance_km(s.latitude, s.longitude, v_order_lat, v_order_lng) AS distance_km,
                ROW_NUMBER() OVER (ORDER BY distance_calc.distance_km(s.latitude, s.longitude, v_order_lat, v_order_lng) ASC) AS rn
            FROM public.ml_delivery_staff s
            WHERE s.online_status = 'online'
              AND s.latitude IS NOT NULL
              AND s.longitude IS NOT NULL
              AND (
                  v_order.dispatch_station_id IS NULL
                  OR s.station_id::TEXT = v_order.dispatch_station_id
              )
              AND (
                  v_order.required_qualification_code IS NULL
                  OR EXISTS (
                      SELECT 1
                      FROM public.hc_worker_qualifications q
                      WHERE q.staff_id::TEXT = s.id::TEXT
                        AND q.qualification_code = v_order.required_qualification_code
                        AND q.deleted_at IS NULL
                        AND q.status::TEXT IN ('1', 'active', 'approved', 'valid')
                        AND (q.valid_from IS NULL OR q.valid_from <= v_now)
                        AND (q.valid_until IS NULL OR q.valid_until >= v_now)
                  )
              )
              AND distance_calc.distance_km(s.latitude, s.longitude, v_order_lat, v_order_lng) <= COALESCE(s.dispatch_radius_km, 20)
              AND NOT EXISTS (
                  SELECT 1
                  FROM public.hss_service_assignments sa
                  WHERE sa.staff_id = s.id
                    AND sa.status IN ('assigned', 'accepted', 'in_progress')
                    AND sa.deleted_at IS NULL
                    AND sa.start_time <= v_now
                    AND sa.end_time > v_now
              )
        ) AS ranked_candidates
        WHERE rn = 1
        LIMIT 1;
    END IF;

    -- 濡傛灉鎵惧埌鍚堥€傜殑浜哄憳锛屽垱寤哄垎閰?
    IF FOUND AND v_assignment.staff_id IS NOT NULL THEN
        v_staff_id := v_assignment.staff_id;
        v_assignment_id := 'asgn-' || floor(extract(epoch FROM v_now) * 1000)::BIGINT::TEXT || '-' || upper(substr(md5(p_order_id || ':' || v_staff_id::TEXT), 1, 10));

        -- 鍒涘缓鍒嗛厤璁板綍
        INSERT INTO public.hss_service_assignments (
            id, order_id, staff_id, status, assigned_at, start_time, end_time, created_at
        ) VALUES (
            v_assignment_id, p_order_id, v_staff_id, 'assigned', v_now, v_now, v_now + INTERVAL '2 hours', v_now
        );

        -- 鏇存柊璁㈠崟鐘舵€?
        UPDATE public.hss_service_orders
        SET dispatch_status = 'assigned',
            current_staff_id = v_staff_id,
            assigned_at = v_now,
            updated_at = v_now
        WHERE id = p_order_id;

        -- 璁板綍鐘舵€佹棩蹇楋紙浣跨敤姝ｇ‘鐨?operator_role 瀛楁锛?
        v_status_log_id := 'slg-' || floor(extract(epoch FROM v_now) * 1000)::BIGINT::TEXT || '-' || upper(substr(md5(p_order_id || ':assigned'), 1, 10));

        INSERT INTO public.hss_service_order_status_logs (
            id, order_id, from_status, to_status, operator_role, operator_id, remark, created_at
        ) VALUES (
            v_status_log_id, p_order_id, 'created', 'assigned', 'system', v_current_user_id, '绯荤粺鑷姩娲惧崟', v_now
        );

        -- 璁板綍娲惧崟灏濊瘯鏃ュ織
        v_attempt_log_id := 'dalog-' || floor(extract(epoch FROM v_now) * 1000)::BIGINT::TEXT || '-' || upper(substr(md5(p_order_id || ':attempt'), 1, 10));

        INSERT INTO public.hss_service_dispatch_attempt_logs (
            id, order_id, requested_by_user_id, selected_staff_id, selected_station_id, success, assigned_at, created_at
        ) VALUES (
            v_attempt_log_id, p_order_id, v_current_user_id, v_staff_id, v_assignment.station_id::TEXT, TRUE, v_now, v_now
        );

        RETURN jsonb_build_object(
            'success', TRUE,
            'code', 'DISPATCH_ASSIGNED',
            'message', '绯荤粺宸蹭负鎮ㄥ尮閰嶆湇鍔′汉鍛?,
            'display_type', 'none',
            'retryable', FALSE,
            'dispatch_status', 'assigned',
            'order_id', p_order_id,
            'assignment_id', v_assignment_id,
            'staff_name', v_assignment.staff_name,
            'distance_km', v_assignment.distance_km
        );
    END IF;

    -- 鏈壘鍒板悎閫備汉鍛橈紝鍒嗘瀽澶辫触鍘熷洜
    v_failure_code := 'NO_ONLINE_STAFF';
    v_failure_message := '鏆傛棤鍦ㄧ嚎鏈嶅姟浜哄憳锛岃绋嶅悗閲嶈瘯';
    v_retryable := TRUE;

    -- 妫€鏌ユ槸鍚︽湁鍦ㄧ嚎浜哄憳
    IF NOT EXISTS (
        SELECT 1 FROM public.ml_delivery_staff 
        WHERE online_status = 'online' 
          AND deleted_at IS NULL
          AND latitude IS NOT NULL 
          AND longitude IS NOT NULL
    ) THEN
        v_failure_code := 'NO_ONLINE_STAFF';
        v_failure_message := '鏆傛棤鍦ㄧ嚎浜哄憳锛岃绋嶅悗閲嶈瘯鎴栬仈绯诲鏈?;
    
    -- 妫€鏌ユ槸鍚︽湁绔欑偣鍐呯殑鍦ㄧ嚎浜哄憳
    ELSIF v_order.dispatch_station_id IS NOT NULL AND NOT EXISTS (
        SELECT 1 FROM public.ml_delivery_staff s
        WHERE s.station_id::TEXT = v_order.dispatch_station_id
          AND s.online_status = 'online'
          AND s.deleted_at IS NULL
    ) THEN
        v_failure_code := 'NO_STAFF_IN_SERVICE_STATION';
        v_failure_message := '褰撳墠鍖哄煙鏆傛棤浜哄憳锛岃绋嶅悗閲嶈瘯鎴栬仈绯诲鏈?;
    
    -- 妫€鏌ユ槸鍚︽湁鍏峰璧勮川鐨勪汉鍛?
    ELSIF v_order.required_qualification_code IS NOT NULL AND NOT EXISTS (
        SELECT 1 FROM public.hc_worker_qualifications q
        WHERE q.qualification_code = v_order.required_qualification_code
          AND q.deleted_at IS NULL
          AND q.status::TEXT IN ('1', 'active', 'approved', 'valid')
          AND (q.valid_from IS NULL OR q.valid_from <= v_now)
          AND (q.valid_until IS NULL OR q.valid_until >= v_now)
    ) THEN
        v_failure_code := 'NO_QUALIFIED_STAFF';
        v_failure_message := '鏆傛棤鍏峰璇ヨ祫璐ㄧ殑鏈嶅姟浜哄憳锛岃绋嶅悗閲嶈瘯鎴栬仈绯诲鏈?;
    
    -- 妫€鏌ラ檮杩戞槸鍚︽湁浜哄憳
    ELSIF NOT EXISTS (
        SELECT 1 FROM public.ml_delivery_staff s
        WHERE s.online_status = 'online'
          AND s.latitude IS NOT NULL
          AND s.longitude IS NOT NULL
          AND distance_calc.distance_km(s.latitude, s.longitude, v_order_lat, v_order_lng) <= COALESCE(s.dispatch_radius_km, 20)
    ) THEN
        v_failure_code := 'NO_NEARBY_STAFF';
        v_failure_message := '闄勮繎鏆傛棤鍙笂闂ㄦ湇鍔′汉鍛橈紝璇风◢鍚庨噸璇曟垨鑱旂郴瀹㈡湇';
    
    -- 鎵€鏈夌鍚堟潯浠剁殑浜哄憳閮藉湪蹇?
    ELSIF EXISTS (
        SELECT 1 FROM public.ml_delivery_staff s
        WHERE s.online_status = 'online'
          AND s.latitude IS NOT NULL
          AND s.longitude IS NOT NULL
          AND distance_calc.distance_km(s.latitude, s.longitude, v_order_lat, v_order_lng) <= COALESCE(s.dispatch_radius_km, 20)
          AND EXISTS (
              SELECT 1 FROM public.hss_service_assignments sa
              WHERE sa.staff_id = s.id
                AND sa.status IN ('assigned', 'accepted', 'in_progress')
                AND sa.deleted_at IS NULL
                AND sa.start_time <= v_now
                AND sa.end_time > v_now
          )
    ) THEN
        v_failure_code := 'ALL_ELIGIBLE_STAFF_BUSY';
        v_failure_message := '鍙湇鍔′汉鍛樺潎鍦ㄥ繖锛岃绋嶅悗閲嶆柊娲惧崟';
    END IF;

    -- 鏇存柊璁㈠崟涓哄け璐ョ姸鎬?
    UPDATE public.hss_service_orders
    SET dispatch_status = 'failed',
        dispatch_error_code = v_failure_code,
        dispatch_error_message = v_failure_message,
        dispatch_failed_at = v_now,
        updated_at = v_now
    WHERE id = p_order_id;

    -- 璁板綍澶辫触鏃ュ織
    v_attempt_log_id := 'dalog-' || floor(extract(epoch FROM v_now) * 1000)::BIGINT::TEXT || '-' || upper(substr(md5(p_order_id || ':failed'), 1, 10));

    INSERT INTO public.hss_service_dispatch_attempt_logs (
        id, order_id, requested_by_user_id, selected_staff_id, selected_station_id, success, failure_code, failure_message, retryable, created_at
    ) VALUES (
        v_attempt_log_id, p_order_id, v_current_user_id, NULL, NULL, FALSE, v_failure_code, v_failure_message, v_retryable, v_now
    );

    RETURN jsonb_build_object(
        'success', FALSE,
        'code', v_failure_code,
        'message', v_failure_message,
        'display_type', 'modal',
        'retryable', v_retryable,
        'dispatch_status', 'failed',
        'order_id', p_order_id
    );

END;
$$;

COMMENT ON FUNCTION public.rpc_homecare_auto_dispatch_optimized IS '浼樺寲鐗堝眳瀹舵湇鍔¤嚜鍔ㄦ淳鍗昍PC - 娓呯悊鎵€鏈夐噸杞藉悗鍙繚鐣欐鐗堟湰';

