<?php
/**
 * Eqtesad_Melk_Controller — پورتال همکاری بنگاه «اقتصاد ملک»
 */
if ( ! defined( 'ABSPATH' ) ) exit;

class Eqtesad_Melk_Controller {

    public static function register_routes(): void {
        $ns = 'acc/v1';
        $p  = [__CLASS__, 'perm_view'];
        $m  = [__CLASS__, 'perm_manage'];

        register_rest_route( $ns, '/melk/overview', [
            'methods' => 'GET', 'callback' => [self::class, 'get_overview'], 'permission_callback' => $p,
        ] );
        register_rest_route( $ns, '/melk/hierarchy', [
            'methods' => 'GET', 'callback' => [self::class, 'get_hierarchy'], 'permission_callback' => $p,
        ] );
        register_rest_route( $ns, '/melk/org-assignments', [
            ['methods' => 'GET',  'callback' => [self::class, 'get_org_assignments'], 'permission_callback' => $p],
            ['methods' => 'POST', 'callback' => [self::class, 'save_org_assignment'],  'permission_callback' => $m],
        ] );
        register_rest_route( $ns, '/melk/cases', [
            ['methods' => 'GET',  'callback' => [self::class, 'get_cases'],   'permission_callback' => $p],
            ['methods' => 'POST', 'callback' => [self::class, 'create_case'], 'permission_callback' => $p],
        ] );
        register_rest_route( $ns, '/melk/cases/(?P<id>\d+)', [
            ['methods' => 'GET', 'callback' => [self::class, 'get_case'],  'permission_callback' => $p],
            ['methods' => 'PUT', 'callback' => [self::class, 'update_case'], 'permission_callback' => $p],
        ] );
        register_rest_route( $ns, '/melk/cases/(?P<id>\d+)/notes', [
            'methods' => 'POST', 'callback' => [self::class, 'add_case_note'], 'permission_callback' => $p,
        ] );
        register_rest_route( $ns, '/melk/cases/(?P<id>\d+)/participants', [
            'methods' => 'POST', 'callback' => [self::class, 'add_participant'], 'permission_callback' => $p,
        ] );
        register_rest_route( $ns, '/melk/participant-roles', [
            'methods' => 'GET', 'callback' => [self::class, 'get_participant_roles'], 'permission_callback' => $p,
        ] );
        register_rest_route( $ns, '/melk/users', [
            'methods' => 'GET', 'callback' => [self::class, 'search_users'], 'permission_callback' => $p,
        ] );
        register_rest_route( $ns, '/melk/org-assignments/(?P<id>\d+)', [
            'methods' => 'DELETE', 'callback' => [self::class, 'delete_org_assignment'], 'permission_callback' => $m,
        ] );
        register_rest_route( $ns, '/melk/cases/(?P<id>\d+)/participants/(?P<pid>\d+)', [
            'methods' => 'DELETE', 'callback' => [self::class, 'remove_participant'], 'permission_callback' => $p,
        ] );
        register_rest_route( $ns, '/melk/renovation-hub', [
            'methods' => 'GET', 'callback' => [self::class, 'get_renovation_hub'], 'permission_callback' => [self::class, 'perm_renovation_hub'],
        ] );
        register_rest_route( $ns, '/melk/renovation-codes', [
            'methods' => 'GET', 'callback' => [self::class, 'search_renovation_codes'], 'permission_callback' => [self::class, 'perm_renovation_hub'],
        ] );
    }

    public static function perm_renovation_hub(): bool {
        if ( ! is_user_logged_in() ) return false;
        $uid = acl_current_user_id();
        return acl_user_is_super_admin( $uid ) || acl_user_can( $uid, 'melk.manage' );
    }

    public static function perm_view(): bool {
        if ( ! is_user_logged_in() ) return false;
        $uid = acl_current_user_id();
        return acl_user_is_super_admin( $uid ) || acl_user_can( $uid, 'melk.view' );
    }

    public static function perm_manage(): bool {
        if ( ! is_user_logged_in() ) return false;
        $uid = acl_current_user_id();
        return acl_user_is_super_admin( $uid ) || acl_user_can( $uid, 'melk.manage' );
    }

    public static function get_participant_roles(): WP_REST_Response {
        return new WP_REST_Response( [
            'data' => [
                ['key' => 'agent',        'label' => 'مشاور املاک'],
                ['key' => 'legal',        'label' => 'وکیل حقوقی'],
                ['key' => 'municipality', 'label' => 'کارشناس شهرداری'],
                ['key' => 'registry',     'label' => 'کارشناس ثبت'],
                ['key' => 'appraisal',    'label' => 'کارشناس رسمی'],
                ['key' => 'contractor',   'label' => 'پیمانکار ساخت'],
                ['key' => 'finance',      'label' => 'کارشناس مالی'],
                ['key' => 'other',        'label' => 'سایر'],
            ],
        ] );
    }

    public static function get_overview( WP_REST_Request $req ): WP_REST_Response {
        global $wpdb;
        $uid  = acl_current_user_id();
        $pre  = $wpdb->prefix;
        $city = sanitize_text_field( $req->get_param( 'city' ) ?? '' );

        $d_wheres = ["d.city != ''"];
        $d_args   = [];
        if ( $city ) {
            $d_wheres[] = 'd.city = %s';
            $d_args[]   = $city;
        }
        if ( ! self::_append_district_list_scope( $uid, 'd.id', $d_wheres, $d_args ) ) {
            return new WP_REST_Response( self::_empty_overview() );
        }

        $sql = "SELECT d.id, d.name, d.city, d.code, d.color,
                       COALESCE(pc.cnt, 0) AS parcel_count,
                       COALESCE(nc.cnt, 0) AS neighborhood_count,
                       oa.user_id AS lead_user_id, oa.user_name AS lead_name
                FROM {$pre}gis_districts d
                LEFT JOIN (
                    SELECT district_id, COUNT(*) AS cnt FROM {$pre}gis_parcels
                    WHERE status != 'archived' GROUP BY district_id
                ) pc ON pc.district_id = d.id
                LEFT JOIN (
                    SELECT district_id, COUNT(*) AS cnt FROM {$pre}gis_neighborhoods GROUP BY district_id
                ) nc ON nc.district_id = d.id
                LEFT JOIN {$pre}melk_org_assignments oa
                    ON oa.unit_type = 'district' AND oa.unit_id = d.id AND oa.role_slug = 'district_lead'
                WHERE " . implode( ' AND ', $d_wheres ) . "
                ORDER BY d.name";
        $districts = $d_args
            ? $wpdb->get_results( $wpdb->prepare( $sql, ...$d_args ), ARRAY_A )
            : $wpdb->get_results( $sql, ARRAY_A );

        $case_wheres = ['1=1'];
        $case_args   = [];
        if ( ! acl_append_geo_scope_where( $uid, 'c.district_id', 'c.neighborhood_id', $case_wheres, $case_args ) ) {
            $open_cases = 0;
        } else {
            $cw = implode( ' AND ', $case_wheres );
            $open_cases = (int) ( $case_args
                ? $wpdb->get_var( $wpdb->prepare(
                    "SELECT COUNT(*) FROM {$pre}melk_cases c WHERE status IN ('open','in_progress','review') AND {$cw}",
                    ...$case_args
                ) )
                : $wpdb->get_var(
                    "SELECT COUNT(*) FROM {$pre}melk_cases c WHERE status IN ('open','in_progress','review') AND {$cw}"
                ) );
        }

        $scopes    = acl_get_current_user_scopes();
        $role_hint = sanitize_key( $req->get_param('role_hint') ?? '' );
        $role      = self::_user_melk_role_label( $uid, $role_hint );

        return new WP_REST_Response( [
            'city'           => $city ?: ( $districts[0]['city'] ?? 'شیراز' ),
            'district_count' => count( $districts ?: [] ),
            'open_cases'     => $open_cases,
            'user_role'      => $role,
            'scopes'         => $scopes,
            'districts'      => $districts ?: [],
        ] );
    }

    public static function get_hierarchy( WP_REST_Request $req ): WP_REST_Response {
        global $wpdb;
        $uid   = acl_current_user_id();
        $pre   = $wpdb->prefix;
        $level = sanitize_text_field( $req->get_param( 'level' ) ?? 'district' );
        $district_id     = (int) $req->get_param( 'district' );
        $neighborhood_id = (int) $req->get_param( 'neighborhood' );

        if ( $level === 'neighborhood' && $district_id ) {
            $deny = acl_deny_scope( $uid, 'district', $district_id, 'melk.view' );
            if ( $deny ) return $deny;

            $wheres = ['n.district_id = %d'];
            $args   = [$district_id];
            if ( ! acl_append_geo_scope_where( $uid, 'n.district_id', 'n.id', $wheres, $args ) ) {
                return new WP_REST_Response( ['level' => 'neighborhood', 'data' => []] );
            }

            $sql = "SELECT n.id, n.name, n.district_id, n.dominant_use,
                           COALESCE(pc.cnt, 0) AS parcel_count,
                           COALESCE(bc.cnt, 0) AS block_count,
                           oa.user_id AS manager_user_id, oa.user_name AS manager_name
                    FROM {$pre}gis_neighborhoods n
                    LEFT JOIN (
                        SELECT neighborhood_id, COUNT(*) AS cnt FROM {$pre}gis_parcels
                        WHERE status != 'archived' AND neighborhood_id IS NOT NULL GROUP BY neighborhood_id
                    ) pc ON pc.neighborhood_id = n.id
                    LEFT JOIN (
                        SELECT neighborhood_id, COUNT(DISTINCT block_code) AS cnt FROM {$pre}gis_parcels
                        WHERE block_code IS NOT NULL AND block_code != '' GROUP BY neighborhood_id
                    ) bc ON bc.neighborhood_id = n.id
                    LEFT JOIN {$pre}melk_org_assignments oa
                        ON oa.unit_type = 'neighborhood' AND oa.unit_id = n.id AND oa.role_slug = 'neighborhood_manager'
                    WHERE " . implode( ' AND ', $wheres ) . "
                    ORDER BY n.name";
            $rows = $wpdb->get_results( $wpdb->prepare( $sql, ...$args ), ARRAY_A );
            return new WP_REST_Response( ['level' => 'neighborhood', 'data' => $rows ?: []] );
        }

        if ( $level === 'block' && $neighborhood_id ) {
            $nrow = $wpdb->get_row( $wpdb->prepare(
                "SELECT district_id FROM {$pre}gis_neighborhoods WHERE id = %d", $neighborhood_id
            ), ARRAY_A );
            if ( ! $nrow ) return new WP_REST_Response( ['message' => 'محله یافت نشد'], 404 );

            $deny = acl_deny_scope( $uid, 'district', (int) $nrow['district_id'], 'melk.view' );
            if ( $deny ) return $deny;
            $deny = acl_deny_scope( $uid, 'neighborhood', $neighborhood_id, 'melk.view' );
            if ( $deny ) return $deny;

            $wheres = ['bs.neighborhood_id = %d'];
            $args   = [$neighborhood_id];
            if ( ! acl_append_geo_scope_where( $uid, 'bs.district_id', 'bs.neighborhood_id', $wheres, $args ) ) {
                return new WP_REST_Response( ['level' => 'block', 'data' => []] );
            }

            $sql = "SELECT bs.block_code, bs.district_id, bs.neighborhood_id,
                           bs.total_parcels AS parcel_count,
                           bs.cnt_building AS bld_count,
                           bs.cnt_apartment AS apt_count,
                           bs.cnt_shop AS shop_count,
                           bs.center_lat, bs.center_lng,
                           (SELECT COUNT(*) FROM {$pre}melk_cases mc
                            WHERE mc.block_code = bs.block_code AND mc.status NOT IN ('closed','archived')) AS open_cases
                    FROM {$pre}gis_block_stats bs
                    WHERE " . implode( ' AND ', $wheres ) . "
                    ORDER BY bs.total_parcels DESC";
            $rows = $wpdb->get_results( $wpdb->prepare( $sql, ...$args ), ARRAY_A );
            return new WP_REST_Response( ['level' => 'block', 'data' => $rows ?: []] );
        }

        return self::get_overview( $req );
    }

    public static function get_org_assignments( WP_REST_Request $req ): WP_REST_Response {
        global $wpdb;
        $uid = acl_current_user_id();
        if ( ! acl_user_can( $uid, 'melk.manage' ) && ! acl_user_is_super_admin( $uid ) ) {
            return new WP_REST_Response( ['message' => 'دسترسی مدیریت سازمان لازم است'], 403 );
        }

        $rows = $wpdb->get_results(
            "SELECT oa.*, d.name AS district_name, n.name AS neighborhood_name
             FROM {$wpdb->prefix}melk_org_assignments oa
             LEFT JOIN {$wpdb->prefix}gis_districts d ON oa.unit_type = 'district' AND oa.unit_id = d.id
             LEFT JOIN {$wpdb->prefix}gis_neighborhoods n ON oa.unit_type = 'neighborhood' AND oa.unit_id = n.id
             ORDER BY oa.unit_type, oa.unit_id",
            ARRAY_A
        );
        return new WP_REST_Response( ['data' => $rows ?: []] );
    }

    public static function save_org_assignment( WP_REST_Request $req ): WP_REST_Response {
        global $wpdb;
        $unit_type = sanitize_text_field( $req->get_param( 'unit_type' ) ?? '' );
        $unit_id   = (int) $req->get_param( 'unit_id' );
        $user_id   = (int) $req->get_param( 'user_id' );
        $role_slug = sanitize_key( $req->get_param( 'role_slug' ) ?? 'district_lead' );

        if ( ! in_array( $unit_type, ['district', 'neighborhood'], true ) || ! $unit_id || ! $user_id ) {
            return new WP_REST_Response( ['message' => 'unit_type، unit_id و user_id الزامی است'], 400 );
        }

        $user_name = $wpdb->get_var( $wpdb->prepare(
            "SELECT display_name FROM {$wpdb->users} WHERE ID = %d", $user_id
        ) );

        $wpdb->replace( $wpdb->prefix . 'melk_org_assignments', [
            'unit_type' => $unit_type,
            'unit_id'   => $unit_id,
            'user_id'   => $user_id,
            'role_slug' => $role_slug,
            'user_name' => $user_name,
        ] );

        return new WP_REST_Response( ['success' => true] );
    }

    public static function get_cases( WP_REST_Request $req ): WP_REST_Response {
        global $wpdb;
        $uid = acl_current_user_id();
        $pre = $wpdb->prefix;

        $wheres = ['1=1'];
        $args   = [];

        if ( ! acl_user_is_super_admin( $uid ) ) {
            $geo_w = ['1=1'];
            $geo_a = [];
            $geo_ok = acl_append_geo_scope_where( $uid, 'c.district_id', 'c.neighborhood_id', $geo_w, $geo_a );

            if ( $geo_ok && count( $geo_w ) > 1 ) {
                $access = '(' . implode( ' AND ', array_slice( $geo_w, 1 ) ) . ')';
                $access .= " OR c.id IN (SELECT case_id FROM {$pre}melk_case_participants WHERE user_id = %d)";
                $wheres[] = '(' . $access . ')';
                $args     = array_merge( $args, $geo_a, [$uid] );
            } elseif ( ! $geo_ok ) {
                $wheres[] = "c.id IN (SELECT case_id FROM {$pre}melk_case_participants WHERE user_id = %d)";
                $args[]   = $uid;
            }
        }

        $status = sanitize_text_field( $req->get_param( 'status' ) ?? '' );
        if ( $status ) {
            $wheres[] = 'c.status = %s';
            $args[]   = $status;
        }
        $district_id = (int) $req->get_param( 'district_id' );
        if ( $district_id ) {
            $wheres[] = 'c.district_id = %d';
            $args[]   = $district_id;
        }
        $neighborhood_id = (int) $req->get_param( 'neighborhood_id' );
        if ( $neighborhood_id ) {
            $wheres[] = 'c.neighborhood_id = %d';
            $args[]   = $neighborhood_id;
        }
        $block = sanitize_text_field( $req->get_param( 'block' ) ?? '' );
        if ( $block ) {
            $wheres[] = 'c.block_code = %s';
            $args[]   = $block;
        }

        $sql = "SELECT c.*, d.name AS district_name, n.name AS neighborhood_name,
                       p.code AS parcel_code, p.status AS parcel_status,
                       COALESCE(c.renovation_code, p.code) AS renovation_code_display
                FROM {$pre}melk_cases c
                LEFT JOIN {$pre}gis_districts d ON d.id = c.district_id
                LEFT JOIN {$pre}gis_neighborhoods n ON n.id = c.neighborhood_id
                LEFT JOIN {$pre}gis_parcels p ON p.id = c.parcel_id
                WHERE " . implode( ' AND ', $wheres ) . "
                ORDER BY c.updated_at DESC
                LIMIT 200";

        $rows = $args
            ? $wpdb->get_results( $wpdb->prepare( $sql, ...$args ), ARRAY_A )
            : $wpdb->get_results( $sql, ARRAY_A );

        return new WP_REST_Response( ['data' => $rows ?: []] );
    }

    public static function get_case( WP_REST_Request $req ): WP_REST_Response {
        global $wpdb;
        $id  = (int) $req->get_param( 'id' );
        $uid = acl_current_user_id();
        $pre = $wpdb->prefix;

        $case = $wpdb->get_row( $wpdb->prepare(
            "SELECT c.*, d.name AS district_name, n.name AS neighborhood_name,
                    p.code AS parcel_code, p.status AS parcel_status, p.area_sqm,
                    p.address AS parcel_address, p.lat AS parcel_lat, p.lng AS parcel_lng,
                    COALESCE(c.renovation_code, p.code) AS renovation_code_display
             FROM {$pre}melk_cases c
             LEFT JOIN {$pre}gis_districts d ON d.id = c.district_id
             LEFT JOIN {$pre}gis_neighborhoods n ON n.id = c.neighborhood_id
             LEFT JOIN {$pre}gis_parcels p ON p.id = c.parcel_id
             WHERE c.id = %d", $id
        ), ARRAY_A );

        if ( ! $case ) return new WP_REST_Response( ['message' => 'یافت نشد'], 404 );

        $deny = self::_deny_case_access( $uid, $case );
        if ( $deny ) return $deny;

        self::_ensure_participant_meta_cols();
        $case = self::_enrich_case_detail( $case );

        return new WP_REST_Response( ['data' => $case] );
    }

    public static function create_case( WP_REST_Request $req ): WP_REST_Response {
        if ( ! acl_user_can( acl_current_user_id(), 'melk.cases.create' ) ) {
            return new WP_REST_Response( ['message' => 'مجوز ایجاد پرونده ندارید'], 403 );
        }

        global $wpdb;
        $uid  = acl_current_user_id();
        $pre  = $wpdb->prefix;
        $body = $req->get_json_params() ?: [];

        $parcel_id = (int) ( $body['parcel_id'] ?? $req->get_param( 'parcel_id' ) );
        $title     = sanitize_text_field( $body['title'] ?? $req->get_param( 'title' ) ?? '' );
        if ( ! $title ) $title = 'پرونده ملک';

        $district_id = (int) ( $body['district_id'] ?? 0 );
        $neighborhood_id = (int) ( $body['neighborhood_id'] ?? 0 );
        $block_code  = sanitize_text_field( $body['block_code'] ?? '' );
        $reno_code   = sanitize_text_field( $body['renovation_code'] ?? $req->get_param( 'renovation_code' ) ?? '' );

        if ( $parcel_id ) {
            $parcel = $wpdb->get_row( $wpdb->prepare(
                "SELECT id, code, district_id, neighborhood_id, block_code FROM {$pre}gis_parcels WHERE id = %d", $parcel_id
            ), ARRAY_A );
            if ( ! $parcel ) return new WP_REST_Response( ['message' => 'پارسل یافت نشد'], 404 );
            $district_id     = (int) $parcel['district_id'];
            $neighborhood_id = (int) $parcel['neighborhood_id'];
            $block_code      = $parcel['block_code'] ?? $block_code;
            if ( ! $reno_code && ! empty( $parcel['code'] ) ) {
                $reno_code = $parcel['code'];
            }
            if ( ! $title || $title === 'پرونده ملک' ) {
                $title = 'پرونده ' . ( $parcel['code'] ?? $parcel_id );
            }
        }

        if ( ! $reno_code && $block_code ) {
            $reno_code = self::_block_level_renovation_code( $block_code );
        }

        $deny = acl_deny_scope( $uid, 'district', $district_id ?: null, 'melk.cases.create' );
        if ( $deny ) return $deny;
        if ( $neighborhood_id ) {
            $deny = acl_deny_scope( $uid, 'neighborhood', $neighborhood_id, 'melk.cases.create' );
            if ( $deny ) return $deny;
        }

        $user_name = $wpdb->get_var( $wpdb->prepare(
            "SELECT display_name FROM {$wpdb->users} WHERE ID = %d", $uid
        ) );
        $case_key = 'MK-' . date( 'ymd' ) . '-' . wp_rand( 1000, 9999 );

        $wpdb->insert( $pre . 'melk_cases', [
            'case_key'        => $case_key,
            'parcel_id'       => $parcel_id ?: null,
            'title'           => $title,
            'status'          => 'open',
            'district_id'     => $district_id ?: null,
            'neighborhood_id' => $neighborhood_id ?: null,
            'block_code'      => $block_code ?: null,
            'renovation_code' => $reno_code ?: null,
            'created_by'      => $uid,
            'created_by_name' => $user_name,
            'assigned_to'     => $uid,
            'assigned_name'   => $user_name,
            'summary'         => sanitize_textarea_field( $body['summary'] ?? '' ),
        ] );

        $case_id = (int) $wpdb->insert_id;
        $wpdb->insert( $pre . 'melk_case_participants', [
            'case_id'   => $case_id,
            'user_id'   => $uid,
            'user_name' => $user_name,
            'role_type' => 'agent',
            'added_by'  => $uid,
            'added_by_name' => $user_name,
        ] );

        return new WP_REST_Response( [
            'success' => true,
            'id'      => $case_id,
            'case_key'=> $case_key,
        ], 201 );
    }

    public static function update_case( WP_REST_Request $req ): WP_REST_Response {
        global $wpdb;
        $id  = (int) $req->get_param( 'id' );
        $uid = acl_current_user_id();
        $pre = $wpdb->prefix;

        $case = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$pre}melk_cases WHERE id = %d", $id ), ARRAY_A );
        if ( ! $case ) return new WP_REST_Response( ['message' => 'یافت نشد'], 404 );

        $deny = self::_deny_case_access( $uid, $case );
        if ( $deny ) return $deny;

        if ( ! acl_user_can( $uid, 'melk.cases.edit' ) && ! acl_user_can( $uid, 'melk.cases.participate' ) ) {
            return new WP_REST_Response( ['message' => 'مجوز ویرایش ندارید'], 403 );
        }

        $data = [];
        $body = $req->get_json_params() ?: [];
        if ( isset( $body['status'] ) ) $data['status'] = sanitize_text_field( $body['status'] );
        if ( isset( $body['title'] ) )  $data['title']  = sanitize_text_field( $body['title'] );
        if ( isset( $body['summary'] ) ) $data['summary'] = sanitize_textarea_field( $body['summary'] );
        if ( isset( $body['assigned_to'] ) ) {
            $data['assigned_to'] = (int) $body['assigned_to'];
            $data['assigned_name'] = $wpdb->get_var( $wpdb->prepare(
                "SELECT display_name FROM {$wpdb->users} WHERE ID = %d", (int) $body['assigned_to']
            ) );
        }
        if ( isset( $body['renovation_code'] ) && ( acl_user_can( $uid, 'melk.cases.edit' ) || acl_user_can( $uid, 'melk.manage' ) ) ) {
            $data['renovation_code'] = sanitize_text_field( $body['renovation_code'] ) ?: null;
        }
        if ( isset( $body['parcel_id'] ) && acl_user_can( $uid, 'melk.cases.edit' ) ) {
            $pid = (int) $body['parcel_id'];
            if ( $pid ) {
                $parcel = $wpdb->get_row( $wpdb->prepare(
                    "SELECT id, code, district_id, neighborhood_id, block_code FROM {$pre}gis_parcels WHERE id = %d", $pid
                ), ARRAY_A );
                if ( $parcel ) {
                    $data['parcel_id']       = $pid;
                    $data['district_id']     = (int) $parcel['district_id'];
                    $data['neighborhood_id'] = (int) $parcel['neighborhood_id'];
                    $data['block_code']      = $parcel['block_code'] ?? null;
                    if ( ! empty( $parcel['code'] ) ) {
                        $data['renovation_code'] = $parcel['code'];
                    }
                }
            }
        }
        if ( $data ) $wpdb->update( $pre . 'melk_cases', $data, ['id' => $id] );

        return new WP_REST_Response( ['success' => true] );
    }

    public static function search_renovation_codes( WP_REST_Request $req ): WP_REST_Response {
        global $wpdb;
        $pre = $wpdb->prefix;
        $q   = trim( sanitize_text_field( $req->get_param( 'q' ) ?? '' ) );
        if ( strlen( $q ) < 2 ) {
            return new WP_REST_Response( ['data' => []] );
        }
        $like = $wpdb->esc_like( $q ) . '%';
        $from_parcels = $wpdb->get_results( $wpdb->prepare(
            "SELECT DISTINCT code AS renovation_code, block_code, id AS parcel_id
             FROM {$pre}gis_parcels
             WHERE code LIKE %s AND code IS NOT NULL AND code != '' AND status != 'archived'
             ORDER BY code ASC LIMIT 25",
            $like
        ), ARRAY_A ) ?: [];
        $from_cases = $wpdb->get_results( $wpdb->prepare(
            "SELECT DISTINCT renovation_code, block_code, parcel_id
             FROM {$pre}melk_cases
             WHERE renovation_code LIKE %s AND renovation_code IS NOT NULL AND renovation_code != ''
             ORDER BY renovation_code ASC LIMIT 15",
            $like
        ), ARRAY_A ) ?: [];

        $seen = [];
        $out  = [];
        foreach ( array_merge( $from_parcels, $from_cases ) as $row ) {
            $code = $row['renovation_code'] ?? '';
            if ( ! $code || isset( $seen[ $code ] ) ) continue;
            $seen[ $code ] = true;
            $out[] = $row;
        }
        return new WP_REST_Response( ['data' => array_slice( $out, 0, 30 )] );
    }

    public static function get_renovation_hub( WP_REST_Request $req ): WP_REST_Response {
        global $wpdb;
        $pre  = $wpdb->prefix;
        $code = trim( sanitize_text_field( $req->get_param( 'code' ) ?? '' ) );
        if ( ! $code ) {
            return new WP_REST_Response( ['message' => 'کد نوسازی الزامی است'], 400 );
        }

        self::_ensure_renovation_code_col();

        $parcel = $wpdb->get_row( $wpdb->prepare(
            "SELECT p.*, d.name AS district_name, n.name AS neighborhood_name
             FROM {$pre}gis_parcels p
             LEFT JOIN {$pre}gis_districts d ON d.id = p.district_id
             LEFT JOIN {$pre}gis_neighborhoods n ON n.id = p.neighborhood_id
             WHERE p.code = %s AND p.status != 'archived'
             LIMIT 1",
            $code
        ), ARRAY_A );

        $block_code = $parcel['block_code'] ?? self::_block_from_renovation_code( $code );

        $cases = $wpdb->get_results( $wpdb->prepare(
            "SELECT c.*, d.name AS district_name, n.name AS neighborhood_name,
                    (SELECT COUNT(*) FROM {$pre}melk_case_participants mp WHERE mp.case_id = c.id) AS participant_count,
                    (SELECT COUNT(*) FROM {$pre}melk_case_notes mn WHERE mn.case_id = c.id) AS note_count
             FROM {$pre}melk_cases c
             LEFT JOIN {$pre}gis_districts d ON d.id = c.district_id
             LEFT JOIN {$pre}gis_neighborhoods n ON n.id = c.neighborhood_id
             WHERE c.renovation_code = %s
                OR (%d > 0 AND c.parcel_id = %d)
             ORDER BY FIELD(c.status,'in_progress','open','review','closed'), c.updated_at DESC",
            $code,
            (int) ( $parcel['id'] ?? 0 ),
            (int) ( $parcel['id'] ?? 0 )
        ), ARRAY_A ) ?: [];

        $status_labels = [
            'open' => 'باز', 'in_progress' => 'در جریان', 'review' => 'بررسی', 'closed' => 'بسته',
        ];
        foreach ( $cases as &$c ) {
            $c['status_label'] = $status_labels[ $c['status'] ?? 'open' ] ?? $c['status'];
            $c['renovation_code_display'] = $c['renovation_code'] ?: $code;
        }
        unset( $c );

        $gis_summary = null;
        if ( $parcel ) {
            $pid = (int) $parcel['id'];
            $gis_summary = [
                'parcel_id'        => $pid,
                'code'             => $parcel['code'],
                'block_code'       => $parcel['block_code'],
                'address'          => $parcel['address'] ?? '',
                'status'           => $parcel['status'] ?? '',
                'district_name'    => $parcel['district_name'] ?? '',
                'neighborhood_name'=> $parcel['neighborhood_name'] ?? '',
                'lat'              => $parcel['lat'] ?? null,
                'lng'              => $parcel['lng'] ?? null,
                'land'             => $wpdb->get_row( $wpdb->prepare(
                    "SELECT area_sqm, shape, ownership_type FROM {$pre}gis_parcel_land WHERE parcel_id = %d", $pid
                ), ARRAY_A ),
                'building'         => $wpdb->get_row( $wpdb->prepare(
                    "SELECT has_building, floors, units, total_area_sqm, built_year FROM {$pre}gis_parcel_building WHERE parcel_id = %d", $pid
                ), ARRAY_A ),
                'use'              => $wpdb->get_row( $wpdb->prepare(
                    "SELECT current_use, approved_use, density_pct FROM {$pre}gis_parcel_use WHERE parcel_id = %d", $pid
                ), ARRAY_A ),
                'owners'           => $wpdb->get_results( $wpdb->prepare(
                    "SELECT name, phone, ownership_share, is_primary FROM {$pre}gis_parcel_owners WHERE parcel_id = %d ORDER BY is_primary DESC LIMIT 5", $pid
                ), ARRAY_A ) ?: [],
                'units'            => $wpdb->get_results( $wpdb->prepare(
                    "SELECT renovation_code FROM {$pre}gis_parcel_units WHERE parcel_id = %d ORDER BY renovation_code LIMIT 20", $pid
                ), ARRAY_A ) ?: [],
            ];
        }

        $block_parcels = [];
        if ( $block_code ) {
            $block_parcels = $wpdb->get_results( $wpdb->prepare(
                "SELECT id, code, status, address FROM {$pre}gis_parcels
                 WHERE block_code = %s AND status != 'archived'
                 ORDER BY code ASC LIMIT 50",
                $block_code
            ), ARRAY_A ) ?: [];
        }

        $block_cases = [];
        if ( $block_code ) {
            $block_cases = $wpdb->get_results( $wpdb->prepare(
                "SELECT id, case_key, title, status, renovation_code, parcel_id
                 FROM {$pre}melk_cases
                 WHERE block_code = %s AND (renovation_code IS NULL OR renovation_code != %s)
                 ORDER BY updated_at DESC LIMIT 20",
                $block_code, $code
            ), ARRAY_A ) ?: [];
        }

        $commission = self::_commission_summary_for_code( $code );

        return new WP_REST_Response( [
            'data' => [
                'renovation_code' => $code,
                'block_code'      => $block_code,
                'gis'             => $gis_summary,
                'melk_cases'      => $cases,
                'melk_case_count' => count( $cases ),
                'block_parcels'   => $block_parcels,
                'block_cases'     => $block_cases,
                'commission'      => $commission,
            ],
        ] );
    }

    private static function _commission_summary_for_code( string $code ): array {
        global $wpdb;
        $pre = $wpdb->prefix;
        $tbl = $pre . 'gis_trepass';
        $exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $tbl ) );
        if ( ! $exists ) {
            return ['available' => false, 'file_count' => 0, 'files' => []];
        }
        $files = $wpdb->get_results( $wpdb->prepare(
            "SELECT NidCommission, OwnerName, Address, CommissionDate, CI_Commission, CI_CommissionType, CommissionState
             FROM {$tbl}
             WHERE CodeString = %s
             ORDER BY CommissionDate DESC
             LIMIT 10",
            $code
        ), ARRAY_A ) ?: [];
        return [
            'available'  => true,
            'file_count' => count( $files ),
            'files'      => $files,
        ];
    }

    private static function _block_from_renovation_code( string $code ): ?string {
        $parts = explode( '-', $code );
        return count( $parts ) >= 3 ? implode( '-', array_slice( $parts, 0, 3 ) ) : null;
    }

    private static function _block_level_renovation_code( string $block_code ): string {
        return $block_code . '-0-0-0-0';
    }

    private static function _ensure_renovation_code_col(): void {
        global $wpdb;
        static $done = false;
        if ( $done ) return;
        $done = true;
        $table = $wpdb->prefix . 'melk_cases';
        $cols  = $wpdb->get_col( "SHOW COLUMNS FROM {$table}" );
        if ( ! $cols || in_array( 'renovation_code', $cols, true ) ) return;
        $wpdb->query( "ALTER TABLE {$table} ADD COLUMN renovation_code VARCHAR(200) NULL AFTER block_code" );
        $wpdb->query( "ALTER TABLE {$table} ADD INDEX idx_renovation (renovation_code)" );
    }

    public static function sync_case_renovation_codes(): int {
        global $wpdb;
        $pre = $wpdb->prefix;
        self::_ensure_renovation_code_col();
        $rows = $wpdb->get_results(
            "SELECT c.id, c.parcel_id, c.block_code, c.renovation_code, p.code AS parcel_code
             FROM {$pre}melk_cases c
             LEFT JOIN {$pre}gis_parcels p ON p.id = c.parcel_id",
            ARRAY_A
        ) ?: [];
        $updated = 0;
        foreach ( $rows as $row ) {
            $next = $row['renovation_code'];
            if ( ! empty( $row['parcel_code'] ) ) {
                $next = $row['parcel_code'];
            } elseif ( ! $next && ! empty( $row['block_code'] ) ) {
                $next = self::_block_level_renovation_code( $row['block_code'] );
            }
            if ( $next && $next !== ( $row['renovation_code'] ?? '' ) ) {
                $wpdb->update( "{$pre}melk_cases", ['renovation_code' => $next], ['id' => (int) $row['id']] );
                $updated++;
            }
        }
        return $updated;
    }

    public static function add_case_note( WP_REST_Request $req ): WP_REST_Response {
        global $wpdb;
        $id  = (int) $req->get_param( 'id' );
        $uid = acl_current_user_id();
        $pre = $wpdb->prefix;
        $body = sanitize_textarea_field( $req->get_param( 'body' ) ?? ( $req->get_json_params()['body'] ?? '' ) );

        if ( ! $body ) return new WP_REST_Response( ['message' => 'متن یادداشت الزامی است'], 400 );

        $case = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$pre}melk_cases WHERE id = %d", $id ), ARRAY_A );
        if ( ! $case ) return new WP_REST_Response( ['message' => 'یافت نشد'], 404 );

        $deny = self::_deny_case_access( $uid, $case );
        if ( $deny ) return $deny;

        $user_name = $wpdb->get_var( $wpdb->prepare(
            "SELECT display_name FROM {$wpdb->users} WHERE ID = %d", $uid
        ) );

        $wpdb->insert( $pre . 'melk_case_notes', [
            'case_id'   => $id,
            'user_id'   => $uid,
            'user_name' => $user_name,
            'body'      => $body,
            'note_type' => sanitize_text_field( $req->get_param( 'note_type' ) ?? 'note' ),
        ] );

        return new WP_REST_Response( ['success' => true, 'id' => (int) $wpdb->insert_id], 201 );
    }

    public static function add_participant( WP_REST_Request $req ): WP_REST_Response {
        global $wpdb;
        $id  = (int) $req->get_param( 'id' );
        $uid = acl_current_user_id();
        $pre = $wpdb->prefix;

        $case = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$pre}melk_cases WHERE id = %d", $id ), ARRAY_A );
        if ( ! $case ) return new WP_REST_Response( ['message' => 'یافت نشد'], 404 );

        $deny = self::_deny_case_access( $uid, $case );
        if ( $deny ) return $deny;

        $user_id   = (int) $req->get_param( 'user_id' );
        $role_type = sanitize_key( $req->get_param( 'role_type' ) ?? 'other' );
        if ( ! $user_id ) return new WP_REST_Response( ['message' => 'user_id الزامی است'], 400 );

        $user_name = $wpdb->get_var( $wpdb->prepare(
            "SELECT display_name FROM {$wpdb->users} WHERE ID = %d", $user_id
        ) );
        $adder_name = $wpdb->get_var( $wpdb->prepare(
            "SELECT display_name FROM {$wpdb->users} WHERE ID = %d", $uid
        ) );

        self::_ensure_participant_meta_cols();
        $wpdb->query( $wpdb->prepare(
            "INSERT IGNORE INTO {$pre}melk_case_participants (case_id, user_id, user_name, role_type, added_by, added_by_name) VALUES (%d, %d, %s, %s, %d, %s)",
            $id, $user_id, $user_name, $role_type, $uid, $adder_name
        ) );

        return new WP_REST_Response( ['success' => true] );
    }

    public static function search_users( WP_REST_Request $req ): WP_REST_Response {
        global $wpdb;
        $q = sanitize_text_field( $req->get_param( 'q' ) ?? '' );
        if ( strlen( $q ) < 1 ) {
            return new WP_REST_Response( ['data' => []] );
        }
        $like = '%' . $wpdb->esc_like( $q ) . '%';
        $rows = $wpdb->get_results( $wpdb->prepare(
            "SELECT ID AS id, user_login, display_name, user_email
             FROM {$wpdb->users}
             WHERE user_login LIKE %s OR display_name LIKE %s OR user_email LIKE %s
             ORDER BY display_name LIMIT 30",
            $like, $like, $like
        ), ARRAY_A );
        return new WP_REST_Response( ['data' => $rows ?: []] );
    }

    public static function delete_org_assignment( WP_REST_Request $req ): WP_REST_Response {
        global $wpdb;
        $id = (int) $req->get_param( 'id' );
        $wpdb->delete( $wpdb->prefix . 'melk_org_assignments', ['id' => $id] );
        return new WP_REST_Response( ['success' => true] );
    }

    public static function remove_participant( WP_REST_Request $req ): WP_REST_Response {
        global $wpdb;
        $case_id = (int) $req->get_param( 'id' );
        $pid     = (int) $req->get_param( 'pid' );
        $uid     = acl_current_user_id();

        $case = $wpdb->get_row( $wpdb->prepare(
            "SELECT * FROM {$wpdb->prefix}melk_cases WHERE id = %d", $case_id
        ), ARRAY_A );
        if ( ! $case ) return new WP_REST_Response( ['message' => 'یافت نشد'], 404 );

        $deny = self::_deny_case_access( $uid, $case );
        if ( $deny ) return $deny;

        if ( ! acl_user_can( $uid, 'melk.cases.edit' ) && ! acl_user_can( $uid, 'melk.manage' ) ) {
            return new WP_REST_Response( ['message' => 'مجوز حذف عضو ندارید'], 403 );
        }

        $wpdb->delete( $wpdb->prefix . 'melk_case_participants', ['id' => $pid, 'case_id' => $case_id] );
        return new WP_REST_Response( ['success' => true] );
    }

    private static function _ensure_participant_meta_cols(): void {
        global $wpdb;
        static $done = false;
        if ( $done ) return;
        $done = true;
        $table = $wpdb->prefix . 'melk_case_participants';
        $cols  = $wpdb->get_col( "SHOW COLUMNS FROM {$table}" );
        if ( ! $cols ) return;
        if ( ! in_array( 'added_by', $cols, true ) ) {
            $wpdb->query( "ALTER TABLE {$table} ADD COLUMN added_by BIGINT UNSIGNED NULL AFTER role_type" );
        }
        if ( ! in_array( 'added_by_name', $cols, true ) ) {
            $wpdb->query( "ALTER TABLE {$table} ADD COLUMN added_by_name VARCHAR(120) NULL AFTER added_by" );
        }
    }

    private static function _participant_role_label( string $role_type ): string {
        $labels = [
            'agent'        => 'مشاور املاک',
            'legal'        => 'وکیل حقوقی',
            'municipality' => 'کارشناس شهرداری',
            'registry'     => 'کارشناس ثبت',
            'appraisal'    => 'کارشناس رسمی',
            'contractor'   => 'پیمانکار ساخت',
            'finance'      => 'کارشناس مالی',
            'other'        => 'سایر',
        ];
        return $labels[ $role_type ] ?? $role_type;
    }

    private static function _join_meta( array $participant, array $case ): array {
        if ( (int) ( $participant['user_id'] ?? 0 ) === (int) ( $case['created_by'] ?? 0 ) ) {
            return ['source' => 'creator', 'label' => 'ایجادکننده پرونده'];
        }
        if ( ! empty( $participant['added_by_name'] ) || ! empty( $participant['added_by_display_name'] ) ) {
            $login = $participant['added_by_login'] ?? '';
            $who   = $participant['added_by_display_name'] ?? $participant['added_by_name'] ?? '';
            if ( $login ) $who .= ' (@' . $login . ')';
            return ['source' => 'invited', 'label' => 'دعوت توسط ' . $who];
        }
        return ['source' => 'initial', 'label' => 'عضو اولیه پرونده'];
    }

    private static function _enrich_case_detail( array $case ): array {
        global $wpdb;
        $pre = $wpdb->prefix;
        $id  = (int) $case['id'];

        $creator = $wpdb->get_row( $wpdb->prepare(
            "SELECT user_login, display_name, user_email FROM {$wpdb->users} WHERE ID = %d",
            (int) ( $case['created_by'] ?? 0 )
        ), ARRAY_A );
        if ( $creator ) {
            $case['created_by_login']  = $creator['user_login'];
            $case['created_by_email']  = $creator['user_email'];
        }

        if ( (int) ( $case['assigned_to'] ?? 0 ) ) {
            $assignee = $wpdb->get_row( $wpdb->prepare(
                "SELECT user_login, display_name FROM {$wpdb->users} WHERE ID = %d",
                (int) $case['assigned_to']
            ), ARRAY_A );
            if ( $assignee ) {
                $case['assigned_login'] = $assignee['user_login'];
            }
        }

        $participants = $wpdb->get_results( $wpdb->prepare(
            "SELECT p.*, u.user_login, u.display_name AS wp_display_name, u.user_email,
                    ab.user_login AS added_by_login, ab.display_name AS added_by_display_name,
                    COALESCE(nc.note_count, 0) AS note_count
             FROM {$pre}melk_case_participants p
             LEFT JOIN {$wpdb->users} u ON u.ID = p.user_id
             LEFT JOIN {$wpdb->users} ab ON ab.ID = p.added_by
             LEFT JOIN (
                 SELECT user_id, COUNT(*) AS note_count
                 FROM {$pre}melk_case_notes WHERE case_id = %d GROUP BY user_id
             ) nc ON nc.user_id = p.user_id
             WHERE p.case_id = %d
             ORDER BY p.joined_at ASC, p.id ASC",
            $id, $id
        ), ARRAY_A ) ?: [];

        foreach ( $participants as &$p ) {
            $join = self::_join_meta( $p, $case );
            $p['role_label']   = self::_participant_role_label( (string) ( $p['role_type'] ?? 'other' ) );
            $p['join_source']  = $join['source'];
            $p['join_label']   = $join['label'];
            $p['display_name'] = $p['wp_display_name'] ?: ( $p['user_name'] ?? '' );
            if ( empty( $p['user_login'] ) ) $p['user_login'] = '';
        }
        unset( $p );

        $notes = $wpdb->get_results( $wpdb->prepare(
            "SELECT n.*, u.user_login, u.display_name AS wp_display_name
             FROM {$pre}melk_case_notes n
             LEFT JOIN {$wpdb->users} u ON u.ID = n.user_id
             WHERE n.case_id = %d
             ORDER BY n.created_at DESC",
            $id
        ), ARRAY_A ) ?: [];

        $note_type_labels = [
            'note' => 'یادداشت', 'action' => 'اقدام', 'legal' => 'حقوقی',
            'municipality' => 'شهرداری', 'registry' => 'ثبت',
        ];
        foreach ( $notes as &$n ) {
            $n['note_type_label'] = $note_type_labels[ $n['note_type'] ?? 'note' ] ?? ( $n['note_type'] ?? 'یادداشت' );
            $n['user_login']      = $n['user_login'] ?? '';
            $n['display_name']    = $n['wp_display_name'] ?: ( $n['user_name'] ?? '' );
        }
        unset( $n );

        $timeline = [];
        foreach ( $participants as $p ) {
            $timeline[] = [
                'kind'        => 'joined',
                'at'          => $p['joined_at'],
                'user_id'     => (int) $p['user_id'],
                'user_name'   => $p['display_name'] ?: $p['user_name'],
                'user_login'  => $p['user_login'],
                'role_type'   => $p['role_type'],
                'role_label'  => $p['role_label'],
                'join_source' => $p['join_source'],
                'join_label'  => $p['join_label'],
                'added_by_name' => $p['added_by_name'] ?? '',
            ];
        }
        foreach ( $notes as $n ) {
            $timeline[] = [
                'kind'             => 'note',
                'at'               => $n['created_at'],
                'id'               => (int) $n['id'],
                'user_id'          => (int) $n['user_id'],
                'user_name'        => $n['display_name'] ?: $n['user_name'],
                'user_login'       => $n['user_login'],
                'body'             => $n['body'],
                'note_type'        => $n['note_type'],
                'note_type_label'  => $n['note_type_label'],
            ];
        }
        usort( $timeline, static function ( $a, $b ) {
            return strcmp( (string) ( $b['at'] ?? '' ), (string) ( $a['at'] ?? '' ) );
        } );

        $case['participants']      = $participants;
        $case['notes']             = $notes;
        $case['activity_timeline'] = $timeline;
        $case['participant_count'] = count( $participants );
        $case['note_count']        = count( $notes );

        return $case;
    }

    private static function _deny_case_access( int $uid, array $case ): ?WP_REST_Response {
        if ( acl_user_is_super_admin( $uid ) ) return null;

        if ( acl_is_scope_unrestricted( $uid, 'district' ) && acl_is_scope_unrestricted( $uid, 'neighborhood' ) ) {
            return null;
        }

        global $wpdb;
        $is_participant = (int) $wpdb->get_var( $wpdb->prepare(
            "SELECT COUNT(*) FROM {$wpdb->prefix}melk_case_participants WHERE case_id = %d AND user_id = %d",
            (int) $case['id'], $uid
        ) );
        if ( $is_participant ) return null;

        if ( (int) $case['district_id'] ) {
            $deny_d = acl_deny_scope( $uid, 'district', (int) $case['district_id'], 'melk.view' );
            if ( ! $deny_d ) return null;
            if ( (int) $case['neighborhood_id'] ) {
                $deny_n = acl_deny_scope( $uid, 'neighborhood', (int) $case['neighborhood_id'], 'melk.view' );
                if ( ! $deny_n ) return null;
                return $deny_n;
            }
            return $deny_d;
        }

        return new WP_REST_Response( ['message' => 'دسترسی به این پرونده مجاز نیست'], 403 );
    }

    private static function _user_melk_role_label( int $uid, string $role_hint = '' ): string {
        // اگر نقش از فرانت‌اند آمده، مستقیم از آن استفاده کن
        $hint_labels = [
            'sysadmin'            => 'مدیر کل سیستم',
            'district_lead'       => 'سرگروه منطقه',
            'neighborhood_manager'=> 'مدیر محله',
            'melk_coordinator'    => 'هماهنگ‌کننده',
            'melk_specialist'     => 'کارشناس مشترک',
            'agent'               => 'مشاور املاک',
            'legal'               => 'وکیل حقوقی',
            'municipality'        => 'کارشناس شهرداری',
            'registry'            => 'کارشناس ثبت',
            'appraisal'           => 'کارشناس رسمی',
            'contractor'          => 'پیمانکار',
            'finance'             => 'کارشناس مالی',
        ];
        if ( $role_hint && isset( $hint_labels[ $role_hint ] ) ) {
            return $hint_labels[ $role_hint ];
        }

        // در غیر این صورت از جدول ACL بخوان — نقش تخصصی بر super_admin ترجیح دارد
        global $wpdb;
        $slug = $wpdb->get_var( $wpdb->prepare(
            "SELECT r.slug FROM {$wpdb->prefix}acl_user_roles ur
             INNER JOIN {$wpdb->prefix}acl_roles r ON r.id = ur.role_id
             WHERE ur.user_id = %d AND r.slug IN ('district_lead','neighborhood_manager','melk_specialist','melk_coordinator','super_admin')
             ORDER BY CASE r.slug
                WHEN 'district_lead'        THEN 1
                WHEN 'neighborhood_manager' THEN 2
                WHEN 'melk_coordinator'     THEN 3
                WHEN 'melk_specialist'      THEN 4
                WHEN 'super_admin'          THEN 99
                ELSE 100
             END LIMIT 1", $uid
        ) );
        $labels = [
            'district_lead'        => 'سرگروه منطقه',
            'neighborhood_manager' => 'مدیر محله',
            'melk_specialist'      => 'کارشناس مشترک',
            'melk_coordinator'     => 'هماهنگ‌کننده',
            'super_admin'          => 'مدیر کل سیستم',
        ];
        return $labels[ $slug ] ?? 'کاربر بنگاه';
    }

    private static function _append_district_list_scope( int $uid, string $district_col, array &$wheres, array &$args ): bool {
        if ( acl_user_is_super_admin( $uid ) ) return true;

        $district_allowed = acl_get_allowed_scope_ids( $uid, 'district' );
        $neigh_allowed    = acl_get_allowed_scope_ids( $uid, 'neighborhood' );

        if ( $district_allowed === null && $neigh_allowed === null ) return true;

        $conds = [];
        if ( $district_allowed !== null && $district_allowed ) {
            $ph     = implode( ',', array_fill( 0, count( $district_allowed ), '%d' ) );
            $conds[] = "{$district_col} IN ({$ph})";
            $args    = array_merge( $args, $district_allowed );
        }
        if ( $neigh_allowed !== null && $neigh_allowed ) {
            global $wpdb;
            $ph     = implode( ',', array_fill( 0, count( $neigh_allowed ), '%d' ) );
            $conds[] = "{$district_col} IN (SELECT district_id FROM {$wpdb->prefix}gis_neighborhoods WHERE id IN ({$ph}))";
            $args    = array_merge( $args, $neigh_allowed );
        }
        if ( ! $conds ) return false;

        $wheres[] = '(' . implode( ' OR ', $conds ) . ')';
        return true;
    }

    private static function _empty_overview(): array {
        return [
            'city' => 'شیراز', 'district_count' => 0, 'open_cases' => 0,
            'user_role' => '', 'scopes' => [], 'districts' => [],
        ];
    }
}
