BEGIN;

-- =====================================================================================
-- RPC: rpc_homecare_auto_dispatch
-- Version: v2_secure_dispatch
-- Purpose:
--   1. 消费者支付成功后，由数据库自动派单
--   2. 禁止前端直接 INSERT hss_service_assignments
--   3. 支持派单失败原因返回、失败记录、重新派单
--   4. 增加权限校验、支付校验、并发锁、重复派单保护
--
-- Dispatch rules:
--   - 订单必须属于当前登录用户
--   - 订单必须已经支付 payment_status = 2
--   - 订单状态必须仍为 created
--   - 服务人员必须在线、启用、未删除
--   - 可选匹配：站点、资质、服务半径、预约时间冲突
--
-- Return fields:
--   success          BOOLEAN
--   code             TEXT        前端用于分支判断
--   message          TEXT        前端弹窗 content 直接使用
--   display_type     TEXT        modal / toast
--   retryable        BOOLEAN     是否允许显示“重新派单”
--   dispatch_status  TEXT
-- =====================================================================================


-- =====================================================================================
-- 1. 收紧前端直接写入派单表的权限
-- =====================================================================================

DROP POLICY IF EXISTS hss_service_assignments_order_owner_insert
ON public.hss_service_assignments;


-- =====================================================================================
-- 2. 补充服务订单派单字段
-- =====================================================================================

ALTER TABLE public.hss_service_orders
    ADD COLUMN IF NOT EXISTS payment_status SMALLINT NOT NULL DEFAULT 1,
    ADD COLUMN IF NOT EXISTS dispatch_status TEXT NOT NULL DEFAULT 'pending',
    ADD COLUMN IF NOT EXISTS dispatch_error_code TEXT,
    ADD COLUMN IF NOT EXISTS dispatch_error_message TEXT,
    ADD COLUMN IF NOT EXISTS dispatch_failed_at TIMESTAMPTZ,
    ADD COLUMN IF NOT EXISTS dispatch_attempt_count INTEGER NOT NULL DEFAULT 0,

    -- 订单需要的服务资质编码，例如 BASIC_CARE、PROFESSIONAL_NURSING
    ADD COLUMN IF NOT EXISTS required_qualification_code TEXT,

    -- 指定服务站点，为空时不限定站点
    ADD COLUMN IF NOT EXISTS dispatch_station_id TEXT,

    -- 用户服务地址经纬度；用于距离筛选，不负责地址文字转换
    ADD COLUMN IF NOT EXISTS service_lat NUMERIC(10,7),
    ADD COLUMN IF NOT EXISTS service_lng NUMERIC(10,7),

    -- 用户预约服务时间；为空时表示立即上门或未指定时间
    ADD COLUMN IF NOT EXISTS scheduled_start_at TIMESTAMPTZ,
    ADD COLUMN IF NOT EXISTS scheduled_end_at TIMESTAMPTZ;

UPDATE public.hss_service_orders
SET dispatch_status = 'pending'
WHERE dispatch_status IS NULL;

UPDATE public.hss_service_orders
SET dispatch_attempt_count = 0
WHERE dispatch_attempt_count IS NULL;


DO $$
BEGIN
    IF NOT EXISTS (
        SELECT 1
        FROM pg_constraint
        WHERE conname = 'chk_hss_service_orders_dispatch_status'
          AND conrelid = 'public.hss_service_orders'::regclass
    ) THEN
        ALTER TABLE public.hss_service_orders
            ADD CONSTRAINT chk_hss_service_orders_dispatch_status
            CHECK (
                dispatch_status IN ('pending', 'dispatching', 'assigned', 'failed')
            ) NOT VALID;
    END IF;

    IF NOT EXISTS (
        SELECT 1
        FROM pg_constraint
        WHERE conname = 'chk_hss_service_orders_service_lat'
          AND conrelid = 'public.hss_service_orders'::regclass
    ) THEN
        ALTER TABLE public.hss_service_orders
            ADD CONSTRAINT chk_hss_service_orders_service_lat
            CHECK (
                service_lat IS NULL OR service_lat BETWEEN -90 AND 90
            ) NOT VALID;
    END IF;

    IF NOT EXISTS (
        SELECT 1
        FROM pg_constraint
        WHERE conname = 'chk_hss_service_orders_service_lng'
          AND conrelid = 'public.hss_service_orders'::regclass
    ) THEN
        ALTER TABLE public.hss_service_orders
            ADD CONSTRAINT chk_hss_service_orders_service_lng
            CHECK (
                service_lng IS NULL OR service_lng BETWEEN -180 AND 180
            ) NOT VALID;
    END IF;

    IF NOT EXISTS (
        SELECT 1
        FROM pg_constraint
        WHERE conname = 'chk_hss_service_orders_schedule_time'
          AND conrelid = 'public.hss_service_orders'::regclass
    ) THEN
        ALTER TABLE public.hss_service_orders
            ADD CONSTRAINT chk_hss_service_orders_schedule_time
            CHECK (
                scheduled_start_at IS NULL
                OR scheduled_end_at IS NULL
                OR scheduled_end_at > scheduled_start_at
            ) NOT VALID;
    END IF;
END;
$$;


-- =====================================================================================
-- 3. 补充服务人员派单能力字段
-- =====================================================================================

ALTER TABLE public.ml_delivery_staff
    ADD COLUMN IF NOT EXISTS current_lat NUMERIC(10,7),
    ADD COLUMN IF NOT EXISTS current_lng NUMERIC(10,7),

    -- 允许接单的最大直线距离，单位：公里
    ADD COLUMN IF NOT EXISTS dispatch_radius_km NUMERIC(8,2) NOT NULL DEFAULT 20,

    -- 用于公平派单，优先选择更久没有接到任务的人
    ADD COLUMN IF NOT EXISTS last_dispatched_at TIMESTAMPTZ;

DO $$
BEGIN
    IF NOT EXISTS (
        SELECT 1
        FROM pg_constraint
        WHERE conname = 'chk_ml_delivery_staff_dispatch_radius'
          AND conrelid = 'public.ml_delivery_staff'::regclass
    ) THEN
        ALTER TABLE public.ml_delivery_staff
            ADD CONSTRAINT chk_ml_delivery_staff_dispatch_radius
            CHECK (
                dispatch_radius_km > 0 AND dispatch_radius_km <= 200
            ) NOT VALID;
    END IF;
END;
$$;


-- =====================================================================================
-- 4. 补充派单记录字段
-- =====================================================================================

ALTER TABLE public.hss_service_assignments
    ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ,

    -- 派单当时服务人员与用户之间的计算距离
    ADD COLUMN IF NOT EXISTS dispatch_distance_km NUMERIC(8,3),

    -- 派单时使用的资质要求快照
    ADD COLUMN IF NOT EXISTS qualification_code_snapshot TEXT;


-- =====================================================================================
-- 5. 补充订单日志触发者字段
--    原表 operator_id 可能已有 NOT NULL 约束，所以继续保留兼容写入；
--    新增 triggered_by_user_id 用于明确“由哪个消费者触发系统派单”。
-- =====================================================================================

ALTER TABLE public.hss_service_order_status_logs
    ADD COLUMN IF NOT EXISTS triggered_by_user_id UUID;


-- =====================================================================================
-- 6. 服务人员资质表
--    如果该表此前已经存在，会补充以下标准字段。
-- =====================================================================================

CREATE TABLE IF NOT EXISTS public.hc_worker_qualifications (
    id TEXT PRIMARY KEY,
    staff_id TEXT NOT NULL,
    qualification_code TEXT NOT NULL,
    qualification_name TEXT NOT NULL DEFAULT '',
    certificate_no TEXT NOT NULL DEFAULT '',
    status SMALLINT NOT NULL DEFAULT 1,
    valid_from TIMESTAMPTZ,
    valid_until TIMESTAMPTZ,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    deleted_at TIMESTAMPTZ
);

ALTER TABLE public.hc_worker_qualifications
    ADD COLUMN IF NOT EXISTS staff_id TEXT,
    ADD COLUMN IF NOT EXISTS qualification_code TEXT,
    ADD COLUMN IF NOT EXISTS qualification_name TEXT NOT NULL DEFAULT '',
    ADD COLUMN IF NOT EXISTS certificate_no TEXT NOT NULL DEFAULT '',
    ADD COLUMN IF NOT EXISTS status SMALLINT NOT NULL DEFAULT 1,
    ADD COLUMN IF NOT EXISTS valid_from TIMESTAMPTZ,
    ADD COLUMN IF NOT EXISTS valid_until TIMESTAMPTZ,
    ADD COLUMN IF NOT EXISTS created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ;

ALTER TABLE public.hc_worker_qualifications ENABLE ROW LEVEL SECURITY;

REVOKE ALL ON TABLE public.hc_worker_qualifications FROM anon;
REVOKE ALL ON TABLE public.hc_worker_qualifications FROM authenticated;

CREATE INDEX IF NOT EXISTS idx_hc_worker_qualifications_dispatch_match
ON public.hc_worker_qualifications (
    staff_id,
    qualification_code,
    status,
    valid_until
)
WHERE deleted_at IS NULL;


-- =====================================================================================
-- 7. 新增派单尝试日志表
--    专门记录成功或失败派单，不强行复用订单状态日志。
-- =====================================================================================

CREATE TABLE IF NOT EXISTS public.hss_service_dispatch_attempt_logs (
    id TEXT PRIMARY KEY,
    order_id TEXT NOT NULL,
    requested_by_user_id UUID,
    selected_staff_id TEXT,
    selected_station_id TEXT,
    success BOOLEAN NOT NULL DEFAULT FALSE,
    result_code TEXT NOT NULL,
    result_message TEXT NOT NULL,
    retryable BOOLEAN NOT NULL DEFAULT FALSE,
    filters_snapshot JSONB NOT NULL DEFAULT '{}'::jsonb,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

ALTER TABLE public.hss_service_dispatch_attempt_logs ENABLE ROW LEVEL SECURITY;

REVOKE ALL ON TABLE public.hss_service_dispatch_attempt_logs FROM anon;
REVOKE ALL ON TABLE public.hss_service_dispatch_attempt_logs FROM authenticated;

CREATE INDEX IF NOT EXISTS idx_hss_dispatch_attempt_logs_order_time
ON public.hss_service_dispatch_attempt_logs (
    order_id,
    created_at DESC
);


-- =====================================================================================
-- 8. 派单性能索引
-- =====================================================================================

CREATE INDEX IF NOT EXISTS idx_hss_service_orders_dispatch_query
ON public.hss_service_orders (
    user_id,
    payment_status,
    status,
    dispatch_status,
    created_at DESC
)
WHERE deleted_at IS NULL;

CREATE INDEX IF NOT EXISTS idx_ml_delivery_staff_dispatch_candidate
ON public.ml_delivery_staff (
    online_status,
    status,
    is_active,
    station_id,
    last_dispatched_at
)
WHERE deleted_at IS NULL;

CREATE INDEX IF NOT EXISTS idx_hss_service_assignments_staff_active
ON public.hss_service_assignments (
    staff_id,
    status,
    assigned_at DESC
)
WHERE deleted_at IS NULL
  AND status IN ('assigned', 'accepted', 'departed', 'arrived', 'serving', 'in_service');


-- =====================================================================================
-- 9. 创建活动派单唯一索引前，先检查是否已有脏数据
--    如果同一订单已经存在多个有效派单，会中止脚本，避免自动误删业务数据。
-- =====================================================================================

DO $$
DECLARE
    v_duplicate_orders TEXT;
BEGIN
    SELECT string_agg(t.order_id::text, ', ')
    INTO v_duplicate_orders
    FROM (
        SELECT order_id
        FROM public.hss_service_assignments
        WHERE deleted_at IS NULL
          AND status IN ('assigned', 'accepted', 'departed', 'arrived', 'serving', 'in_service')
        GROUP BY order_id
        HAVING COUNT(*) > 1
        LIMIT 20
    ) t;

    IF v_duplicate_orders IS NOT NULL THEN
        RAISE EXCEPTION
            '存在同一订单的多条有效派单记录，请先人工核对并关闭重复记录后重新执行。订单ID: %',
            v_duplicate_orders;
    END IF;
END;
$$;

CREATE UNIQUE INDEX IF NOT EXISTS uq_hss_service_assignments_active_order
ON public.hss_service_assignments (order_id)
WHERE deleted_at IS NULL
  AND status IN ('assigned', 'accepted', 'departed', 'arrived', 'serving', 'in_service');


-- =====================================================================================
-- 10. 创建安全自动派单 RPC
-- =====================================================================================

CREATE OR REPLACE FUNCTION public.rpc_homecare_auto_dispatch(
    p_order_id TEXT DEFAULT NULL
)
RETURNS JSONB
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = ''
AS $function$
DECLARE
    v_now TIMESTAMPTZ := clock_timestamp();

    v_current_user_id UUID;
    v_order public.hss_service_orders%ROWTYPE;

    v_existing_assignment RECORD;
    v_candidate RECORD;
    v_candidate_found BOOLEAN := FALSE;

    v_assignment_id TEXT;
    v_status_log_id TEXT;
    v_attempt_log_id TEXT;

    v_failure_code TEXT;
    v_failure_message TEXT;
    v_retryable BOOLEAN := FALSE;
BEGIN
    -- =========================================================================
    -- A. 基础参数与身份校验
    -- =========================================================================

    IF p_order_id IS NULL OR btrim(p_order_id) = '' THEN
        RETURN jsonb_build_object(
            'success', FALSE,
            'code', 'ORDER_ID_REQUIRED',
            'message', '订单ID不能为空',
            'display_type', 'toast',
            'retryable', FALSE
        );
    END IF;

    IF auth.uid() IS NULL THEN
        RETURN jsonb_build_object(
            'success', FALSE,
            'code', 'UNAUTHENTICATED',
            'message', '登录状态已失效，请重新登录后再操作',
            'display_type', 'modal',
            'retryable', FALSE
        );
    END IF;

    SELECT u.id
    INTO v_current_user_id
    FROM public.ak_users u
    WHERE u.auth_id = auth.uid()
    LIMIT 1;

    IF v_current_user_id IS NULL THEN
        RETURN jsonb_build_object(
            'success', FALSE,
            'code', 'USER_PROFILE_NOT_FOUND',
            'message', '当前账户信息异常，请重新登录或联系客服',
            'display_type', 'modal',
            'retryable', FALSE
        );
    END IF;


    -- =========================================================================
    -- B. 锁定订单，避免同一个订单被重复派单
    -- =========================================================================

    SELECT o.*
    INTO v_order
    FROM public.hss_service_orders o
    WHERE o.id = p_order_id
      AND o.deleted_at IS NULL
    FOR UPDATE OF o;

    IF NOT FOUND THEN
        RETURN jsonb_build_object(
            'success', FALSE,
            'code', 'ORDER_NOT_FOUND',
            'message', '订单不存在或已被删除',
            'display_type', 'modal',
            'retryable', FALSE
        );
    END IF;

    IF v_order.user_id IS DISTINCT FROM v_current_user_id THEN
        RETURN jsonb_build_object(
            'success', FALSE,
            'code', 'ORDER_ACCESS_DENIED',
            'message', '您无权操作该订单',
            'display_type', 'modal',
            'retryable', FALSE
        );
    END IF;


    -- =========================================================================
    -- C. 幂等处理：订单已经派单成功时，不重复插入记录
    -- =========================================================================

    IF v_order.status = 'assigned'
       AND v_order.current_assignment_id IS NOT NULL THEN

        RETURN jsonb_build_object(
            'success', TRUE,
            'code', 'ALREADY_ASSIGNED',
            'message', '订单已完成派单',
            'display_type', 'none',
            'retryable', FALSE,
            'dispatch_status', 'assigned',
            'order_id', p_order_id,
            'assignment_id', v_order.current_assignment_id,
            'staff_id', v_order.current_staff_id
        );
    END IF;


    -- =========================================================================
    -- D. 支付与订单状态校验
    --    payment_status:
    --      1 = 待支付
    --      2 = 已支付
    --      3 = 已退款
    --      4 = 支付失败
    --      5 = 支付超时关闭
    -- =========================================================================

    IF COALESCE(v_order.payment_status, 1) <> 2 THEN
        RETURN jsonb_build_object(
            'success', FALSE,
            'code', 'ORDER_NOT_PAID',
            'message', '订单尚未完成支付，暂不能安排服务人员',
            'display_type', 'modal',
            'retryable', FALSE,
            'dispatch_status', COALESCE(v_order.dispatch_status, 'pending'),
            'order_id', p_order_id
        );
    END IF;

    IF v_order.status <> 'created' THEN
        RETURN jsonb_build_object(
            'success', FALSE,
            'code', 'ORDER_STATUS_NOT_DISPATCHABLE',
            'message', '当前订单状态不允许重新派单',
            'display_type', 'modal',
            'retryable', FALSE,
            'dispatch_status', COALESCE(v_order.dispatch_status, 'pending'),
            'order_id', p_order_id
        );
    END IF;


    -- =========================================================================
    -- E. 兼容历史数据：若订单已存在有效派单记录，但订单主表未同步，则修复主表
    -- =========================================================================

    SELECT
        a.id,
        a.staff_id,
        a.station_id
    INTO v_existing_assignment
    FROM public.hss_service_assignments a
    WHERE a.order_id = p_order_id
      AND a.deleted_at IS NULL
      AND a.status IN ('assigned', 'accepted', 'departed', 'arrived', 'serving', 'in_service')
    ORDER BY a.assigned_at DESC
    LIMIT 1;

    IF FOUND THEN
        UPDATE public.hss_service_orders
        SET status = 'assigned',
            dispatch_status = 'assigned',
            current_assignment_id = v_existing_assignment.id,
            current_staff_id = v_existing_assignment.staff_id,
            dispatch_error_code = NULL,
            dispatch_error_message = NULL,
            dispatch_failed_at = NULL,
            updated_at = v_now
        WHERE id = p_order_id;

        RETURN jsonb_build_object(
            'success', TRUE,
            'code', 'ALREADY_ASSIGNED_RECOVERED',
            'message', '订单已存在有效派单记录，系统已完成状态同步',
            'display_type', 'none',
            'retryable', FALSE,
            'dispatch_status', 'assigned',
            'order_id', p_order_id,
            'assignment_id', v_existing_assignment.id,
            'staff_id', v_existing_assignment.staff_id,
            'station_id', COALESCE(v_existing_assignment.station_id::text, '')
        );
    END IF;


    -- =========================================================================
    -- F. 进入派单中状态
    -- =========================================================================

    UPDATE public.hss_service_orders
    SET dispatch_status = 'dispatching',
        dispatch_attempt_count = COALESCE(dispatch_attempt_count, 0) + 1,
        dispatch_error_code = NULL,
        dispatch_error_message = NULL,
        dispatch_failed_at = NULL,
        updated_at = v_now
    WHERE id = p_order_id;


    -- =========================================================================
    -- G. 查找可派单服务人员
    --
    -- 筛选规则：
    --   1. 正常、启用、在线、绑定用户
    --   2. 订单指定站点时，必须属于该站点
    --   3. 订单指定资质时，必须持有有效资质
    --   4. 订单包含经纬度时，必须在服务半径内
    --   5. 不得与当前有效任务发生服务时间冲突
    --   6. 锁住被选中的服务人员，防止并发订单同时选中同一人
    -- =========================================================================

    SELECT
        s.id,
        s.station_id,
        distance_calc.distance_km
    INTO v_candidate
    FROM public.ml_delivery_staff s
    CROSS JOIN LATERAL (
        SELECT
            CASE
                WHEN v_order.service_lat IS NULL
                  OR v_order.service_lng IS NULL
                  OR s.current_lat IS NULL
                  OR s.current_lng IS NULL
                THEN NULL
                ELSE (
                    6371 * 2 * asin(
                        sqrt(
                            power(
                                sin(
                                    radians((s.current_lat - v_order.service_lat)::DOUBLE PRECISION) / 2
                                ),
                                2
                            )
                            +
                            cos(radians(v_order.service_lat::DOUBLE PRECISION))
                            * cos(radians(s.current_lat::DOUBLE PRECISION))
                            * power(
                                sin(
                                    radians((s.current_lng - v_order.service_lng)::DOUBLE PRECISION) / 2
                                ),
                                2
                            )
                        )
                    )
                )::NUMERIC(8,3)
            END AS distance_km
    ) distance_calc
    WHERE s.deleted_at IS NULL
      AND s.status = 1
      AND COALESCE(s.is_active, TRUE) = TRUE
      AND s.online_status = 'online'
      AND s.uid 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 (
          v_order.service_lat IS NULL
          OR v_order.service_lng IS NULL
          OR (
              s.current_lat IS NOT NULL
              AND s.current_lng IS NOT NULL
              AND distance_calc.distance_km <= COALESCE(s.dispatch_radius_km, 20)
          )
      )

      AND NOT EXISTS (
          SELECT 1
          FROM public.hss_service_assignments existing_assignment
          JOIN public.hss_service_orders existing_order
            ON existing_order.id = existing_assignment.order_id
          WHERE existing_assignment.staff_id::TEXT = s.id::TEXT
            AND existing_assignment.deleted_at IS NULL
            AND existing_assignment.status IN (
                'assigned',
                'accepted',
                'departed',
                'arrived',
                'serving',
                'in_service'
            )
            AND (
                v_order.scheduled_start_at IS NULL
                OR v_order.scheduled_end_at IS NULL
                OR existing_order.scheduled_start_at IS NULL
                OR existing_order.scheduled_end_at IS NULL
                OR tstzrange(
                    existing_order.scheduled_start_at,
                    existing_order.scheduled_end_at,
                    '[)'
                ) && tstzrange(
                    v_order.scheduled_start_at,
                    v_order.scheduled_end_at,
                    '[)'
                )
            )
      )

    ORDER BY
        CASE
            WHEN v_order.dispatch_station_id IS NOT NULL
             AND s.station_id::TEXT = v_order.dispatch_station_id
            THEN 0
            ELSE 1
        END,
        distance_calc.distance_km ASC NULLS LAST,
        COALESCE(s.last_dispatched_at, '-infinity'::TIMESTAMPTZ) ASC,
        COALESCE(s.updated_at, s.created_at) DESC

    LIMIT 1
    FOR UPDATE OF s SKIP LOCKED;

    v_candidate_found := FOUND;


    -- =========================================================================
    -- H. 没有找到候选人员时，识别具体失败原因
    -- =========================================================================

    IF NOT v_candidate_found THEN

        IF NOT EXISTS (
            SELECT 1
            FROM public.ml_delivery_staff s
            WHERE s.deleted_at IS NULL
              AND s.status = 1
              AND COALESCE(s.is_active, TRUE) = TRUE
              AND s.online_status = 'online'
              AND s.uid IS NOT NULL
        ) THEN
            v_failure_code := 'NO_ONLINE_STAFF';
            v_failure_message := '当前暂无在线服务人员，请稍后重新派单';
            v_retryable := TRUE;

        ELSIF v_order.dispatch_station_id IS NOT NULL
          AND NOT EXISTS (
              SELECT 1
              FROM public.ml_delivery_staff s
              WHERE s.deleted_at IS NULL
                AND s.status = 1
                AND COALESCE(s.is_active, TRUE) = TRUE
                AND s.online_status = 'online'
                AND s.uid IS NOT NULL
                AND s.station_id::TEXT = v_order.dispatch_station_id
          ) THEN
            v_failure_code := 'NO_STAFF_IN_SERVICE_STATION';
            v_failure_message := '当前服务区域暂无可派服务人员，请稍后重试或联系客服';
            v_retryable := TRUE;

        ELSIF v_order.required_qualification_code IS NOT NULL
          AND NOT EXISTS (
              SELECT 1
              FROM public.ml_delivery_staff s
              WHERE s.deleted_at IS NULL
                AND s.status = 1
                AND COALESCE(s.is_active, TRUE) = TRUE
                AND s.online_status = 'online'
                AND s.uid IS NOT NULL
                AND (
                    v_order.dispatch_station_id IS NULL
                    OR s.station_id::TEXT = v_order.dispatch_station_id
                )
                AND 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)
                )
          ) THEN
            v_failure_code := 'NO_QUALIFIED_STAFF';
            v_failure_message := '当前暂无具备该服务资质的服务人员，请稍后重试或联系客服';
            v_retryable := TRUE;

        ELSIF v_order.service_lat IS NOT NULL
          AND v_order.service_lng IS NOT NULL
          AND NOT EXISTS (
              SELECT 1
              FROM public.ml_delivery_staff s
              CROSS JOIN LATERAL (
                  SELECT
                      (
                          6371 * 2 * asin(
                              sqrt(
                                  power(
                                      sin(
                                          radians((s.current_lat - v_order.service_lat)::DOUBLE PRECISION) / 2
                                      ),
                                      2
                                  )
                                  +
                                  cos(radians(v_order.service_lat::DOUBLE PRECISION))
                                  * cos(radians(s.current_lat::DOUBLE PRECISION))
                                  * power(
                                      sin(
                                          radians((s.current_lng - v_order.service_lng)::DOUBLE PRECISION) / 2
                                      ),
                                      2
                                  )
                              )
                          )
                      )::NUMERIC(8,3) AS distance_km
              ) distance_calc
              WHERE s.deleted_at IS NULL
                AND s.status = 1
                AND COALESCE(s.is_active, TRUE) = TRUE
                AND s.online_status = 'online'
                AND s.uid IS NOT NULL
                AND s.current_lat IS NOT NULL
                AND s.current_lng 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 <= COALESCE(s.dispatch_radius_km, 20)
          ) THEN
            v_failure_code := 'NO_NEARBY_STAFF';
            v_failure_message := '附近暂无可上门服务人员，请稍后重试或联系客服';
            v_retryable := TRUE;

        ELSE
            v_failure_code := 'ALL_ELIGIBLE_STAFF_BUSY';
            v_failure_message := '当前可服务人员均在忙，请稍后重新派单';
            v_retryable := TRUE;
        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 || ':' || random()::TEXT), 1, 10));

        INSERT INTO public.hss_service_dispatch_attempt_logs (
            id,
            order_id,
            requested_by_user_id,
            selected_staff_id,
            selected_station_id,
            success,
            result_code,
            result_message,
            retryable,
            filters_snapshot,
            created_at
        ) VALUES (
            v_attempt_log_id,
            p_order_id,
            v_current_user_id,
            NULL,
            v_order.dispatch_station_id,
            FALSE,
            v_failure_code,
            v_failure_message,
            v_retryable,
            jsonb_build_object(
                'required_qualification_code', v_order.required_qualification_code,
                'dispatch_station_id', v_order.dispatch_station_id,
                'service_lat', v_order.service_lat,
                'service_lng', v_order.service_lng,
                'scheduled_start_at', v_order.scheduled_start_at,
                'scheduled_end_at', v_order.scheduled_end_at
            ),
            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 IF;


    -- =========================================================================
    -- I. 派单成功：创建派单记录
    -- =========================================================================

    v_assignment_id :=
        'sa-'
        || floor(extract(epoch FROM v_now) * 1000)::BIGINT::TEXT
        || '-'
        || upper(substr(md5(p_order_id || ':' || v_candidate.id::TEXT || ':' || random()::TEXT), 1, 10));

    INSERT INTO public.hss_service_assignments (
        id,
        order_id,
        staff_id,
        station_id,
        status,
        assigned_at,
        dispatch_distance_km,
        qualification_code_snapshot,
        created_at,
        updated_at
    ) VALUES (
        v_assignment_id,
        p_order_id,
        v_candidate.id,
        v_candidate.station_id,
        'assigned',
        v_now,
        v_candidate.distance_km,
        v_order.required_qualification_code,
        v_now,
        v_now
    );


    -- =========================================================================
    -- J. 更新订单与服务人员状态
    -- =========================================================================

    UPDATE public.hss_service_orders
    SET status = 'assigned',
        dispatch_status = 'assigned',
        current_assignment_id = v_assignment_id,
        current_staff_id = v_candidate.id,
        dispatch_error_code = NULL,
        dispatch_error_message = NULL,
        dispatch_failed_at = NULL,
        updated_at = v_now
    WHERE id = p_order_id;

    UPDATE public.ml_delivery_staff
    SET last_dispatched_at = v_now,
        updated_at = v_now
    WHERE id = v_candidate.id;


    -- =========================================================================
    -- K. 写入订单状态日志
    -- =========================================================================

    v_status_log_id :=
        'slog-'
        || floor(extract(epoch FROM v_now) * 1000)::BIGINT::TEXT
        || '-'
        || upper(substr(md5(p_order_id || ':status:' || random()::TEXT), 1, 10));

    INSERT INTO public.hss_service_order_status_logs (
        id,
        order_id,
        from_status,
        to_status,
        operator_id,
        operator_role,
        triggered_by_user_id,
        remark,
        created_at
    ) VALUES (
        v_status_log_id,
        p_order_id,
        'created',
        'assigned',
        v_current_user_id,
        'system',
        v_current_user_id,
        '消费者支付成功后触发系统自动派单',
        v_now
    );


    -- =========================================================================
    -- L. 写入派单成功尝试日志
    -- =========================================================================

    v_attempt_log_id :=
        'dalog-'
        || floor(extract(epoch FROM v_now) * 1000)::BIGINT::TEXT
        || '-'
        || upper(substr(md5(p_order_id || ':success:' || random()::TEXT), 1, 10));

    INSERT INTO public.hss_service_dispatch_attempt_logs (
        id,
        order_id,
        requested_by_user_id,
        selected_staff_id,
        selected_station_id,
        success,
        result_code,
        result_message,
        retryable,
        filters_snapshot,
        created_at
    ) VALUES (
        v_attempt_log_id,
        p_order_id,
        v_current_user_id,
        v_candidate.id::TEXT,
        COALESCE(v_candidate.station_id::TEXT, ''),
        TRUE,
        'DISPATCH_ASSIGNED',
        '系统已为您匹配服务人员',
        FALSE,
        jsonb_build_object(
            'required_qualification_code', v_order.required_qualification_code,
            'dispatch_station_id', v_order.dispatch_station_id,
            'service_lat', v_order.service_lat,
            'service_lng', v_order.service_lng,
            'scheduled_start_at', v_order.scheduled_start_at,
            'scheduled_end_at', v_order.scheduled_end_at,
            'dispatch_distance_km', v_candidate.distance_km
        ),
        v_now
    );


    -- =========================================================================
    -- M. 返回派单成功结果
    -- =========================================================================

    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_id', v_candidate.id,
        'station_id', COALESCE(v_candidate.station_id::TEXT, ''),
        'dispatch_distance_km', v_candidate.distance_km
    );


EXCEPTION
    WHEN unique_violation THEN
        -- 极端并发场景下，唯一索引阻止重复派单后，将结果转换为前端可处理的返回值。

        UPDATE public.hss_service_orders
        SET dispatch_status = 'failed',
            dispatch_error_code = 'DISPATCH_CONFLICT_RETRY',
            dispatch_error_message = '派单请求较多，请重新尝试',
            dispatch_failed_at = clock_timestamp(),
            updated_at = clock_timestamp()
        WHERE id = p_order_id
          AND status = 'created';

        RETURN jsonb_build_object(
            'success', FALSE,
            'code', 'DISPATCH_CONFLICT_RETRY',
            'message', '派单请求较多，请重新尝试',
            'display_type', 'modal',
            'retryable', TRUE,
            'dispatch_status', 'failed',
            'order_id', p_order_id
        );
END;
$function$;


-- =====================================================================================
-- 11. 限制 RPC 调用权限
-- =====================================================================================

REVOKE ALL ON FUNCTION public.rpc_homecare_auto_dispatch(TEXT) FROM PUBLIC;
REVOKE ALL ON FUNCTION public.rpc_homecare_auto_dispatch(TEXT) FROM anon;

GRANT EXECUTE ON FUNCTION public.rpc_homecare_auto_dispatch(TEXT) TO authenticated;


COMMENT ON FUNCTION public.rpc_homecare_auto_dispatch(TEXT)
IS '居家服务订单支付成功后的安全自动派单RPC：校验用户、支付、状态、资质、站点、距离与时间冲突，并返回结构化失败原因';


COMMIT;