<?php
/**
 * Melk_Extensions_Controller — ساخت‌وساز، آگهی ملک با کنترل دسترسی
 */
if ( ! defined( 'ABSPATH' ) ) exit;

class Melk_Extensions_Controller {

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

        // ساخت‌وساز
        register_rest_route( $ns, '/melk/construction', [
            [ 'methods' => 'GET',  'callback' => [ __CLASS__, 'get_construction_projects' ], 'permission_callback' => $p ],
            [ 'methods' => 'POST', 'callback' => [ __CLASS__, 'create_construction_project' ], 'permission_callback' => $p ],
        ] );
        register_rest_route( $ns, '/melk/construction/(?P<id>\d+)', [
            [ 'methods' => 'GET', 'callback' => [ __CLASS__, 'get_construction_project' ], 'permission_callback' => $p ],
            [ 'methods' => 'PUT', 'callback' => [ __CLASS__, 'update_construction_project' ], 'permission_callback' => $p ],
        ] );
        register_rest_route( $ns, '/melk/construction/(?P<id>\d+)/participants', [
            [ 'methods' => 'GET',  'callback' => [ __CLASS__, 'get_construction_participants' ], 'permission_callback' => $p ],
            [ 'methods' => 'POST', 'callback' => [ __CLASS__, 'add_construction_participant' ], 'permission_callback' => $p ],
        ] );
        register_rest_route( $ns, '/melk/construction/(?P<id>\d+)/participants/(?P<pid>\d+)', [
            'methods' => 'DELETE', 'callback' => [ __CLASS__, 'remove_construction_participant' ], 'permission_callback' => $p,
        ] );

        // حسابداری ABC / گراف هزینه مشارکت
        register_rest_route( $ns, '/melk/acct/activities', [
            'methods' => 'GET', 'callback' => [ __CLASS__, 'acct_activities' ], 'permission_callback' => $p,
        ] );
        register_rest_route( $ns, '/melk/acct/cost-graph', [
            'methods' => 'GET', 'callback' => [ __CLASS__, 'acct_cost_graph' ], 'permission_callback' => $p,
        ] );
        register_rest_route( $ns, '/melk/acct/activity-cost/(?P<project_id>\d+)', [
            'methods' => 'GET', 'callback' => [ __CLASS__, 'acct_activity_cost' ], 'permission_callback' => $p,
        ] );
        register_rest_route( $ns, '/melk/acct/allocate/(?P<project_id>\d+)', [
            'methods' => 'POST', 'callback' => [ __CLASS__, 'acct_allocate' ], 'permission_callback' => $p,
        ] );
        register_rest_route( $ns, '/melk/acct/crew/(?P<project_id>\d+)', [
            [ 'methods' => 'GET', 'callback' => [ __CLASS__, 'acct_get_crew' ], 'permission_callback' => $p ],
            [ 'methods' => 'PUT', 'callback' => [ __CLASS__, 'acct_save_crew' ], 'permission_callback' => $p ],
        ] );

        // آگهی ملک
        register_rest_route( $ns, '/melk/listings', [
            [ 'methods' => 'GET',  'callback' => [ __CLASS__, 'get_listings' ], 'permission_callback' => $p ],
            [ 'methods' => 'POST', 'callback' => [ __CLASS__, 'create_listing' ], 'permission_callback' => $p ],
        ] );
        register_rest_route( $ns, '/melk/listings/(?P<id>\d+)', [
            [ 'methods' => 'GET', 'callback' => [ __CLASS__, 'get_listing' ], 'permission_callback' => $p ],
            [ 'methods' => 'PUT', 'callback' => [ __CLASS__, 'update_listing' ], 'permission_callback' => $p ],
        ] );
        register_rest_route( $ns, '/melk/listing-visibility-options', [
            'methods' => 'GET', 'callback' => [ __CLASS__, 'get_visibility_options' ], 'permission_callback' => $p,
        ] );
        register_rest_route( $ns, '/melk/listing-form-bank', [
            'methods' => 'GET', 'callback' => [ __CLASS__, 'get_listing_form_bank' ], 'permission_callback' => $p,
        ] );
        register_rest_route( $ns, '/melk/listing-form-config', [
            'methods' => 'GET', 'callback' => [ __CLASS__, 'get_listing_form_config' ], 'permission_callback' => $p,
        ] );

        register_rest_route( $ns, '/melk/specialist-form-bank', [
            'methods' => 'GET', 'callback' => [ __CLASS__, 'get_specialist_form_bank' ], 'permission_callback' => $p,
        ] );
        register_rest_route( $ns, '/melk/specialist-form-config', [
            'methods' => 'GET', 'callback' => [ __CLASS__, 'get_specialist_form_config' ], 'permission_callback' => $p,
        ] );

        register_rest_route( $ns, '/melk/county-form-bank', [
            'methods' => 'GET', 'callback' => [ __CLASS__, 'get_county_form_bank' ], 'permission_callback' => $p,
        ] );
        register_rest_route( $ns, '/melk/county-form-config', [
            'methods' => 'GET', 'callback' => [ __CLASS__, 'get_county_form_config' ], 'permission_callback' => $p,
        ] );

        // بنگاه‌های شهرستان
        register_rest_route( $ns, '/melk/county-requests', [
            [ 'methods' => 'GET',  'callback' => [ __CLASS__, 'get_county_requests' ], 'permission_callback' => $p ],
            [ 'methods' => 'POST', 'callback' => [ __CLASS__, 'create_county_request' ], 'permission_callback' => $p ],
        ] );
        register_rest_route( $ns, '/melk/county-requests/(?P<id>\d+)', [
            [ 'methods' => 'GET', 'callback' => [ __CLASS__, 'get_county_request' ], 'permission_callback' => $p ],
            [ 'methods' => 'PUT', 'callback' => [ __CLASS__, 'update_county_request' ], 'permission_callback' => $p ],
        ] );
        register_rest_route( $ns, '/melk/county-requests/(?P<id>\d+)/status', [
            'methods' => 'PUT', 'callback' => [ __CLASS__, 'update_county_request_status' ], 'permission_callback' => $p,
        ] );

        // متخصصان ساختمان
        register_rest_route( $ns, '/melk/building-specialists', [
            [ 'methods' => 'GET',  'callback' => [ __CLASS__, 'get_building_specialists' ], 'permission_callback' => $p ],
            [ 'methods' => 'POST', 'callback' => [ __CLASS__, 'register_building_specialist' ], 'permission_callback' => $p ],
        ] );
        register_rest_route( $ns, '/melk/building-specialists/(?P<id>\d+)', [
            [ 'methods' => 'GET', 'callback' => [ __CLASS__, 'get_building_specialist' ], 'permission_callback' => $p ],
            [ 'methods' => 'PUT', 'callback' => [ __CLASS__, 'update_building_specialist' ], 'permission_callback' => $p ],
        ] );
        register_rest_route( $ns, '/melk/building-specialists/(?P<id>\d+)/approve', [
            'methods' => 'PUT', 'callback' => [ __CLASS__, 'approve_building_specialist' ], 'permission_callback' => $p,
        ] );
        register_rest_route( $ns, '/melk/building-specialists/(?P<id>\d+)/reject', [
            'methods' => 'PUT', 'callback' => [ __CLASS__, 'reject_building_specialist' ], 'permission_callback' => $p,
        ] );

        // مناقصه متخصصان
        register_rest_route( $ns, '/melk/specialist-tenders', [
            [ 'methods' => 'GET',  'callback' => [ __CLASS__, 'get_specialist_tenders' ], 'permission_callback' => $p ],
            [ 'methods' => 'POST', 'callback' => [ __CLASS__, 'create_specialist_tender' ], 'permission_callback' => $p ],
        ] );
        register_rest_route( $ns, '/melk/specialist-tenders/(?P<id>\d+)/bids', [
            [ 'methods' => 'GET',  'callback' => [ __CLASS__, 'get_tender_bids' ], 'permission_callback' => $p ],
            [ 'methods' => 'POST', 'callback' => [ __CLASS__, 'submit_specialist_bid' ], 'permission_callback' => $p ],
        ] );
        register_rest_route( $ns, '/melk/specialist-bids', [
            'methods' => 'GET', 'callback' => [ __CLASS__, 'get_my_specialist_bids' ], 'permission_callback' => $p,
        ] );

        // سرمایه‌گذار ملک
        $pi = [ __CLASS__, 'perm_investor' ];
        register_rest_route( $ns, '/melk/investor/meta', [
            'methods' => 'GET', 'callback' => [ __CLASS__, 'get_investor_meta' ], 'permission_callback' => $pi,
        ] );
        register_rest_route( $ns, '/melk/investor/profile', [
            [ 'methods' => 'GET', 'callback' => [ __CLASS__, 'get_investor_profile' ], 'permission_callback' => $pi ],
            [ 'methods' => 'PUT', 'callback' => [ __CLASS__, 'save_investor_profile' ], 'permission_callback' => $pi ],
        ] );
        register_rest_route( $ns, '/melk/investor/criteria', [
            [ 'methods' => 'GET', 'callback' => [ __CLASS__, 'get_investment_criteria' ], 'permission_callback' => $pi ],
            [ 'methods' => 'POST', 'callback' => [ __CLASS__, 'create_investment_criteria' ], 'permission_callback' => $pi ],
        ] );
        register_rest_route( $ns, '/melk/investor/criteria/(?P<id>\d+)', [
            [ 'methods' => 'PUT', 'callback' => [ __CLASS__, 'update_investment_criteria' ], 'permission_callback' => $pi ],
            [ 'methods' => 'DELETE', 'callback' => [ __CLASS__, 'delete_investment_criteria' ], 'permission_callback' => $pi ],
        ] );
        register_rest_route( $ns, '/melk/investor/matches', [
            'methods' => 'GET', 'callback' => [ __CLASS__, 'get_investor_matches' ], 'permission_callback' => $pi,
        ] );
        register_rest_route( $ns, '/melk/investor/matches/(?P<id>\d+)/seen', [
            'methods' => 'POST', 'callback' => [ __CLASS__, 'mark_investor_match_seen' ], 'permission_callback' => $pi,
        ] );
        register_rest_route( $ns, '/melk/investor/experts', [
            'methods' => 'GET', 'callback' => [ __CLASS__, 'get_investor_experts' ], 'permission_callback' => $pi,
        ] );
        register_rest_route( $ns, '/melk/investor/expert-requests', [
            [ 'methods' => 'GET', 'callback' => [ __CLASS__, 'get_investor_expert_requests' ], 'permission_callback' => $pi ],
            [ 'methods' => 'POST', 'callback' => [ __CLASS__, 'create_investor_expert_request' ], 'permission_callback' => $pi ],
        ] );
        register_rest_route( $ns, '/melk/investor/expert-requests/(?P<id>\d+)/respond', [
            'methods' => 'POST', 'callback' => [ __CLASS__, 'respond_investor_expert_request' ], 'permission_callback' => $p,
        ] );

        // مالیات و حقوق — مشاور AI
        $pa = [ __CLASS__, 'perm_advisory' ];
        register_rest_route( $ns, '/melk/advisory/meta', [
            'methods' => 'GET', 'callback' => [ __CLASS__, 'get_advisory_meta' ], 'permission_callback' => $pa,
        ] );
        register_rest_route( $ns, '/melk/advisory/tax', [
            'methods' => 'GET', 'callback' => [ __CLASS__, 'get_advisory_tax_list' ], 'permission_callback' => $pa,
        ] );
        register_rest_route( $ns, '/melk/advisory/tax/(?P<key>[a-z_]+)', [
            'methods' => 'GET', 'callback' => [ __CLASS__, 'get_advisory_tax_detail' ], 'permission_callback' => $pa,
        ] );
        register_rest_route( $ns, '/melk/advisory/legal', [
            'methods' => 'GET', 'callback' => [ __CLASS__, 'get_advisory_legal_list' ], 'permission_callback' => $pa,
        ] );
        register_rest_route( $ns, '/melk/advisory/legal/(?P<key>[a-z_]+)', [
            'methods' => 'GET', 'callback' => [ __CLASS__, 'get_advisory_legal_detail' ], 'permission_callback' => $pa,
        ] );
        register_rest_route( $ns, '/melk/advisory/search', [
            'methods' => 'GET', 'callback' => [ __CLASS__, 'search_advisory' ], 'permission_callback' => $pa,
        ] );
        register_rest_route( $ns, '/melk/advisory/chat', [
            'methods' => 'POST', 'callback' => [ __CLASS__, 'advisory_chat' ], 'permission_callback' => $pa,
        ] );
        register_rest_route( $ns, '/melk/advisory/sessions', [
            'methods' => 'GET', 'callback' => [ __CLASS__, 'get_advisory_sessions' ], 'permission_callback' => $pa,
        ] );
        register_rest_route( $ns, '/melk/advisory/sessions/(?P<id>\d+)/messages', [
            'methods' => 'GET', 'callback' => [ __CLASS__, 'get_advisory_session_messages' ], 'permission_callback' => $pa,
        ] );
        register_rest_route( $ns, '/melk/advisory/escalate', [
            'methods' => 'POST', 'callback' => [ __CLASS__, 'escalate_advisory' ], 'permission_callback' => $pa,
        ] );
    }

    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_investor(): bool {
        if ( ! is_user_logged_in() ) return false;
        require_once ACC_PATH . 'includes/helpers/melk-investor.php';
        return Melk_Investor::can_investor( acl_current_user_id() );
    }

    public static function perm_advisory(): bool {
        if ( ! is_user_logged_in() ) return false;
        require_once ACC_PATH . 'includes/helpers/melk-advisory.php';
        return Melk_Advisory::can_use( acl_current_user_id() );
    }

    /* ── ساخت‌وساز ───────────────────────────────────── */

    public static function get_construction_projects( WP_REST_Request $req ): WP_REST_Response {
        global $wpdb;
        $uid  = acl_current_user_id();
        $pre  = $wpdb->prefix;
        $w    = [ '1=1' ];
        $args = [];

        if ( $req->get_param( 'status' ) ) {
            $w[]    = 'p.status = %s';
            $args[] = sanitize_text_field( $req->get_param( 'status' ) );
        }
        if ( ! self::_can_manage_construction( $uid ) ) {
            $w[]    = '(p.created_by = %d OR EXISTS (
                SELECT 1 FROM ' . $pre . 'melk_construction_participants cp
                WHERE cp.project_id = p.id AND cp.user_id = %d
            ))';
            $args[] = $uid;
            $args[] = $uid;
        }
        self::_append_geo_where( $uid, 'p.district_id', 'p.neighborhood_id', $w, $args );

        $sql = "SELECT p.*, (SELECT COUNT(*) FROM {$pre}melk_construction_participants cp WHERE cp.project_id = p.id) AS participant_count
                FROM {$pre}melk_construction_projects p
                WHERE " . implode( ' AND ', $w ) . ' ORDER BY p.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' => array_map( [ __CLASS__, '_fmt_project' ], $rows ?: [] ) ] );
    }

    public static function create_construction_project( WP_REST_Request $req ): WP_REST_Response {
        global $wpdb;
        $uid = acl_current_user_id();
        if ( ! self::_can_manage_construction( $uid ) && ! acl_user_can( $uid, 'melk.cases.participate' ) ) {
            return new WP_REST_Response( [ 'message' => 'دسترسی ایجاد پروژه ندارید' ], 403 );
        }
        $title = sanitize_text_field( $req->get_param( 'title' ) ?? '' );
        if ( ! $title ) return new WP_REST_Response( [ 'message' => 'عنوان الزامی است' ], 400 );

        $key = 'CP-' . strtoupper( substr( md5( uniqid( (string) $uid, true ) ), 0, 8 ) );
        $user = self::_user_name( $uid );
        $wpdb->insert( "{$wpdb->prefix}melk_construction_projects", [
            'project_key'      => $key,
            'title'            => $title,
            'project_type'     => sanitize_text_field( $req->get_param( 'project_type' ) ?? 'construction' ),
            'status'           => 'draft',
            'parcel_id'        => (int) ( $req->get_param( 'parcel_id' ) ?? 0 ) ?: null,
            'case_id'          => (int) ( $req->get_param( 'case_id' ) ?? 0 ) ?: null,
            'district_id'      => (int) ( $req->get_param( 'district_id' ) ?? 0 ) ?: null,
            'neighborhood_id'  => (int) ( $req->get_param( 'neighborhood_id' ) ?? 0 ) ?: null,
            'renovation_code'  => sanitize_text_field( $req->get_param( 'renovation_code' ) ?? '' ) ?: null,
            'budget'           => $req->get_param( 'budget' ) !== null ? (float) $req->get_param( 'budget' ) : null,
            'area_sqm'         => $req->get_param( 'area_sqm' ) !== null ? (float) $req->get_param( 'area_sqm' ) : null,
            'floors'           => $req->get_param( 'floors' ) !== null ? (int) $req->get_param( 'floors' ) : null,
            'description'      => sanitize_textarea_field( $req->get_param( 'description' ) ?? '' ),
            'created_by'       => $uid,
            'created_by_name'  => $user,
        ] );
        $id = (int) $wpdb->insert_id;
        $wpdb->insert( "{$wpdb->prefix}melk_construction_participants", [
            'project_id' => $id, 'user_id' => $uid, 'role_type' => 'owner', 'added_by' => $uid,
        ] );
        return new WP_REST_Response( [ 'data' => self::_fmt_project( self::_get_project_row( $id ) ) ], 201 );
    }

    public static function get_construction_project( WP_REST_Request $req ): WP_REST_Response {
        $id  = (int) $req['id'];
        $row = self::_get_project_row( $id );
        if ( ! $row ) return new WP_REST_Response( [ 'message' => 'پروژه یافت نشد' ], 404 );
        $deny = self::_deny_project_access( acl_current_user_id(), $row );
        if ( $deny ) return $deny;
        $p = self::_fmt_project( $row );
        $p['participants'] = self::_get_project_participants( $id );
        return new WP_REST_Response( [ 'data' => $p ] );
    }

    public static function update_construction_project( WP_REST_Request $req ): WP_REST_Response {
        $id  = (int) $req['id'];
        $row = self::_get_project_row( $id );
        if ( ! $row ) return new WP_REST_Response( [ 'message' => 'پروژه یافت نشد' ], 404 );
        $uid = acl_current_user_id();
        $deny = self::_deny_project_access( $uid, $row, true );
        if ( $deny ) return $deny;

        $upd = [];
        foreach ( [ 'title', 'project_type', 'status', 'renovation_code', 'description' ] as $f ) {
            if ( $req->get_param( $f ) !== null ) $upd[ $f ] = sanitize_text_field( $req->get_param( $f ) );
        }
        foreach ( [ 'budget', 'area_sqm' ] as $f ) {
            if ( $req->get_param( $f ) !== null ) $upd[ $f ] = (float) $req->get_param( $f );
        }
        if ( $upd ) {
            global $wpdb;
            $wpdb->update( "{$wpdb->prefix}melk_construction_projects", $upd, [ 'id' => $id ] );
        }
        return new WP_REST_Response( [ 'data' => self::_fmt_project( self::_get_project_row( $id ) ) ] );
    }

    public static function get_construction_participants( WP_REST_Request $req ): WP_REST_Response {
        $id  = (int) $req['id'];
        $row = self::_get_project_row( $id );
        if ( ! $row ) return new WP_REST_Response( [ 'message' => 'پروژه یافت نشد' ], 404 );
        $deny = self::_deny_project_access( acl_current_user_id(), $row );
        if ( $deny ) return $deny;
        return new WP_REST_Response( [ 'data' => self::_get_project_participants( $id ) ] );
    }

    public static function add_construction_participant( WP_REST_Request $req ): WP_REST_Response {
        $id  = (int) $req['id'];
        $row = self::_get_project_row( $id );
        if ( ! $row ) return new WP_REST_Response( [ 'message' => 'پروژه یافت نشد' ], 404 );
        $uid = acl_current_user_id();
        $deny = self::_deny_project_access( $uid, $row, true );
        if ( $deny ) return $deny;

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

        global $wpdb;
        $wpdb->replace( "{$wpdb->prefix}melk_construction_participants", [
            'project_id'    => $id,
            'user_id'       => $user_id,
            'role_type'     => sanitize_text_field( $req->get_param( 'role_type' ) ?? 'contractor' ),
            'share_percent' => $req->get_param( 'share_percent' ) !== null ? (float) $req->get_param( 'share_percent' ) : null,
            'notes'         => sanitize_text_field( $req->get_param( 'notes' ) ?? '' ),
            'added_by'      => $uid,
        ] );
        return new WP_REST_Response( [ 'data' => self::_get_project_participants( $id ) ] );
    }

    public static function remove_construction_participant( WP_REST_Request $req ): WP_REST_Response {
        $id  = (int) $req['id'];
        $pid = (int) $req['pid'];
        $row = self::_get_project_row( $id );
        if ( ! $row ) return new WP_REST_Response( [ 'message' => 'پروژه یافت نشد' ], 404 );
        $deny = self::_deny_project_access( acl_current_user_id(), $row, true );
        if ( $deny ) return $deny;
        global $wpdb;
        $wpdb->delete( "{$wpdb->prefix}melk_construction_participants", [ 'id' => $pid, 'project_id' => $id ] );
        return new WP_REST_Response( [ 'success' => true ] );
    }

    /* ── حسابداری ABC مشارکت ─────────────────────────── */

    private static function _abc(): void {
        require_once ACC_PATH . 'includes/helpers/melk-activity-cost.php';
        Melk_Activity_Cost::ensure_tables();
    }

    public static function acct_activities( WP_REST_Request $req ): WP_REST_Response {
        self::_abc();
        return new WP_REST_Response( [
            'success' => true,
            'data'    => Melk_Activity_Cost::list_activities( true ),
            'roles'   => Melk_Activity_Cost::role_labels(),
        ] );
    }

    public static function acct_cost_graph( WP_REST_Request $req ): WP_REST_Response {
        self::_abc();
        $data = Melk_Activity_Cost::cost_graph( [
            'project_id' => (int) ( $req->get_param( 'project_id' ) ?: 0 ),
        ] );
        return new WP_REST_Response( [ 'success' => true, 'data' => $data ] );
    }

    public static function acct_activity_cost( WP_REST_Request $req ): WP_REST_Response {
        self::_abc();
        $id = (int) $req['project_id'];
        $row = Melk_Activity_Cost::get_project( $id );
        if ( ! $row ) {
            return new WP_REST_Response( [ 'success' => false, 'message' => 'پروژه یافت نشد' ], 404 );
        }
        $deny = self::_deny_project_access( acl_current_user_id(), $row );
        if ( $deny ) return $deny;
        return new WP_REST_Response( [
            'success' => true,
            'data'    => Melk_Activity_Cost::allocate( $row ),
        ] );
    }

    public static function acct_allocate( WP_REST_Request $req ): WP_REST_Response {
        self::_abc();
        $id = (int) $req['project_id'];
        $row = Melk_Activity_Cost::get_project( $id );
        if ( ! $row ) {
            return new WP_REST_Response( [ 'success' => false, 'message' => 'پروژه یافت نشد' ], 404 );
        }
        $deny = self::_deny_project_access( acl_current_user_id(), $row, true );
        if ( $deny ) return $deny;
        $alloc = Melk_Activity_Cost::allocate( $row );
        Melk_Activity_Cost::persist( $id, $alloc );
        return new WP_REST_Response( [
            'success' => true,
            'message' => 'تخصیص فعالیت پروژه ذخیره شد',
            'data'    => $alloc,
        ] );
    }

    public static function acct_get_crew( WP_REST_Request $req ): WP_REST_Response {
        self::_abc();
        $id = (int) $req['project_id'];
        $row = Melk_Activity_Cost::get_project( $id );
        if ( ! $row ) {
            return new WP_REST_Response( [ 'success' => false, 'message' => 'پروژه یافت نشد' ], 404 );
        }
        $deny = self::_deny_project_access( acl_current_user_id(), $row );
        if ( $deny ) return $deny;
        return new WP_REST_Response( [
            'success' => true,
            'data'    => Melk_Activity_Cost::get_crew( $id ),
        ] );
    }

    public static function acct_save_crew( WP_REST_Request $req ): WP_REST_Response {
        self::_abc();
        $id = (int) $req['project_id'];
        $row = Melk_Activity_Cost::get_project( $id );
        if ( ! $row ) {
            return new WP_REST_Response( [ 'success' => false, 'message' => 'پروژه یافت نشد' ], 404 );
        }
        $deny = self::_deny_project_access( acl_current_user_id(), $row, true );
        if ( $deny ) return $deny;
        $members = $req->get_param( 'members' );
        if ( ! is_array( $members ) ) $members = [];
        return new WP_REST_Response( Melk_Activity_Cost::save_crew( $id, $members ) );
    }

    /* ── آگهی ملک ───────────────────────────────────── */

    public static function get_visibility_options(): WP_REST_Response {
        return new WP_REST_Response( [ 'data' => [
            'deal_types' => [
                [ 'key' => 'sale',             'label' => 'فروش',              'group' => 'RESO Standard' ],
                [ 'key' => 'rent',             'label' => 'اجاره مسکونی',      'group' => 'RESO Standard' ],
                [ 'key' => 'rent_commercial',  'label' => 'اجاره تجاری',       'group' => 'RESO Standard' ],
                [ 'key' => 'partnership',      'label' => 'مشارکت در ساخت',    'group' => 'Joint Venture' ],
                [ 'key' => 'land_sale',        'label' => 'فروش زمین',         'group' => 'Land' ],
                [ 'key' => 'pre_sale',         'label' => 'پیش‌فروش / off-plan', 'group' => 'Pre-construction' ],
                [ 'key' => 'mortgage',         'label' => 'رهن و اجاره',       'group' => 'Iranian' ],
                [ 'key' => 'exchange',         'label' => 'معاوضه ملک',        'group' => 'Exchange' ],
            ],
            'property_types' => [
                [ 'key' => 'residential_apartment', 'label' => 'آپارتمان مسکونی' ],
                [ 'key' => 'residential_villa',     'label' => 'ویلا / ساختمان' ],
                [ 'key' => 'residential_land',      'label' => 'زمین مسکونی' ],
                [ 'key' => 'commercial_retail',     'label' => 'مغازه / تجاری' ],
                [ 'key' => 'commercial_office',     'label' => 'دفتر اداری' ],
                [ 'key' => 'commercial_industrial', 'label' => 'صنعتی / انبار' ],
                [ 'key' => 'agricultural',          'label' => 'کشاورزی / باغ' ],
            ],
            'rent_periods' => [
                [ 'key' => 'monthly',  'label' => 'ماهانه' ],
                [ 'key' => 'annual',   'label' => 'سالانه' ],
                [ 'key' => 'daily',    'label' => 'روزانه' ],
            ],
            'modes' => [
                [ 'key' => 'network',  'label' => 'شبکه بنگاه‌ها', 'desc' => 'همه اعضای دارای دسترسی ملک' ],
                [ 'key' => 'geo',      'label' => 'منطقه جغرافیایی', 'desc' => 'منطقه/محله مشخص' ],
                [ 'key' => 'users',    'label' => 'کاربران مشخص', 'desc' => 'یوزرنیم یا انتخاب از لیست' ],
                [ 'key' => 'role',     'label' => 'نقش سازمانی', 'desc' => 'سرگروه، مدیر محله، مشاور و ...' ],
                [ 'key' => 'private',  'label' => 'خصوصی', 'desc' => 'فقط خودم + افراد انتخابی' ],
                [ 'key' => 'public',   'label' => 'عمومی', 'desc' => 'قابل مشاهده برای همه' ],
            ],
            'roles' => [
                [ 'key' => 'agent',                'label' => 'مشاور املاک' ],
                [ 'key' => 'district_lead',        'label' => 'سرگروه منطقه' ],
                [ 'key' => 'neighborhood_manager', 'label' => 'مدیر محله' ],
                [ 'key' => 'melk_coordinator',     'label' => 'هماهنگ‌کننده' ],
                [ 'key' => 'melk_specialist',      'label' => 'کارشناس مشترک' ],
                [ 'key' => 'legal',                'label' => 'وکیل حقوقی' ],
                [ 'key' => 'contractor',           'label' => 'پیمانکار' ],
                [ 'key' => 'finance',              'label' => 'کارشناس مالی' ],
                [ 'key' => 'municipality',         'label' => 'کارشناس شهرداری' ],
                [ 'key' => 'registry',             'label' => 'کارشناس ثبت' ],
                [ 'key' => 'appraisal',            'label' => 'کارشناس رسمی' ],
            ],
        ] ] );
    }

    public static function get_listing_form_bank(): WP_REST_Response {
        require_once ACC_PATH . 'includes/helpers/melk-listing-forms.php';
        return new WP_REST_Response( [ 'data' => Melk_Listing_Forms::bank_from_db() ] );
    }

    public static function get_listing_form_config( WP_REST_Request $req ): WP_REST_Response {
        require_once ACC_PATH . 'includes/helpers/melk-listing-forms.php';
        $key = sanitize_text_field( $req->get_param( 'form_key' ) ?? '' );
        if ( ! $key ) {
            return new WP_REST_Response( [ 'message' => 'form_key الزامی است' ], 400 );
        }
        $cfg = Melk_Listing_Forms::config_from_db( $key );
        if ( ! $cfg ) {
            return new WP_REST_Response( [ 'message' => 'فرم یافت نشد' ], 404 );
        }
        return new WP_REST_Response( [ 'data' => $cfg ] );
    }

    public static function get_listings( WP_REST_Request $req ): WP_REST_Response {
        global $wpdb;
        $uid  = acl_current_user_id();
        $pre  = $wpdb->prefix;
        $w    = [ '1=1' ];
        $args = [];

        if ( $req->get_param( 'mine' ) ) {
            $w[]    = 'l.created_by = %d';
            $args[] = $uid;
        } else {
            $w[] = "l.status = 'active'";
        }
        if ( $req->get_param( 'deal_type' ) ) {
            $w[]    = 'l.deal_type = %s';
            $args[] = sanitize_text_field( $req->get_param( 'deal_type' ) );
        }
        if ( $req->get_param( 'property_type' ) ) {
            $w[]    = 'l.property_type = %s';
            $args[] = sanitize_text_field( $req->get_param( 'property_type' ) );
        }
        if ( ! $req->get_param( 'mine' ) ) {
            self::_append_geo_where( $uid, 'l.district_id', 'l.neighborhood_id', $w, $args );
        }

        $sql = "SELECT l.* FROM {$pre}melk_listings l WHERE " . implode( ' AND ', $w ) . ' ORDER BY l.updated_at DESC LIMIT 500';
        $rows = $args ? $wpdb->get_results( $wpdb->prepare( $sql, ...$args ), ARRAY_A ) : $wpdb->get_results( $sql, ARRAY_A );
        $out  = [];
        foreach ( $rows ?: [] as $row ) {
            if ( $req->get_param( 'mine' ) || self::_user_can_see_listing( $uid, $row ) ) {
                $out[] = self::_fmt_listing( $row );
            }
        }
        return new WP_REST_Response( [ 'data' => array_slice( $out, 0, 200 ) ] );
    }

    public static function create_listing( WP_REST_Request $req ): WP_REST_Response {
        $uid = acl_current_user_id();
        if ( ! self::_can_publish_listing( $uid ) ) {
            return new WP_REST_Response( [ 'message' => 'دسترسی انتشار آگهی ندارید' ], 403 );
        }

        require_once ACC_PATH . 'includes/helpers/melk-listing-forms.php';
        $form_key   = sanitize_text_field( $req->get_param( 'form_template_key' ) ?? $req->get_param( 'form_key' ) ?? '' );
        $form_values = $req->get_param( 'form_data' );
        if ( ! is_array( $form_values ) ) {
            $form_values = [];
        }
        // فیلدهای سطح بالا را هم در form_values ادغام کن
        foreach ( [ 'title', 'description', 'price', 'rent_period', 'area_sqm', 'rooms',
                  'district_id', 'neighborhood_id', 'renovation_code', 'deal_type', 'property_type' ] as $fk ) {
            if ( $req->get_param( $fk ) !== null && ! isset( $form_values[ $fk ] ) ) {
                $form_values[ $fk ] = $req->get_param( $fk );
            }
        }

        $mapped = $form_key
            ? Melk_Listing_Forms::map_submission( $form_key, $form_values )
            : [ 'columns' => [], 'form_data' => $form_values, 'errors' => [] ];

        if ( ! empty( $mapped['errors'] ) ) {
            return new WP_REST_Response( [ 'message' => implode( ' · ', $mapped['errors'] ) ], 400 );
        }

        $cols = $mapped['columns'];
        $title = sanitize_text_field( $cols['title'] ?? $req->get_param( 'title' ) ?? '' );
        if ( ! $title ) return new WP_REST_Response( [ 'message' => 'عنوان الزامی است' ], 400 );

        global $wpdb;
        $key = 'ML-' . strtoupper( substr( md5( uniqid( (string) $uid, true ) ), 0, 8 ) );
        $vis_roles = $req->get_param( 'visibility_roles' );
        $vis_users = $req->get_param( 'visibility_user_ids' );
        $vis_names = $req->get_param( 'visibility_usernames' );
        $vis_dists = $req->get_param( 'visibility_district_ids' );
        $vis_neighs = $req->get_param( 'visibility_neighborhood_ids' );

        $user_ids = is_array( $vis_users ) ? array_map( 'intval', $vis_users ) : [];
        $usernames = self::_normalize_usernames( $vis_names );
        $user_ids  = array_values( array_unique( array_merge( $user_ids, self::_resolve_usernames_to_ids( $usernames ) ) ) );

        $district_id = (int) ( $cols['district_id'] ?? $req->get_param( 'district_id' ) ?? 0 ) ?: null;
        $neighborhood_id = (int) ( $cols['neighborhood_id'] ?? $req->get_param( 'neighborhood_id' ) ?? 0 ) ?: null;
        if ( ! $district_id ) {
            $scopes = acl_get_allowed_scope_ids( $uid, 'district' );
            if ( is_array( $scopes ) && count( $scopes ) === 1 ) {
                $district_id = (int) $scopes[0];
            }
        }

        $extra_data = array_merge( $mapped['form_data'], is_array( $req->get_param( 'form_data' ) ) ? $req->get_param( 'form_data' ) : [] );
        // حذف کلیدهایی که به ستون نگاشت شده‌اند
        foreach ( array_keys( $cols ) as $ck ) {
            unset( $extra_data[ $ck ] );
        }

        $wpdb->insert( "{$wpdb->prefix}melk_listings", [
            'listing_key'                 => $key,
            'deal_type'                   => sanitize_text_field( $cols['deal_type'] ?? $req->get_param( 'deal_type' ) ?? 'sale' ),
            'property_type'               => sanitize_text_field( $cols['property_type'] ?? $req->get_param( 'property_type' ) ?? 'residential_apartment' ),
            'form_template_key'           => $form_key ?: null,
            'form_data'                   => $extra_data ? wp_json_encode( $extra_data, JSON_UNESCAPED_UNICODE ) : null,
            'title'                       => $title,
            'description'                 => sanitize_textarea_field( $cols['description'] ?? $req->get_param( 'description' ) ?? '' ),
            'price'                       => isset( $cols['price'] ) && $cols['price'] !== '' ? (float) $cols['price'] : ( $req->get_param( 'price' ) !== null ? (float) $req->get_param( 'price' ) : null ),
            'rent_period'                 => sanitize_text_field( $cols['rent_period'] ?? $req->get_param( 'rent_period' ) ?? '' ) ?: null,
            'area_sqm'                    => isset( $cols['area_sqm'] ) && $cols['area_sqm'] !== '' ? (float) $cols['area_sqm'] : ( $req->get_param( 'area_sqm' ) !== null ? (float) $req->get_param( 'area_sqm' ) : null ),
            'rooms'                       => isset( $cols['rooms'] ) && $cols['rooms'] !== '' ? (int) $cols['rooms'] : ( $req->get_param( 'rooms' ) !== null ? (int) $req->get_param( 'rooms' ) : null ),
            'parcel_id'                   => (int) ( $req->get_param( 'parcel_id' ) ?? 0 ) ?: null,
            'district_id'                 => $district_id,
            'neighborhood_id'             => $neighborhood_id,
            'renovation_code'             => sanitize_text_field( $cols['renovation_code'] ?? $req->get_param( 'renovation_code' ) ?? '' ) ?: null,
            'visibility_mode'             => sanitize_text_field( $req->get_param( 'visibility_mode' ) ?? 'network' ),
            'visibility_roles'            => is_array( $vis_roles ) ? wp_json_encode( array_values( $vis_roles ) ) : null,
            'visibility_user_ids'         => $user_ids ? wp_json_encode( $user_ids ) : null,
            'visibility_usernames'        => $usernames ? wp_json_encode( $usernames ) : null,
            'visibility_district_ids'     => is_array( $vis_dists ) ? wp_json_encode( array_map( 'intval', $vis_dists ) ) : null,
            'visibility_neighborhood_ids' => is_array( $vis_neighs ) ? wp_json_encode( array_map( 'intval', $vis_neighs ) ) : null,
            'status'                      => sanitize_text_field( $req->get_param( 'status' ) ?? 'active' ),
            'created_by'                  => $uid,
            'created_by_name'             => self::_user_name( $uid ),
        ] );
        $id = (int) $wpdb->insert_id;
        if ( sanitize_text_field( $req->get_param( 'status' ) ?? 'active' ) === 'active' ) {
            require_once ACC_PATH . 'includes/helpers/melk-investor.php';
            Melk_Investor::match_listing( $id );
        }
        return new WP_REST_Response( [ 'data' => self::_fmt_listing( self::_get_listing_row( $id ) ) ], 201 );
    }

    public static function get_listing( WP_REST_Request $req ): WP_REST_Response {
        $id  = (int) $req['id'];
        $row = self::_get_listing_row( $id );
        if ( ! $row ) return new WP_REST_Response( [ 'message' => 'آگهی یافت نشد' ], 404 );
        $deny = self::_deny_listing_access( acl_current_user_id(), $row );
        if ( $deny ) return $deny;
        return new WP_REST_Response( [ 'data' => self::_fmt_listing( $row ) ] );
    }

    public static function update_listing( WP_REST_Request $req ): WP_REST_Response {
        $id  = (int) $req['id'];
        $row = self::_get_listing_row( $id );
        if ( ! $row ) return new WP_REST_Response( [ 'message' => 'آگهی یافت نشد' ], 404 );
        $uid = acl_current_user_id();
        if ( (int) $row['created_by'] !== $uid && ! self::_can_manage_construction( $uid ) ) {
            return new WP_REST_Response( [ 'message' => 'فقط ناشر می‌تواند ویرایش کند' ], 403 );
        }
        $upd = [];
        foreach ( [ 'title', 'deal_type', 'property_type', 'status', 'rent_period', 'renovation_code', 'visibility_mode', 'description' ] as $f ) {
            if ( $req->get_param( $f ) !== null ) {
                $upd[ $f ] = $f === 'description' ? sanitize_textarea_field( $req->get_param( $f ) ) : sanitize_text_field( $req->get_param( $f ) );
            }
        }
        foreach ( [ 'price', 'area_sqm' ] as $f ) {
            if ( $req->get_param( $f ) !== null ) $upd[ $f ] = (float) $req->get_param( $f );
        }
        foreach ( [ 'rooms', 'district_id', 'neighborhood_id' ] as $f ) {
            if ( $req->get_param( $f ) !== null ) $upd[ $f ] = (int) $req->get_param( $f ) ?: null;
        }
        if ( $req->get_param( 'visibility_roles' ) !== null ) {
            $upd['visibility_roles'] = wp_json_encode( array_values( (array) $req->get_param( 'visibility_roles' ) ) );
        }
        if ( $req->get_param( 'visibility_user_ids' ) !== null || $req->get_param( 'visibility_usernames' ) !== null ) {
            $user_ids = (array) ( $req->get_param( 'visibility_user_ids' ) ?? json_decode( $row['visibility_user_ids'] ?? '[]', true ) ?: [] );
            $usernames = self::_normalize_usernames( $req->get_param( 'visibility_usernames' ) ?? json_decode( $row['visibility_usernames'] ?? '[]', true ) );
            $user_ids = array_values( array_unique( array_merge( array_map( 'intval', $user_ids ), self::_resolve_usernames_to_ids( $usernames ) ) ) );
            $upd['visibility_user_ids'] = wp_json_encode( $user_ids );
            $upd['visibility_usernames'] = wp_json_encode( $usernames );
        }
        if ( $req->get_param( 'visibility_district_ids' ) !== null ) {
            $upd['visibility_district_ids'] = wp_json_encode( array_map( 'intval', (array) $req->get_param( 'visibility_district_ids' ) ) );
        }
        if ( $req->get_param( 'visibility_neighborhood_ids' ) !== null ) {
            $upd['visibility_neighborhood_ids'] = wp_json_encode( array_map( 'intval', (array) $req->get_param( 'visibility_neighborhood_ids' ) ) );
        }
        if ( $upd ) {
            global $wpdb;
            $wpdb->update( "{$wpdb->prefix}melk_listings", $upd, [ 'id' => $id ] );
        }
        $fresh = self::_get_listing_row( $id );
        if ( ( $fresh['status'] ?? '' ) === 'active' ) {
            require_once ACC_PATH . 'includes/helpers/melk-investor.php';
            Melk_Investor::match_listing( $id );
        }
        return new WP_REST_Response( [ 'data' => self::_fmt_listing( $fresh ) ] );
    }

    /* ── سرمایه‌گذار ─────────────────────────────────── */

    public static function get_investor_meta(): WP_REST_Response {
        require_once ACC_PATH . 'includes/helpers/melk-investor.php';
        $uid = acl_current_user_id();
        return new WP_REST_Response( [
            'data' => [
                'deal_types'    => Melk_Investor::DEAL_TYPES,
                'expert_types'  => Melk_Investor::EXPERT_TYPES,
                'geo'           => Melk_Investor::geo_options(),
                'unseen_alerts' => Melk_Investor::unseen_count( $uid ),
            ],
        ] );
    }

    public static function get_investor_profile(): WP_REST_Response {
        require_once ACC_PATH . 'includes/helpers/melk-investor.php';
        return new WP_REST_Response( [ 'data' => Melk_Investor::get_profile( acl_current_user_id() ) ] );
    }

    public static function save_investor_profile( WP_REST_Request $req ): WP_REST_Response {
        require_once ACC_PATH . 'includes/helpers/melk-investor.php';
        $res = Melk_Investor::save_profile( acl_current_user_id(), (array) $req->get_json_params() );
        return new WP_REST_Response( $res );
    }

    public static function get_investment_criteria(): WP_REST_Response {
        require_once ACC_PATH . 'includes/helpers/melk-investor.php';
        return new WP_REST_Response( [ 'data' => Melk_Investor::list_criteria( acl_current_user_id() ) ] );
    }

    public static function create_investment_criteria( WP_REST_Request $req ): WP_REST_Response {
        require_once ACC_PATH . 'includes/helpers/melk-investor.php';
        $res = Melk_Investor::create_criteria( acl_current_user_id(), (array) $req->get_json_params() );
        $code = ! empty( $res['success'] ) ? 201 : 400;
        return new WP_REST_Response( $res, $code );
    }

    public static function update_investment_criteria( WP_REST_Request $req ): WP_REST_Response {
        require_once ACC_PATH . 'includes/helpers/melk-investor.php';
        $res = Melk_Investor::update_criteria( (int) $req['id'], acl_current_user_id(), (array) $req->get_json_params() );
        $code = ! empty( $res['success'] ) ? 200 : 404;
        return new WP_REST_Response( $res, $code );
    }

    public static function delete_investment_criteria( WP_REST_Request $req ): WP_REST_Response {
        require_once ACC_PATH . 'includes/helpers/melk-investor.php';
        $ok = Melk_Investor::delete_criteria( (int) $req['id'], acl_current_user_id() );
        return new WP_REST_Response( [ 'success' => $ok ], $ok ? 200 : 404 );
    }

    public static function get_investor_matches( WP_REST_Request $req ): WP_REST_Response {
        require_once ACC_PATH . 'includes/helpers/melk-investor.php';
        $unseen = (bool) $req->get_param( 'unseen' );
        return new WP_REST_Response( [
            'data' => Melk_Investor::list_matches( acl_current_user_id(), $unseen ),
        ] );
    }

    public static function mark_investor_match_seen( WP_REST_Request $req ): WP_REST_Response {
        require_once ACC_PATH . 'includes/helpers/melk-investor.php';
        $ok = Melk_Investor::mark_match_seen( (int) $req['id'], acl_current_user_id() );
        return new WP_REST_Response( [ 'success' => $ok ], $ok ? 200 : 404 );
    }

    public static function get_investor_experts(): WP_REST_Response {
        require_once ACC_PATH . 'includes/helpers/melk-investor.php';
        return new WP_REST_Response( [ 'data' => Melk_Investor::list_experts() ] );
    }

    public static function get_investor_expert_requests(): WP_REST_Response {
        require_once ACC_PATH . 'includes/helpers/melk-investor.php';
        return new WP_REST_Response( [
            'data' => Melk_Investor::list_expert_requests( acl_current_user_id(), true ),
        ] );
    }

    public static function create_investor_expert_request( WP_REST_Request $req ): WP_REST_Response {
        require_once ACC_PATH . 'includes/helpers/melk-investor.php';
        $res = Melk_Investor::create_expert_request( acl_current_user_id(), (array) $req->get_json_params() );
        $code = ! empty( $res['success'] ) ? 201 : 400;
        return new WP_REST_Response( $res, $code );
    }

    public static function respond_investor_expert_request( WP_REST_Request $req ): WP_REST_Response {
        require_once ACC_PATH . 'includes/helpers/melk-investor.php';
        $uid  = acl_current_user_id();
        $body = (array) $req->get_json_params();
        $text = sanitize_textarea_field( $body['response'] ?? $body['response_text'] ?? '' );
        if ( ! $text ) {
            return new WP_REST_Response( [ 'message' => 'متن پاسخ الزامی است' ], 400 );
        }
        global $wpdb;
        $row = $wpdb->get_row( $wpdb->prepare(
            "SELECT * FROM " . Melk_Investor::expert_req_table() . " WHERE id = %d", (int) $req['id']
        ), ARRAY_A );
        if ( ! $row ) {
            return new WP_REST_Response( [ 'message' => 'درخواست یافت نشد' ], 404 );
        }
        $types = json_decode( $row['expert_types'] ?? '[]', true ) ?: [];
        if ( ! self::_user_can_answer_expert_request( $uid, $types ) ) {
            return new WP_REST_Response( [ 'message' => 'دسترسی پاسخ‌دهی ندارید' ], 403 );
        }
        $res = Melk_Investor::respond_expert_request( (int) $req['id'], $uid, $text );
        return new WP_REST_Response( $res );
    }

    private static function _user_can_answer_expert_request( int $uid, array $types ): bool {
        if ( acl_user_is_super_admin( $uid ) || acl_user_can( $uid, 'melk.manage' ) ) {
            return true;
        }
        require_once ACC_PATH . 'includes/helpers/melk-investor.php';
        foreach ( $types as $t ) {
            $meta = Melk_Investor::EXPERT_TYPES[ $t ] ?? null;
            if ( $meta && self::_user_has_role_slug( $uid, $meta['role'] ) ) {
                return true;
            }
        }
        return false;
    }

    private static function _user_has_role_slug( int $uid, string $slug ): bool {
        global $wpdb;
        return (bool) (int) $wpdb->get_var( $wpdb->prepare(
            "SELECT COUNT(*) 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 = %s",
            $uid, $slug
        ) );
    }

    /* ── مالیات و حقوق ───────────────────────────────── */

    public static function get_advisory_meta(): WP_REST_Response {
        require_once ACC_PATH . 'includes/helpers/melk-advisory-knowledge.php';
        require_once ACC_PATH . 'includes/helpers/melk-advisory-ai.php';
        return new WP_REST_Response( [
            'data' => [
                'tax_categories'   => Melk_Advisory_Knowledge::tax_categories(),
                'legal_categories' => Melk_Advisory_Knowledge::legal_categories(),
                'tax_count'        => count( Melk_Advisory_Knowledge::tax_rules() ),
                'legal_count'      => count( Melk_Advisory_Knowledge::legal_topics() ),
                'prompts'          => Melk_Advisory_Knowledge::quick_prompts(),
                'ai'               => Melk_Advisory_AI::status(),
            ],
        ] );
    }

    public static function get_advisory_tax_list( WP_REST_Request $req ): WP_REST_Response {
        require_once ACC_PATH . 'includes/helpers/melk-advisory-knowledge.php';
        $q = sanitize_text_field( $req->get_param( 'q' ) ?? '' );
        if ( $q ) {
            return new WP_REST_Response( [ 'data' => Melk_Advisory_Knowledge::search( $q, 'tax' )['tax'] ?? [] ] );
        }
        return new WP_REST_Response( [ 'data' => Melk_Advisory_Knowledge::list_tax_public() ] );
    }

    public static function get_advisory_tax_detail( WP_REST_Request $req ): WP_REST_Response {
        require_once ACC_PATH . 'includes/helpers/melk-advisory-knowledge.php';
        $row = Melk_Advisory_Knowledge::get_tax( sanitize_key( $req['key'] ) );
        if ( ! $row ) {
            return new WP_REST_Response( [ 'message' => 'قانون یافت نشد' ], 404 );
        }
        return new WP_REST_Response( [ 'data' => $row ] );
    }

    public static function get_advisory_legal_list( WP_REST_Request $req ): WP_REST_Response {
        require_once ACC_PATH . 'includes/helpers/melk-advisory-knowledge.php';
        $q = sanitize_text_field( $req->get_param( 'q' ) ?? '' );
        if ( $q ) {
            return new WP_REST_Response( [ 'data' => Melk_Advisory_Knowledge::search( $q, 'legal' )['legal'] ?? [] ] );
        }
        return new WP_REST_Response( [ 'data' => Melk_Advisory_Knowledge::list_legal_public() ] );
    }

    public static function get_advisory_legal_detail( WP_REST_Request $req ): WP_REST_Response {
        require_once ACC_PATH . 'includes/helpers/melk-advisory-knowledge.php';
        $row = Melk_Advisory_Knowledge::get_legal( sanitize_key( $req['key'] ) );
        if ( ! $row ) {
            return new WP_REST_Response( [ 'message' => 'موضوع حقوقی یافت نشد' ], 404 );
        }
        return new WP_REST_Response( [ 'data' => $row ] );
    }

    public static function search_advisory( WP_REST_Request $req ): WP_REST_Response {
        require_once ACC_PATH . 'includes/helpers/melk-advisory-knowledge.php';
        $q = sanitize_text_field( $req->get_param( 'q' ) ?? '' );
        $domain = sanitize_key( $req->get_param( 'domain' ) ?? 'all' );
        return new WP_REST_Response( [
            'data' => Melk_Advisory_Knowledge::search( $q, $domain ),
        ] );
    }

    public static function advisory_chat( WP_REST_Request $req ): WP_REST_Response {
        require_once ACC_PATH . 'includes/helpers/melk-advisory-ai.php';
        $body = (array) $req->get_json_params();
        $uid  = acl_current_user_id();
        $res  = Melk_Advisory_AI::chat(
            $uid,
            (string) ( $body['message'] ?? '' ),
            is_array( $body['history'] ?? null ) ? $body['history'] : [],
            [
                'topic'      => $body['topic'] ?? 'both',
                'context'    => is_array( $body['context'] ?? null ) ? $body['context'] : [],
                'session_id' => (int) ( $body['session_id'] ?? 0 ),
            ]
        );
        $code = ! empty( $res['success'] ) ? 200 : 400;
        return new WP_REST_Response( [ 'data' => $res ], $code );
    }

    public static function get_advisory_sessions(): WP_REST_Response {
        require_once ACC_PATH . 'includes/helpers/melk-advisory.php';
        return new WP_REST_Response( [
            'data' => Melk_Advisory::list_sessions( acl_current_user_id() ),
        ] );
    }

    public static function get_advisory_session_messages( WP_REST_Request $req ): WP_REST_Response {
        require_once ACC_PATH . 'includes/helpers/melk-advisory.php';
        $msgs = Melk_Advisory::list_messages( (int) $req['id'], acl_current_user_id() );
        if ( ! $msgs && ! Melk_Advisory::get_session( (int) $req['id'], acl_current_user_id() ) ) {
            return new WP_REST_Response( [ 'message' => 'جلسه یافت نشد' ], 404 );
        }
        return new WP_REST_Response( [ 'data' => $msgs ] );
    }

    public static function escalate_advisory( WP_REST_Request $req ): WP_REST_Response {
        require_once ACC_PATH . 'includes/helpers/melk-advisory.php';
        require_once ACC_PATH . 'includes/helpers/melk-investor.php';
        $body = (array) $req->get_json_params();
        $uid  = acl_current_user_id();
        $session_id = (int) ( $body['session_id'] ?? 0 );
        $session = $session_id ? Melk_Advisory::get_session( $session_id, $uid ) : null;
        $topic = sanitize_key( $body['topic'] ?? ( $session['topic'] ?? 'legal' ) );
        $types = $topic === 'tax' ? [ 'finance' ] : ( $topic === 'legal' ? [ 'legal', 'registry' ] : [ 'legal', 'finance' ] );
        $summary = sanitize_textarea_field( $body['summary'] ?? '' );
        if ( ! $summary && $session_id ) {
            $msgs = Melk_Advisory::list_messages( $session_id, $uid );
            $parts = array_map( static fn( $m ) => ( $m['role'] === 'user' ? 'سؤال: ' : 'پاسخ: ' ) . $m['body'], array_slice( $msgs, -4 ) );
            $summary = implode( "\n", $parts );
        }
        $res = Melk_Investor::create_expert_request( $uid, [
            'expert_types' => $types,
            'subject'      => sanitize_text_field( $body['subject'] ?? 'ارجاع از مشاور AI — بررسی رسمی' ),
            'description'  => $summary,
            'listing_id'   => $session['listing_id'] ?? ( $body['listing_id'] ?? null ),
        ] );
        return new WP_REST_Response( $res, ! empty( $res['success'] ) ? 201 : 400 );
    }

    /* ── Helpers ─────────────────────────────────────── */

    private static function _can_manage_construction( int $uid ): bool {
        return acl_user_is_super_admin( $uid )
            || acl_user_can( $uid, 'melk.manage' )
            || acl_user_can( $uid, 'melk.construction.manage' );
    }

    private static function _can_publish_listing( int $uid ): bool {
        return acl_user_is_super_admin( $uid )
            || acl_user_can( $uid, 'melk.manage' )
            || acl_user_can( $uid, 'melk.listings.publish' );
    }

    private static function _normalize_usernames( $raw ): array {
        if ( ! is_array( $raw ) ) return [];
        return array_values( array_filter( array_map( static function ( $u ) {
            return sanitize_user( trim( (string) $u ), true );
        }, $raw ) ) );
    }

    private static function _resolve_usernames_to_ids( array $usernames ): array {
        if ( ! $usernames ) return [];
        global $wpdb;
        $ids = [];
        foreach ( $usernames as $login ) {
            $id = (int) $wpdb->get_var( $wpdb->prepare(
                "SELECT ID FROM {$wpdb->users} WHERE user_login = %s LIMIT 1", $login
            ) );
            if ( $id ) $ids[] = $id;
        }
        return $ids;
    }

    private static function _user_in_geo_audience( int $uid, array $row ): bool {
        $d_ids = json_decode( $row['visibility_district_ids'] ?? '[]', true ) ?: [];
        $n_ids = json_decode( $row['visibility_neighborhood_ids'] ?? '[]', true ) ?: [];
        foreach ( $d_ids as $did ) {
            if ( ! acl_deny_scope( $uid, 'district', (int) $did, 'melk.view' ) ) return true;
        }
        foreach ( $n_ids as $nid ) {
            if ( ! acl_deny_scope( $uid, 'neighborhood', (int) $nid, 'melk.view' ) ) return true;
        }
        return false;
    }

    private static function _user_name( int $uid ): string {
        $u = get_userdata( $uid );
        return $u ? ( $u->display_name ?: $u->user_login ) : '';
    }

    private static function _get_project_row( int $id ): ?array {
        global $wpdb;
        return $wpdb->get_row( $wpdb->prepare(
            "SELECT * FROM {$wpdb->prefix}melk_construction_projects WHERE id = %d", $id
        ), ARRAY_A ) ?: null;
    }

    private static function _get_listing_row( int $id ): ?array {
        global $wpdb;
        return $wpdb->get_row( $wpdb->prepare(
            "SELECT * FROM {$wpdb->prefix}melk_listings WHERE id = %d", $id
        ), ARRAY_A ) ?: null;
    }

    private static function _get_project_participants( int $project_id ): array {
        global $wpdb;
        $rows = $wpdb->get_results( $wpdb->prepare(
            "SELECT cp.*, u.display_name, u.user_login
             FROM {$wpdb->prefix}melk_construction_participants cp
             LEFT JOIN {$wpdb->users} u ON u.ID = cp.user_id
             WHERE cp.project_id = %d ORDER BY cp.id", $project_id
        ), ARRAY_A );
        $labels = [ 'contractor' => 'پیمانکار', 'owner' => 'مالک', 'supervisor' => 'ناظر', 'investor' => 'سرمایه‌گذار' ];
        return array_map( static function ( $r ) use ( $labels ) {
            return [
                'id'            => (int) $r['id'],
                'user_id'       => (int) $r['user_id'],
                'display_name'  => $r['display_name'] ?: $r['user_login'],
                'role_type'     => $r['role_type'],
                'role_label'    => $labels[ $r['role_type'] ] ?? $r['role_type'],
                'share_percent' => $r['share_percent'] !== null ? (float) $r['share_percent'] : null,
                'notes'         => $r['notes'],
            ];
        }, $rows ?: [] );
    }

    private static function _fmt_project( ?array $r ): ?array {
        if ( ! $r ) return null;
        $types = [ 'construction' => 'ساخت‌وساز', 'partnership' => 'مشارکت', 'renovation' => 'نوسازی' ];
        $st    = [ 'draft' => 'پیش‌نویس', 'active' => 'فعال', 'completed' => 'تکمیل', 'cancelled' => 'لغو' ];
        return [
            'id'                => (int) $r['id'],
            'project_key'       => $r['project_key'],
            'title'             => $r['title'],
            'project_type'      => $r['project_type'],
            'project_type_label'=> $types[ $r['project_type'] ] ?? $r['project_type'],
            'status'            => $r['status'],
            'status_label'      => $st[ $r['status'] ] ?? $r['status'],
            'parcel_id'         => $r['parcel_id'] ? (int) $r['parcel_id'] : null,
            'case_id'           => $r['case_id'] ? (int) $r['case_id'] : null,
            'district_id'       => $r['district_id'] ? (int) $r['district_id'] : null,
            'neighborhood_id'   => $r['neighborhood_id'] ? (int) $r['neighborhood_id'] : null,
            'renovation_code'   => $r['renovation_code'],
            'budget'            => $r['budget'] !== null ? (float) $r['budget'] : null,
            'area_sqm'          => $r['area_sqm'] !== null ? (float) $r['area_sqm'] : null,
            'floors'            => $r['floors'] !== null ? (int) $r['floors'] : null,
            'description'       => $r['description'],
            'created_by'        => (int) $r['created_by'],
            'created_by_name'   => $r['created_by_name'],
            'participant_count' => isset( $r['participant_count'] ) ? (int) $r['participant_count'] : null,
            'created_at'        => $r['created_at'],
            'updated_at'        => $r['updated_at'],
        ];
    }

    private static function _fmt_listing( ?array $r ): ?array {
        if ( ! $r ) return null;
        $deals = [
            'sale' => 'فروش', 'rent' => 'اجاره', 'rent_commercial' => 'اجاره تجاری',
            'partnership' => 'مشارکت', 'land_sale' => 'فروش زمین', 'pre_sale' => 'پیش‌فروش',
            'mortgage' => 'رهن و اجاره', 'exchange' => 'معاوضه',
        ];
        $props = [
            'residential_apartment' => 'آپارتمان', 'residential_villa' => 'ویلا',
            'residential_land' => 'زمین مسکونی', 'commercial_retail' => 'تجاری',
            'commercial_office' => 'دفتر', 'commercial_industrial' => 'صنعتی',
            'agricultural' => 'کشاورزی',
        ];
        $vis   = [
            'private' => 'خصوصی', 'role' => 'نقش‌محور', 'district' => 'منطقه ملک',
            'geo' => 'مخاطب جغرافیایی', 'users' => 'کاربران مشخص',
            'network' => 'شبکه', 'public' => 'عمومی',
        ];
        $form_key   = $r['form_template_key'] ?? null;
        $form_data  = json_decode( $r['form_data'] ?? '{}', true ) ?: [];
        $form_meta  = null;
        $attachment = [];
        if ( $form_key ) {
            require_once ACC_PATH . 'includes/helpers/melk-listing-forms.php';
            $form_meta  = Melk_Listing_Forms::template_meta( $form_key );
            $attachment = Melk_Listing_Forms::attachment_fields( $form_key, $form_data, $r );
        }
        return [
            'id'                          => (int) $r['id'],
            'listing_key'                 => $r['listing_key'],
            'deal_type'                   => $r['deal_type'],
            'deal_type_label'             => $deals[ $r['deal_type'] ] ?? $r['deal_type'],
            'property_type'               => $r['property_type'] ?? 'residential_apartment',
            'property_type_label'         => $props[ $r['property_type'] ?? '' ] ?? ( $r['property_type'] ?? '' ),
            'form_template_key'           => $form_key,
            'form_template_title'         => $form_meta['title'] ?? null,
            'form_template_icon'          => $form_meta['icon'] ?? null,
            'form_template_standard'      => $form_meta['standard'] ?? null,
            'form_data'                   => $form_data,
            'form_attachment'             => $attachment,
            'title'                       => $r['title'],
            'description'                 => $r['description'],
            'price'                       => $r['price'] !== null ? (float) $r['price'] : null,
            'rent_period'                 => $r['rent_period'],
            'area_sqm'                    => $r['area_sqm'] !== null ? (float) $r['area_sqm'] : null,
            'rooms'                       => $r['rooms'] !== null ? (int) $r['rooms'] : null,
            'district_id'                 => $r['district_id'] ? (int) $r['district_id'] : null,
            'neighborhood_id'             => $r['neighborhood_id'] ? (int) $r['neighborhood_id'] : null,
            'renovation_code'             => $r['renovation_code'],
            'visibility_mode'             => $r['visibility_mode'],
            'visibility_label'            => $vis[ $r['visibility_mode'] ] ?? $r['visibility_mode'],
            'visibility_roles'            => json_decode( $r['visibility_roles'] ?? '[]', true ) ?: [],
            'visibility_user_ids'         => json_decode( $r['visibility_user_ids'] ?? '[]', true ) ?: [],
            'visibility_usernames'        => json_decode( $r['visibility_usernames'] ?? '[]', true ) ?: [],
            'visibility_district_ids'     => json_decode( $r['visibility_district_ids'] ?? '[]', true ) ?: [],
            'visibility_neighborhood_ids' => json_decode( $r['visibility_neighborhood_ids'] ?? '[]', true ) ?: [],
            'status'                      => $r['status'],
            'created_by'                  => (int) $r['created_by'],
            'created_by_name'             => $r['created_by_name'],
            'created_at'                  => $r['created_at'],
            'updated_at'                  => $r['updated_at'],
        ];
    }

    private static function _append_geo_where( int $uid, string $d_col, string $n_col, array &$w, array &$args ): void {
        if ( acl_user_is_super_admin( $uid ) ) return;
        $d_allowed = acl_get_allowed_scope_ids( $uid, 'district' );
        $n_allowed = acl_get_allowed_scope_ids( $uid, 'neighborhood' );
        if ( $d_allowed === null && $n_allowed === null ) return;
        $conds = [];
        if ( $d_allowed !== null && $d_allowed ) {
            $ph = implode( ',', array_fill( 0, count( $d_allowed ), '%d' ) );
            $conds[] = "({$d_col} IN ({$ph}) OR {$d_col} IS NULL)";
            $args = array_merge( $args, $d_allowed );
        }
        if ( $n_allowed !== null && $n_allowed ) {
            global $wpdb;
            $ph = implode( ',', array_fill( 0, count( $n_allowed ), '%d' ) );
            $conds[] = "({$n_col} IN ({$ph}) OR {$n_col} IS NULL)";
            $args = array_merge( $args, $n_allowed );
        }
        if ( $conds ) $w[] = '(' . implode( ' OR ', $conds ) . ')';
    }

    private static function _deny_project_access( int $uid, array $row, bool $edit = false ): ?WP_REST_Response {
        if ( acl_user_is_super_admin( $uid ) || self::_can_manage_construction( $uid ) ) return null;
        if ( (int) $row['created_by'] === $uid ) return null;
        global $wpdb;
        $is_part = (int) $wpdb->get_var( $wpdb->prepare(
            "SELECT COUNT(*) FROM {$wpdb->prefix}melk_construction_participants WHERE project_id = %d AND user_id = %d",
            (int) $row['id'], $uid
        ) );
        if ( $is_part ) return $edit ? new WP_REST_Response( [ 'message' => 'فقط مالک/مدیر می‌تواند ویرایش کند' ], 403 ) : null;
        if ( (int) $row['district_id'] && ! acl_deny_scope( $uid, 'district', (int) $row['district_id'], 'melk.view' ) ) return null;
        return new WP_REST_Response( [ 'message' => 'دسترسی به پروژه مجاز نیست' ], 403 );
    }

    private static function _user_can_see_listing( int $uid, array $row ): bool {
        if ( acl_user_is_super_admin( $uid ) || acl_user_can( $uid, 'melk.manage' ) ) return true;
        if ( (int) $row['created_by'] === $uid ) return true;

        $mode = $row['visibility_mode'] ?? 'network';
        if ( $mode === 'public' || $mode === 'network' ) return true;

        if ( $mode === 'private' || $mode === 'users' ) {
            $ids = json_decode( $row['visibility_user_ids'] ?? '[]', true ) ?: [];
            if ( in_array( $uid, array_map( 'intval', $ids ), true ) ) return true;
            $logins = json_decode( $row['visibility_usernames'] ?? '[]', true ) ?: [];
            if ( $logins ) {
                $user = get_userdata( $uid );
                if ( $user && in_array( $user->user_login, $logins, true ) ) return true;
            }
            return $mode === 'private' ? false : false;
        }

        if ( $mode === 'role' ) {
            $allowed = json_decode( $row['visibility_roles'] ?? '[]', true ) ?: [];
            if ( ! $allowed ) return false;
            global $wpdb;
            $user_roles = $wpdb->get_col( $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", $uid
            ) );
            return (bool) array_intersect( $allowed, $user_roles ?: [] );
        }

        if ( $mode === 'geo' ) {
            return self::_user_in_geo_audience( $uid, $row );
        }

        if ( $mode === 'district' ) {
            $did = (int) ( $row['district_id'] ?? 0 );
            if ( ! $did ) return false;
            return ! acl_deny_scope( $uid, 'district', $did, 'melk.view' );
        }

        return false;
    }

    private static function _deny_listing_access( int $uid, array $row ): ?WP_REST_Response {
        return self::_user_can_see_listing( $uid, $row )
            ? null
            : new WP_REST_Response( [ 'message' => 'دسترسی به آگهی مجاز نیست' ], 403 );
    }

    /* ── بنگاه‌های شهرستان ───────────────────────────── */

    public static function get_county_form_bank( WP_REST_Request $req ): WP_REST_Response {
        require_once ACC_PATH . 'includes/helpers/melk-county-forms.php';
        $type = sanitize_text_field( $req->get_param( 'request_type' ) ?? '' ) ?: null;
        return new WP_REST_Response( [ 'data' => Melk_County_Forms::bank_from_db( $type ) ] );
    }

    public static function get_county_form_config( WP_REST_Request $req ): WP_REST_Response {
        require_once ACC_PATH . 'includes/helpers/melk-county-forms.php';
        $key = sanitize_text_field( $req->get_param( 'form_key' ) ?? '' );
        $type = sanitize_text_field( $req->get_param( 'request_type' ) ?? '' );
        if ( ! $key && $type ) {
            $fb = Melk_County_Forms::form_by_request_type( $type );
            if ( $fb ) $key = $fb['form_key'];
        }
        if ( ! $key ) return new WP_REST_Response( [ 'message' => 'form_key یا request_type الزامی است' ], 400 );
        $cfg = Melk_County_Forms::config_from_db( $key );
        if ( ! $cfg ) return new WP_REST_Response( [ 'message' => 'فرم یافت نشد' ], 404 );
        return new WP_REST_Response( [ 'data' => $cfg ] );
    }

    public static function get_county_requests( WP_REST_Request $req ): WP_REST_Response {
        global $wpdb;
        $uid = acl_current_user_id();
        $pre = $wpdb->prefix;
        $w   = [ '1=1' ];
        $args = [];

        if ( $req->get_param( 'status' ) ) {
            $w[]    = 'r.status = %s';
            $args[] = sanitize_text_field( $req->get_param( 'status' ) );
        }
        if ( $req->get_param( 'request_type' ) ) {
            $w[]    = 'r.request_type = %s';
            $args[] = sanitize_text_field( $req->get_param( 'request_type' ) );
        }
        if ( $req->get_param( 'priority' ) ) {
            $w[]    = 'r.priority = %s';
            $args[] = sanitize_text_field( $req->get_param( 'priority' ) );
        }
        if ( ! self::_can_manage_county( $uid ) || $req->get_param( 'mine' ) ) {
            $w[]    = 'r.created_by = %d';
            $args[] = $uid;
        }

        $sql = "SELECT r.* FROM {$pre}melk_county_requests r
                WHERE " . implode( ' AND ', $w ) . ' ORDER BY r.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' => array_map( [ __CLASS__, '_fmt_county_request' ], $rows ?: [] ) ] );
    }

    public static function create_county_request( WP_REST_Request $req ): WP_REST_Response {
        global $wpdb;
        $uid = acl_current_user_id();
        if ( ! self::_can_submit_county( $uid ) ) {
            return new WP_REST_Response( [ 'message' => 'دسترسی ثبت درخواست ندارید' ], 403 );
        }

        require_once ACC_PATH . 'includes/helpers/melk-county-forms.php';
        $form_key = sanitize_text_field( $req->get_param( 'form_template_key' ) ?? $req->get_param( 'form_key' ) ?? '' );
        $form_values = $req->get_param( 'form_data' );
        if ( ! is_array( $form_values ) ) $form_values = [];

        foreach ( [ 'title', 'description', 'priority', 'county', 'city', 'agency_name',
                  'contact_phone', 'contact_email', 'request_type' ] as $fk ) {
            if ( $req->get_param( $fk ) !== null && ! isset( $form_values[ $fk ] ) ) {
                $form_values[ $fk ] = $req->get_param( $fk );
            }
        }

        if ( ! $form_key && $req->get_param( 'request_type' ) ) {
            $fb = Melk_County_Forms::form_by_request_type( sanitize_text_field( $req->get_param( 'request_type' ) ) );
            if ( $fb ) $form_key = $fb['form_key'];
        }

        $mapped = $form_key
            ? Melk_County_Forms::map_submission( $form_key, $form_values )
            : [ 'columns' => [], 'form_data' => $form_values, 'errors' => [] ];

        if ( ! empty( $mapped['errors'] ) ) {
            return new WP_REST_Response( [ 'message' => implode( ' · ', $mapped['errors'] ) ], 400 );
        }

        $cols = $mapped['columns'];
        $title = sanitize_text_field( $cols['title'] ?? $req->get_param( 'title' ) ?? '' );
        $county = sanitize_text_field( $cols['county'] ?? $req->get_param( 'county' ) ?? '' );
        if ( ! $title || ! $county ) {
            return new WP_REST_Response( [ 'message' => 'عنوان و شهرستان الزامی است' ], 400 );
        }

        $status = sanitize_text_field( $req->get_param( 'status' ) ?? 'draft' );
        if ( ! in_array( $status, [ 'draft', 'submitted' ], true ) ) {
            $status = 'draft';
        }

        $extra_data = $mapped['form_data'];
        foreach ( array_keys( $cols ) as $ck ) {
            if ( $ck !== 'form_template_key' ) unset( $extra_data[ $ck ] );
        }

        $key = 'CR-' . strtoupper( substr( md5( uniqid( (string) $uid, true ) ), 0, 8 ) );
        $wpdb->insert( "{$wpdb->prefix}melk_county_requests", [
            'request_key'       => $key,
            'request_type'      => sanitize_text_field( $cols['request_type'] ?? $req->get_param( 'request_type' ) ?? 'demand' ),
            'form_template_key' => $form_key ?: null,
            'form_data'         => $extra_data ? wp_json_encode( $extra_data, JSON_UNESCAPED_UNICODE ) : null,
            'title'             => $title,
            'description'       => sanitize_textarea_field( $cols['description'] ?? $req->get_param( 'description' ) ?? '' ),
            'priority'          => sanitize_text_field( $cols['priority'] ?? $req->get_param( 'priority' ) ?? 'normal' ),
            'status'            => $status,
            'county'            => $county,
            'city'              => sanitize_text_field( $cols['city'] ?? $req->get_param( 'city' ) ?? '' ) ?: null,
            'agency_name'       => sanitize_text_field( $cols['agency_name'] ?? $req->get_param( 'agency_name' ) ?? '' ) ?: null,
            'contact_phone'     => sanitize_text_field( $cols['contact_phone'] ?? $req->get_param( 'contact_phone' ) ?? '' ) ?: null,
            'contact_email'     => sanitize_email( $cols['contact_email'] ?? $req->get_param( 'contact_email' ) ?? '' ) ?: null,
            'created_by'        => $uid,
            'created_by_name'   => self::_user_name( $uid ),
        ] );

        $id = (int) $wpdb->insert_id;
        return new WP_REST_Response( [ 'data' => self::_fmt_county_request( self::_get_county_request_row( $id ) ) ], 201 );
    }

    public static function get_county_request( WP_REST_Request $req ): WP_REST_Response {
        $id  = (int) $req['id'];
        $row = self::_get_county_request_row( $id );
        if ( ! $row ) return new WP_REST_Response( [ 'message' => 'درخواست یافت نشد' ], 404 );
        $deny = self::_deny_county_access( acl_current_user_id(), $row );
        if ( $deny ) return $deny;
        return new WP_REST_Response( [ 'data' => self::_fmt_county_request( $row ) ] );
    }

    public static function update_county_request( WP_REST_Request $req ): WP_REST_Response {
        $id  = (int) $req['id'];
        $row = self::_get_county_request_row( $id );
        if ( ! $row ) return new WP_REST_Response( [ 'message' => 'درخواست یافت نشد' ], 404 );
        $uid = acl_current_user_id();
        $is_admin = self::_can_manage_county( $uid );
        if ( ! $is_admin && (int) $row['created_by'] !== $uid ) {
            return new WP_REST_Response( [ 'message' => 'دسترسی ویرایش ندارید' ], 403 );
        }
        if ( ! $is_admin && ! in_array( $row['status'], [ 'draft', 'submitted' ], true ) ) {
            return new WP_REST_Response( [ 'message' => 'درخواست در این وضعیت قابل ویرایش نیست' ], 403 );
        }

        $upd = [];
        foreach ( [ 'title', 'request_type', 'description', 'priority', 'county', 'city', 'agency_name', 'contact_phone', 'contact_email' ] as $f ) {
            if ( $req->get_param( $f ) !== null ) {
                $upd[ $f ] = in_array( $f, [ 'description' ], true )
                    ? sanitize_textarea_field( $req->get_param( $f ) )
                    : ( $f === 'contact_email' ? sanitize_email( $req->get_param( $f ) ) : sanitize_text_field( $req->get_param( $f ) ) );
            }
        }
        if ( ! $is_admin && $req->get_param( 'status' ) !== null ) {
            $st = sanitize_text_field( $req->get_param( 'status' ) );
            if ( in_array( $st, [ 'draft', 'submitted' ], true ) ) {
                $upd['status'] = $st;
            }
        }
        if ( $upd ) {
            global $wpdb;
            $wpdb->update( "{$wpdb->prefix}melk_county_requests", $upd, [ 'id' => $id ] );
        }
        return new WP_REST_Response( [ 'data' => self::_fmt_county_request( self::_get_county_request_row( $id ) ) ] );
    }

    public static function update_county_request_status( WP_REST_Request $req ): WP_REST_Response {
        $id  = (int) $req['id'];
        $row = self::_get_county_request_row( $id );
        if ( ! $row ) return new WP_REST_Response( [ 'message' => 'درخواست یافت نشد' ], 404 );
        $uid = acl_current_user_id();
        if ( ! self::_can_manage_county( $uid ) ) {
            return new WP_REST_Response( [ 'message' => 'دسترسی مدیریت ندارید' ], 403 );
        }

        $status = sanitize_text_field( $req->get_param( 'status' ) ?? '' );
        $valid  = [ 'draft', 'submitted', 'under_review', 'in_progress', 'resolved', 'closed' ];
        if ( ! in_array( $status, $valid, true ) ) {
            return new WP_REST_Response( [ 'message' => 'وضعیت نامعتبر' ], 400 );
        }

        $upd = [
            'status'      => $status,
            'reviewed_by' => $uid,
            'reviewed_at' => current_time( 'mysql' ),
        ];
        if ( $req->get_param( 'admin_note' ) !== null ) {
            $upd['admin_note'] = sanitize_textarea_field( $req->get_param( 'admin_note' ) );
        }
        if ( $req->get_param( 'assigned_to' ) !== null ) {
            $aid = (int) $req->get_param( 'assigned_to' );
            $upd['assigned_to'] = $aid ?: null;
            $upd['assigned_name'] = $aid ? self::_user_name( $aid ) : null;
        }
        if ( in_array( $status, [ 'resolved', 'closed' ], true ) ) {
            $upd['resolved_at'] = current_time( 'mysql' );
        }

        global $wpdb;
        $wpdb->update( "{$wpdb->prefix}melk_county_requests", $upd, [ 'id' => $id ] );
        return new WP_REST_Response( [ 'data' => self::_fmt_county_request( self::_get_county_request_row( $id ) ) ] );
    }

    /* ── متخصصان ساختمان ─────────────────────────────── */

    public static function get_specialist_form_bank( WP_REST_Request $req ): WP_REST_Response {
        require_once ACC_PATH . 'includes/helpers/melk-specialist-forms.php';
        $specialty = sanitize_text_field( $req->get_param( 'specialty' ) ?? '' ) ?: null;
        return new WP_REST_Response( [ 'data' => Melk_Specialist_Forms::bank_from_db( $specialty ) ] );
    }

    public static function get_specialist_form_config( WP_REST_Request $req ): WP_REST_Response {
        require_once ACC_PATH . 'includes/helpers/melk-specialist-forms.php';
        $key = sanitize_text_field( $req->get_param( 'form_key' ) ?? '' );
        $specialty = sanitize_text_field( $req->get_param( 'specialty' ) ?? '' );
        if ( ! $key && $specialty ) {
            $cfg = Melk_Specialist_Forms::form_by_specialty( $specialty );
            if ( $cfg ) $key = $cfg['form_key'];
        }
        if ( ! $key ) return new WP_REST_Response( [ 'message' => 'form_key یا specialty الزامی است' ], 400 );
        $cfg = Melk_Specialist_Forms::config_from_db( $key );
        if ( ! $cfg ) return new WP_REST_Response( [ 'message' => 'فرم یافت نشد' ], 404 );
        return new WP_REST_Response( [ 'data' => $cfg ] );
    }

    public static function get_building_specialists( WP_REST_Request $req ): WP_REST_Response {
        global $wpdb;
        $uid = acl_current_user_id();
        $pre = $wpdb->prefix;
        $w   = [ '1=1' ];
        $args = [];

        if ( $req->get_param( 'specialty' ) ) {
            $w[]    = 's.specialty = %s';
            $args[] = sanitize_text_field( $req->get_param( 'specialty' ) );
        }

        $show_all = self::_can_approve_specialists( $uid ) && $req->get_param( 'all' );
        $mine     = $req->get_param( 'mine' );

        if ( $mine ) {
            $w[]    = 's.user_id = %d';
            $args[] = $uid;
        } elseif ( ! self::_can_browse_specialist_directory( $uid ) ) {
            return new WP_REST_Response( [ 'data' => [] ] );
        } elseif ( ! $show_all ) {
            $w[]    = 's.status = %s';
            $args[] = 'approved';
        } elseif ( $req->get_param( 'status' ) ) {
            $w[]    = 's.status = %s';
            $args[] = sanitize_text_field( $req->get_param( 'status' ) );
        }

        $sql = "SELECT s.*, u.display_name, u.user_login
                FROM {$pre}melk_building_specialists s
                LEFT JOIN {$wpdb->users} u ON u.ID = s.user_id
                WHERE " . implode( ' AND ', $w ) . ' ORDER BY s.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' => array_map( [ __CLASS__, '_fmt_specialist' ], $rows ?: [] ) ] );
    }

    public static function register_building_specialist( WP_REST_Request $req ): WP_REST_Response {
        global $wpdb;
        $uid = acl_current_user_id();
        if ( ! self::_can_register_specialist( $uid ) ) {
            return new WP_REST_Response( [ 'message' => 'دسترسی ثبت‌نام متخصص ندارید' ], 403 );
        }

        $existing = self::_get_specialist_by_user( $uid );
        if ( $existing && $existing['status'] !== 'rejected' ) {
            return new WP_REST_Response( [ 'message' => 'پروفایل متخصص قبلاً ثبت شده است' ], 400 );
        }

        require_once ACC_PATH . 'includes/helpers/melk-specialist-forms.php';
        $form_key = sanitize_text_field( $req->get_param( 'form_template_key' ) ?? $req->get_param( 'form_key' ) ?? '' );
        $form_values = $req->get_param( 'form_data' );
        if ( ! is_array( $form_values ) ) $form_values = [];

        foreach ( [ 'specialty', 'license_no', 'years_experience', 'county', 'city',
                  'contact_phone', 'contact_email', 'bio', 'credentials', 'service_areas' ] as $fk ) {
            if ( $req->get_param( $fk ) !== null && ! isset( $form_values[ $fk ] ) ) {
                $form_values[ $fk ] = $req->get_param( $fk );
            }
        }

        if ( ! $form_key && ! empty( $form_values['specialty'] ) ) {
            $fb = Melk_Specialist_Forms::form_by_specialty( sanitize_text_field( $form_values['specialty'] ) );
            if ( $fb ) $form_key = $fb['form_key'];
        }
        if ( ! $form_key && $req->get_param( 'specialty' ) ) {
            $fb = Melk_Specialist_Forms::form_by_specialty( sanitize_text_field( $req->get_param( 'specialty' ) ) );
            if ( $fb ) $form_key = $fb['form_key'];
        }

        $mapped = $form_key
            ? Melk_Specialist_Forms::map_submission( $form_key, $form_values )
            : [ 'columns' => [], 'form_data' => $form_values, 'errors' => [] ];

        if ( ! empty( $mapped['errors'] ) ) {
            return new WP_REST_Response( [ 'message' => implode( ' · ', $mapped['errors'] ) ], 400 );
        }

        $cols = $mapped['columns'];
        $specialty = sanitize_text_field( $cols['specialty'] ?? $req->get_param( 'specialty' ) ?? 'other' );
        $areas = $cols['service_areas'] ?? $req->get_param( 'service_areas' );
        if ( is_string( $areas ) ) {
            $areas = array_values( array_filter( array_map( 'trim', preg_split( '/[،,]/u', $areas ) ?: [] ) ) );
        }
        $areas_json = is_array( $areas ) ? wp_json_encode( $areas, JSON_UNESCAPED_UNICODE ) : null;

        $extra_data = $mapped['form_data'];
        foreach ( array_keys( $cols ) as $ck ) {
            if ( $ck !== 'form_template_key' ) unset( $extra_data[ $ck ] );
        }

        $data = [
            'user_id'           => $uid,
            'specialty'         => $specialty,
            'form_template_key' => $form_key ?: null,
            'form_data'         => $extra_data ? wp_json_encode( $extra_data, JSON_UNESCAPED_UNICODE ) : null,
            'license_no'        => sanitize_text_field( $cols['license_no'] ?? $req->get_param( 'license_no' ) ?? '' ) ?: null,
            'years_experience'  => isset( $cols['years_experience'] ) ? (int) $cols['years_experience'] : ( $req->get_param( 'years_experience' ) !== null ? (int) $req->get_param( 'years_experience' ) : null ),
            'service_areas'     => $areas_json,
            'county'            => sanitize_text_field( $cols['county'] ?? $req->get_param( 'county' ) ?? '' ) ?: null,
            'city'              => sanitize_text_field( $cols['city'] ?? $req->get_param( 'city' ) ?? '' ) ?: null,
            'contact_phone'     => sanitize_text_field( $cols['contact_phone'] ?? $req->get_param( 'contact_phone' ) ?? '' ) ?: null,
            'contact_email'     => sanitize_email( $cols['contact_email'] ?? $req->get_param( 'contact_email' ) ?? '' ) ?: null,
            'bio'               => sanitize_textarea_field( $cols['bio'] ?? $req->get_param( 'bio' ) ?? '' ) ?: null,
            'credentials'       => sanitize_textarea_field( $cols['credentials'] ?? $req->get_param( 'credentials' ) ?? '' ) ?: null,
            'status'            => 'pending_approval',
            'rejection_reason'  => null,
            'approved_by'       => null,
            'approved_at'       => null,
        ];

        if ( $existing ) {
            $wpdb->update( "{$wpdb->prefix}melk_building_specialists", $data, [ 'id' => (int) $existing['id'] ] );
            $id = (int) $existing['id'];
        } else {
            $wpdb->insert( "{$wpdb->prefix}melk_building_specialists", $data );
            $id = (int) $wpdb->insert_id;
        }

        return new WP_REST_Response( [ 'data' => self::_fmt_specialist( self::_get_specialist_row( $id ) ) ], 201 );
    }

    public static function get_building_specialist( WP_REST_Request $req ): WP_REST_Response {
        $id  = (int) $req['id'];
        $row = self::_get_specialist_row( $id );
        if ( ! $row ) return new WP_REST_Response( [ 'message' => 'متخصص یافت نشد' ], 404 );
        $deny = self::_deny_specialist_access( acl_current_user_id(), $row );
        if ( $deny ) return $deny;
        return new WP_REST_Response( [ 'data' => self::_fmt_specialist( $row ) ] );
    }

    public static function update_building_specialist( WP_REST_Request $req ): WP_REST_Response {
        $id  = (int) $req['id'];
        $row = self::_get_specialist_row( $id );
        if ( ! $row ) return new WP_REST_Response( [ 'message' => 'متخصص یافت نشد' ], 404 );
        $uid = acl_current_user_id();
        if ( (int) $row['user_id'] !== $uid && ! self::_can_approve_specialists( $uid ) ) {
            return new WP_REST_Response( [ 'message' => 'دسترسی ویرایش ندارید' ], 403 );
        }
        if ( (int) $row['user_id'] === $uid && ! in_array( $row['status'], [ 'pending_approval', 'rejected' ], true ) ) {
            return new WP_REST_Response( [ 'message' => 'پروفایل تأیید‌شده فقط توسط مدیر قابل ویرایش است' ], 403 );
        }

        $upd = [];
        foreach ( [ 'specialty', 'license_no', 'county', 'city', 'contact_phone', 'contact_email', 'bio', 'credentials' ] as $f ) {
            if ( $req->get_param( $f ) !== null ) {
                $upd[ $f ] = in_array( $f, [ 'bio', 'credentials' ], true )
                    ? sanitize_textarea_field( $req->get_param( $f ) )
                    : ( $f === 'contact_email' ? sanitize_email( $req->get_param( $f ) ) : sanitize_text_field( $req->get_param( $f ) ) );
            }
        }
        if ( $req->get_param( 'years_experience' ) !== null ) {
            $upd['years_experience'] = (int) $req->get_param( 'years_experience' );
        }
        if ( $req->get_param( 'service_areas' ) !== null ) {
            $areas = $req->get_param( 'service_areas' );
            $upd['service_areas'] = is_array( $areas ) ? wp_json_encode( $areas ) : null;
        }
        if ( $upd ) {
            global $wpdb;
            $wpdb->update( "{$wpdb->prefix}melk_building_specialists", $upd, [ 'id' => $id ] );
        }
        return new WP_REST_Response( [ 'data' => self::_fmt_specialist( self::_get_specialist_row( $id ) ) ] );
    }

    public static function approve_building_specialist( WP_REST_Request $req ): WP_REST_Response {
        $id  = (int) $req['id'];
        $row = self::_get_specialist_row( $id );
        if ( ! $row ) return new WP_REST_Response( [ 'message' => 'متخصص یافت نشد' ], 404 );
        $uid = acl_current_user_id();
        if ( ! self::_can_approve_specialists( $uid ) ) {
            return new WP_REST_Response( [ 'message' => 'دسترسی تأیید ندارید' ], 403 );
        }

        global $wpdb;
        $wpdb->update( "{$wpdb->prefix}melk_building_specialists", [
            'status'           => 'approved',
            'approved_by'      => $uid,
            'approved_at'      => current_time( 'mysql' ),
            'rejection_reason' => null,
        ], [ 'id' => $id ] );

        return new WP_REST_Response( [ 'data' => self::_fmt_specialist( self::_get_specialist_row( $id ) ) ] );
    }

    /* ── مناقصه متخصصان ─────────────────────────────── */

    public static function get_specialist_tenders( WP_REST_Request $req ): WP_REST_Response {
        global $wpdb;
        $uid = acl_current_user_id();
        $pre = $wpdb->prefix;
        $w   = [ '1=1' ];
        $args = [];

        if ( self::_can_manage_tenders( $uid ) && $req->get_param( 'all' ) ) {
            if ( $req->get_param( 'status' ) ) {
                $w[]    = 't.status = %s';
                $args[] = sanitize_text_field( $req->get_param( 'status' ) );
            }
        } else {
            $w[]    = 't.status = %s';
            $args[] = 'open';
            $spec_row = self::_get_specialist_by_user( $uid );
            if ( $spec_row && $spec_row['status'] === 'approved' ) {
                $w[]    = '(t.specialty_required = %s OR t.specialty_required = %s)';
                $args[] = 'any';
                $args[] = $spec_row['specialty'];
            } elseif ( ! self::_can_manage_tenders( $uid ) ) {
                return new WP_REST_Response( [ 'data' => [] ] );
            }
        }

        $sql = "SELECT t.* FROM {$pre}melk_specialist_tenders t
                WHERE " . implode( ' AND ', $w ) . ' ORDER BY t.deadline ASC, t.created_at DESC LIMIT 100';
        $rows = $args ? $wpdb->get_results( $wpdb->prepare( $sql, ...$args ), ARRAY_A ) : $wpdb->get_results( $sql, ARRAY_A );

        $my_spec = self::_get_specialist_by_user( $uid );
        $data = [];
        foreach ( $rows ?: [] as $row ) {
            $item = self::_fmt_tender( $row );
            if ( $my_spec && $my_spec['status'] === 'approved' ) {
                $bid = $wpdb->get_row( $wpdb->prepare(
                    "SELECT * FROM {$pre}melk_specialist_bids WHERE tender_id=%d AND specialist_id=%d",
                    (int) $row['id'], (int) $my_spec['id']
                ), ARRAY_A );
                $item['my_bid'] = $bid ? self::_fmt_bid( $bid ) : null;
            }
            $data[] = $item;
        }
        return new WP_REST_Response( [ 'data' => $data ] );
    }

    public static function create_specialist_tender( WP_REST_Request $req ): WP_REST_Response {
        global $wpdb;
        $uid = acl_current_user_id();
        if ( ! self::_can_manage_tenders( $uid ) ) {
            return new WP_REST_Response( [ 'message' => 'دسترسی ایجاد مناقصه ندارید' ], 403 );
        }
        $title = sanitize_text_field( $req->get_param( 'title' ) ?? '' );
        if ( ! $title ) {
            return new WP_REST_Response( [ 'message' => 'عنوان الزامی است' ], 400 );
        }
        $key = 'TN-' . strtoupper( substr( md5( uniqid( (string) $uid, true ) ), 0, 8 ) );
        $wpdb->insert( "{$wpdb->prefix}melk_specialist_tenders", [
            'tender_key'         => $key,
            'title'              => $title,
            'description'        => sanitize_textarea_field( $req->get_param( 'description' ) ?? '' ) ?: null,
            'specialty_required' => sanitize_text_field( $req->get_param( 'specialty_required' ) ?? 'any' ),
            'project_type'       => sanitize_text_field( $req->get_param( 'project_type' ) ?? 'design' ),
            'location_county'    => sanitize_text_field( $req->get_param( 'location_county' ) ?? '' ) ?: null,
            'location_city'      => sanitize_text_field( $req->get_param( 'location_city' ) ?? '' ) ?: null,
            'budget_min'         => $req->get_param( 'budget_min' ) !== null ? (int) $req->get_param( 'budget_min' ) : null,
            'budget_max'         => $req->get_param( 'budget_max' ) !== null ? (int) $req->get_param( 'budget_max' ) : null,
            'deadline'           => sanitize_text_field( $req->get_param( 'deadline' ) ?? '' ) ?: null,
            'status'             => sanitize_text_field( $req->get_param( 'status' ) ?? 'open' ),
            'created_by'         => $uid,
            'created_by_name'    => self::_user_display_name( $uid ),
            'source_type'        => sanitize_text_field( $req->get_param( 'source_type' ) ?? '' ) ?: null,
            'source_id'          => $req->get_param( 'source_id' ) ? (int) $req->get_param( 'source_id' ) : null,
        ] );
        $id = (int) $wpdb->insert_id;
        return new WP_REST_Response( [ 'data' => self::_fmt_tender( self::_get_tender_row( $id ) ) ], 201 );
    }

    public static function get_tender_bids( WP_REST_Request $req ): WP_REST_Response {
        global $wpdb;
        $uid = acl_current_user_id();
        $tid = (int) $req['id'];
        $tender = self::_get_tender_row( $tid );
        if ( ! $tender ) return new WP_REST_Response( [ 'message' => 'مناقصه یافت نشد' ], 404 );

        if ( ! self::_can_manage_tenders( $uid ) ) {
            return new WP_REST_Response( [ 'message' => 'دسترسی مشاهده پیشنهادها ندارید' ], 403 );
        }
        $rows = $wpdb->get_results( $wpdb->prepare(
            "SELECT b.*, s.specialty, u.display_name
             FROM {$wpdb->prefix}melk_specialist_bids b
             JOIN {$wpdb->prefix}melk_building_specialists s ON s.id = b.specialist_id
             LEFT JOIN {$wpdb->users} u ON u.ID = b.user_id
             WHERE b.tender_id = %d ORDER BY b.amount ASC",
            $tid
        ), ARRAY_A );
        return new WP_REST_Response( [ 'data' => array_map( [ __CLASS__, '_fmt_bid' ], $rows ?: [] ) ] );
    }

    public static function submit_specialist_bid( WP_REST_Request $req ): WP_REST_Response {
        global $wpdb;
        $uid = acl_current_user_id();
        if ( ! self::_can_submit_bid( $uid ) ) {
            return new WP_REST_Response( [ 'message' => 'دسترسی ارسال پیشنهاد ندارید' ], 403 );
        }
        $spec = self::_get_specialist_by_user( $uid );
        if ( ! $spec || $spec['status'] !== 'approved' ) {
            return new WP_REST_Response( [ 'message' => 'فقط متخصص تأیید‌شده می‌تواند پیشنهاد بدهد' ], 403 );
        }
        $tid = (int) $req['id'];
        $tender = self::_get_tender_row( $tid );
        if ( ! $tender || $tender['status'] !== 'open' ) {
            return new WP_REST_Response( [ 'message' => 'مناقصه باز نیست' ], 400 );
        }
        if ( $tender['specialty_required'] !== 'any' && $tender['specialty_required'] !== $spec['specialty'] ) {
            return new WP_REST_Response( [ 'message' => 'تخصص شما با این مناقصه مطابقت ندارد' ], 400 );
        }
        $amount = (int) $req->get_param( 'amount' );
        if ( $amount <= 0 ) {
            return new WP_REST_Response( [ 'message' => 'مبلغ پیشنهادی الزامی است' ], 400 );
        }
        $existing = $wpdb->get_var( $wpdb->prepare(
            "SELECT id FROM {$wpdb->prefix}melk_specialist_bids WHERE tender_id=%d AND specialist_id=%d",
            $tid, (int) $spec['id']
        ) );
        $data = [
            'tender_id'      => $tid,
            'specialist_id'  => (int) $spec['id'],
            'user_id'        => $uid,
            'amount'         => $amount,
            'delivery_days'  => $req->get_param( 'delivery_days' ) !== null ? (int) $req->get_param( 'delivery_days' ) : null,
            'proposal_text'  => sanitize_textarea_field( $req->get_param( 'proposal_text' ) ?? '' ) ?: null,
            'status'         => 'submitted',
        ];
        if ( $existing ) {
            $wpdb->update( "{$wpdb->prefix}melk_specialist_bids", $data, [ 'id' => (int) $existing ] );
            $bid_id = (int) $existing;
        } else {
            $wpdb->insert( "{$wpdb->prefix}melk_specialist_bids", $data );
            $bid_id = (int) $wpdb->insert_id;
        }
        $row = $wpdb->get_row( $wpdb->prepare(
            "SELECT b.*, s.specialty, u.display_name FROM {$wpdb->prefix}melk_specialist_bids b
             JOIN {$wpdb->prefix}melk_building_specialists s ON s.id = b.specialist_id
             LEFT JOIN {$wpdb->users} u ON u.ID = b.user_id WHERE b.id=%d", $bid_id
        ), ARRAY_A );
        return new WP_REST_Response( [ 'data' => self::_fmt_bid( $row ) ], $existing ? 200 : 201 );
    }

    public static function get_my_specialist_bids( WP_REST_Request $req ): WP_REST_Response {
        global $wpdb;
        $uid = acl_current_user_id();
        $rows = $wpdb->get_results( $wpdb->prepare(
            "SELECT b.*, t.title AS tender_title, t.tender_key, t.status AS tender_status,
                    s.specialty, u.display_name
             FROM {$wpdb->prefix}melk_specialist_bids b
             JOIN {$wpdb->prefix}melk_specialist_tenders t ON t.id = b.tender_id
             JOIN {$wpdb->prefix}melk_building_specialists s ON s.id = b.specialist_id
             LEFT JOIN {$wpdb->users} u ON u.ID = b.user_id
             WHERE b.user_id = %d ORDER BY b.updated_at DESC LIMIT 100",
            $uid
        ), ARRAY_A );
        return new WP_REST_Response( [ 'data' => array_map( [ __CLASS__, '_fmt_bid' ], $rows ?: [] ) ] );
    }

    public static function reject_building_specialist( WP_REST_Request $req ): WP_REST_Response {
        $id  = (int) $req['id'];
        $row = self::_get_specialist_row( $id );
        if ( ! $row ) return new WP_REST_Response( [ 'message' => 'متخصص یافت نشد' ], 404 );
        $uid = acl_current_user_id();
        if ( ! self::_can_approve_specialists( $uid ) ) {
            return new WP_REST_Response( [ 'message' => 'دسترسی رد ندارید' ], 403 );
        }

        global $wpdb;
        $wpdb->update( "{$wpdb->prefix}melk_building_specialists", [
            'status'           => 'rejected',
            'rejection_reason' => sanitize_textarea_field( $req->get_param( 'reason' ) ?? '' ) ?: null,
            'approved_by'      => $uid,
            'approved_at'      => current_time( 'mysql' ),
        ], [ 'id' => $id ] );

        return new WP_REST_Response( [ 'data' => self::_fmt_specialist( self::_get_specialist_row( $id ) ) ] );
    }

    private static function _can_manage_county( int $uid ): bool {
        return acl_user_is_super_admin( $uid )
            || acl_user_can( $uid, 'melk.manage' )
            || acl_user_can( $uid, 'melk.county.manage' );
    }

    private static function _can_submit_county( int $uid ): bool {
        return self::_can_manage_county( $uid )
            || acl_user_can( $uid, 'melk.county.submit' );
    }

    private static function _can_approve_specialists( int $uid ): bool {
        return acl_user_is_super_admin( $uid )
            || acl_user_can( $uid, 'melk.manage' )
            || acl_user_can( $uid, 'melk.specialists.approve' );
    }

    private static function _can_register_specialist( int $uid ): bool {
        return self::_can_approve_specialists( $uid )
            || acl_user_can( $uid, 'melk.specialists.register' );
    }

    private static function _can_browse_specialist_directory( int $uid ): bool {
        if ( self::_can_approve_specialists( $uid ) ) return true;
        if ( ! acl_user_can( $uid, 'melk.specialists.view' ) ) return false;
        $spec = self::_get_specialist_by_user( $uid );
        if ( $spec && $spec['status'] === 'approved' ) return false;
        return true;
    }

    private static function _can_manage_tenders( int $uid ): bool {
        return acl_user_is_super_admin( $uid )
            || acl_user_can( $uid, 'melk.manage' )
            || acl_user_can( $uid, 'melk.specialists.tender' )
            || acl_user_can( $uid, 'melk.county.manage' );
    }

    private static function _can_submit_bid( int $uid ): bool {
        return self::_can_approve_specialists( $uid )
            || acl_user_can( $uid, 'melk.specialists.bid' );
    }

    private static function _user_display_name( int $uid ): string {
        global $wpdb;
        return (string) $wpdb->get_var( $wpdb->prepare(
            "SELECT display_name FROM {$wpdb->users} WHERE ID = %d", $uid
        ) );
    }

    private static function _get_tender_row( int $id ): ?array {
        global $wpdb;
        return $wpdb->get_row( $wpdb->prepare(
            "SELECT * FROM {$wpdb->prefix}melk_specialist_tenders WHERE id = %d", $id
        ), ARRAY_A ) ?: null;
    }

    private static function _fmt_tender( ?array $r ): ?array {
        if ( ! $r ) return null;
        $specs = [
            'any' => 'همه تخصص‌ها', 'architect' => 'معمار', 'structural' => 'سازه',
            'mep' => 'تاسیسات', 'civil' => 'عمران', 'interior' => 'طراحی داخلی',
            'supervisor' => 'ناظر', 'surveyor' => 'نقشه‌بردار', 'other' => 'سایر',
        ];
        $types = [
            'design' => 'طراحی', 'supervision' => 'نظارت', 'consultation' => 'مشاوره',
            'renovation' => 'نوسازی', 'appraisal' => 'کارشناسی', 'other' => 'سایر',
        ];
        $statuses = [
            'draft' => 'پیش‌نویس', 'open' => 'باز', 'closed' => 'بسته',
            'awarded' => 'واگذار‌شده', 'cancelled' => 'لغو',
        ];
        return [
            'id'                  => (int) $r['id'],
            'tender_key'          => $r['tender_key'],
            'title'               => $r['title'],
            'description'         => $r['description'],
            'specialty_required'  => $r['specialty_required'],
            'specialty_label'     => $specs[ $r['specialty_required'] ] ?? $r['specialty_required'],
            'project_type'        => $r['project_type'],
            'project_type_label'  => $types[ $r['project_type'] ] ?? $r['project_type'],
            'location_county'     => $r['location_county'],
            'location_city'       => $r['location_city'],
            'budget_min'          => $r['budget_min'] !== null ? (int) $r['budget_min'] : null,
            'budget_max'          => $r['budget_max'] !== null ? (int) $r['budget_max'] : null,
            'deadline'            => $r['deadline'],
            'status'              => $r['status'],
            'status_label'        => $statuses[ $r['status'] ] ?? $r['status'],
            'created_by'          => (int) $r['created_by'],
            'created_by_name'     => $r['created_by_name'],
            'created_at'          => $r['created_at'],
        ];
    }

    private static function _fmt_bid( ?array $r ): ?array {
        if ( ! $r ) return null;
        $statuses = [
            'submitted' => 'ارسال‌شده', 'shortlisted' => 'منتخب',
            'accepted' => 'پذیرفته', 'rejected' => 'رد‌شده', 'withdrawn' => 'انصراف',
        ];
        $specs = [
            'architect' => 'معمار', 'structural' => 'سازه', 'mep' => 'تاسیسات',
            'civil' => 'عمران', 'interior' => 'طراحی داخلی', 'supervisor' => 'ناظر',
            'surveyor' => 'نقشه‌بردار', 'other' => 'سایر',
        ];
        return [
            'id'             => (int) $r['id'],
            'tender_id'      => (int) $r['tender_id'],
            'tender_title'   => $r['tender_title'] ?? null,
            'tender_key'     => $r['tender_key'] ?? null,
            'tender_status'  => $r['tender_status'] ?? null,
            'specialist_id'  => (int) $r['specialist_id'],
            'user_id'        => (int) $r['user_id'],
            'display_name'   => $r['display_name'] ?? '',
            'specialty'      => $r['specialty'] ?? null,
            'specialty_label'=> isset( $r['specialty'] ) ? ( $specs[ $r['specialty'] ] ?? $r['specialty'] ) : null,
            'amount'         => (int) $r['amount'],
            'delivery_days'  => $r['delivery_days'] !== null ? (int) $r['delivery_days'] : null,
            'proposal_text'  => $r['proposal_text'],
            'status'         => $r['status'],
            'status_label'   => $statuses[ $r['status'] ] ?? $r['status'],
            'created_at'     => $r['created_at'],
            'updated_at'     => $r['updated_at'],
        ];
    }

    private static function _get_county_request_row( int $id ): ?array {
        global $wpdb;
        return $wpdb->get_row( $wpdb->prepare(
            "SELECT * FROM {$wpdb->prefix}melk_county_requests WHERE id = %d", $id
        ), ARRAY_A ) ?: null;
    }

    private static function _get_specialist_row( int $id ): ?array {
        global $wpdb;
        return $wpdb->get_row( $wpdb->prepare(
            "SELECT s.*, u.display_name, u.user_login
             FROM {$wpdb->prefix}melk_building_specialists s
             LEFT JOIN {$wpdb->users} u ON u.ID = s.user_id
             WHERE s.id = %d", $id
        ), ARRAY_A ) ?: null;
    }

    private static function _get_specialist_by_user( int $uid ): ?array {
        global $wpdb;
        return $wpdb->get_row( $wpdb->prepare(
            "SELECT s.*, u.display_name, u.user_login
             FROM {$wpdb->prefix}melk_building_specialists s
             LEFT JOIN {$wpdb->users} u ON u.ID = s.user_id
             WHERE s.user_id = %d", $uid
        ), ARRAY_A ) ?: null;
    }

    private static function _fmt_county_request( ?array $r ): ?array {
        if ( ! $r ) return null;
        $types = [
            'property_buy' => 'خرید ملک', 'property_search' => 'جستجوی ملک',
            'partnership' => 'مشارکت', 'legal_issue' => 'مسئله حقوقی',
            'valuation' => 'کارشناسی', 'problem' => 'مشکل/چالش',
            'demand' => 'تقاضا', 'other' => 'سایر',
        ];
        $statuses = [
            'draft' => 'پیش‌نویس', 'submitted' => 'ارسال‌شده',
            'under_review' => 'در حال بررسی', 'in_progress' => 'در دست اقدام',
            'resolved' => 'حل‌شده', 'closed' => 'بسته',
        ];
        $priorities = [ 'low' => 'کم', 'normal' => 'معمولی', 'high' => 'بالا', 'urgent' => 'فوری' ];
        $form_key  = $r['form_template_key'] ?? null;
        $form_data = json_decode( $r['form_data'] ?? '{}', true ) ?: [];
        $form_meta = null;
        $attachment = [];
        if ( $form_key ) {
            require_once ACC_PATH . 'includes/helpers/melk-county-forms.php';
            $form_meta  = Melk_County_Forms::template_meta( $form_key );
            $attachment = Melk_County_Forms::attachment_fields( $form_key, $form_data, $r );
        }
        return [
            'id'              => (int) $r['id'],
            'request_key'     => $r['request_key'],
            'request_type'    => $r['request_type'],
            'request_type_label' => $types[ $r['request_type'] ] ?? $r['request_type'],
            'form_template_key' => $form_key,
            'form_template_title' => $form_meta['title'] ?? null,
            'form_template_icon' => $form_meta['icon'] ?? null,
            'form_template_standard' => $form_meta['standard'] ?? null,
            'form_data'       => $form_data,
            'form_attachment' => $attachment,
            'title'           => $r['title'],
            'description'     => $r['description'],
            'priority'        => $r['priority'],
            'priority_label'  => $priorities[ $r['priority'] ] ?? $r['priority'],
            'status'          => $r['status'],
            'status_label'    => $statuses[ $r['status'] ] ?? $r['status'],
            'county'          => $r['county'],
            'city'            => $r['city'],
            'agency_name'     => $r['agency_name'],
            'contact_phone'   => $r['contact_phone'],
            'contact_email'   => $r['contact_email'],
            'assigned_to'     => $r['assigned_to'] ? (int) $r['assigned_to'] : null,
            'assigned_name'   => $r['assigned_name'],
            'admin_note'      => $r['admin_note'],
            'resolved_at'     => $r['resolved_at'],
            'created_by'      => (int) $r['created_by'],
            'created_by_name' => $r['created_by_name'],
            'reviewed_by'     => $r['reviewed_by'] ? (int) $r['reviewed_by'] : null,
            'reviewed_at'     => $r['reviewed_at'],
            'created_at'      => $r['created_at'],
            'updated_at'      => $r['updated_at'],
        ];
    }

    private static function _fmt_specialist( ?array $r ): ?array {
        if ( ! $r ) return null;
        $specs = [
            'architect' => 'معمار', 'structural' => 'کارشناس طراحی و سازه',
            'mep' => 'کارشناس تاسیسات', 'civil' => 'مهندس عمران',
            'interior' => 'طراح داخلی', 'supervisor' => 'ناظر ساختمان',
            'surveyor' => 'نقشه‌بردار', 'quantity_surveyor' => 'متره و برآورد', 'other' => 'سایر',
        ];
        $statuses = [
            'pending_approval' => 'در انتظار تأیید', 'approved' => 'تأیید‌شده',
            'rejected' => 'رد‌شده', 'suspended' => 'تعلیق',
        ];
        $form_key  = $r['form_template_key'] ?? null;
        $form_data = json_decode( $r['form_data'] ?? '{}', true ) ?: [];
        $form_meta = null;
        $attachment = [];
        if ( $form_key ) {
            require_once ACC_PATH . 'includes/helpers/melk-specialist-forms.php';
            $form_meta  = Melk_Specialist_Forms::template_meta( $form_key );
            $attachment = Melk_Specialist_Forms::attachment_fields( $form_key, $form_data, $r );
        }
        return [
            'id'                => (int) $r['id'],
            'user_id'           => (int) $r['user_id'],
            'display_name'      => $r['display_name'] ?: $r['user_login'] ?: '',
            'specialty'         => $r['specialty'],
            'specialty_label'   => $specs[ $r['specialty'] ] ?? $r['specialty'],
            'form_template_key' => $form_key,
            'form_template_title'=> $form_meta['title'] ?? null,
            'form_template_icon'=> $form_meta['icon'] ?? null,
            'form_template_standard'=> $form_meta['standard'] ?? null,
            'form_data'         => $form_data,
            'form_attachment'   => $attachment,
            'license_no'        => $r['license_no'],
            'years_experience'  => $r['years_experience'] !== null ? (int) $r['years_experience'] : null,
            'service_areas'     => json_decode( $r['service_areas'] ?? '[]', true ) ?: [],
            'county'            => $r['county'],
            'city'              => $r['city'],
            'contact_phone'     => $r['contact_phone'],
            'contact_email'     => $r['contact_email'],
            'bio'               => $r['bio'],
            'credentials'       => $r['credentials'],
            'status'            => $r['status'],
            'status_label'      => $statuses[ $r['status'] ] ?? $r['status'],
            'approved_by'       => $r['approved_by'] ? (int) $r['approved_by'] : null,
            'approved_at'       => $r['approved_at'],
            'rejection_reason'  => $r['rejection_reason'],
            'created_at'        => $r['created_at'],
            'updated_at'        => $r['updated_at'],
        ];
    }

    private static function _deny_county_access( int $uid, array $row ): ?WP_REST_Response {
        if ( self::_can_manage_county( $uid ) ) return null;
        if ( (int) $row['created_by'] === $uid ) return null;
        return new WP_REST_Response( [ 'message' => 'دسترسی به درخواست مجاز نیست' ], 403 );
    }

    private static function _deny_specialist_access( int $uid, array $row ): ?WP_REST_Response {
        if ( self::_can_approve_specialists( $uid ) ) return null;
        if ( (int) $row['user_id'] === $uid ) return null;
        if ( self::_can_browse_specialist_directory( $uid ) && $row['status'] === 'approved' ) return null;
        return new WP_REST_Response( [ 'message' => 'دسترسی به پروفایل مجاز نیست' ], 403 );
    }
}
