<?php
/**
 * svc-rewards.php — موتور پاداش HVAC (HVAC Reward Engine)
 * امتیاز = پایه × ضریب سطح × ضریب ارزش سفارش × ضریب کیفیت
 */
if ( ! defined( 'ABSPATH' ) ) exit;

class Svc_Rewards {

    public const CREDIT_LABEL = 'HVAC Credit';

    /** @var array<string,array<string,array{base:int|float,label:string,type?:string}>> */
    public static function activity_catalog(): array {
        return [
            'customer' => [
                'register'           => [ 'base' => 50,  'label' => 'ثبت‌نام' ],
                'profile_complete'   => [ 'base' => 30,  'label' => 'تکمیل اطلاعات تجهیزات' ],
                'first_request'      => [ 'base' => 100, 'label' => 'ثبت اولین درخواست' ],
                'parts_purchase'     => [ 'base' => 0.01, 'label' => 'خرید قطعه', 'type' => 'percent' ],
                'equipment_purchase' => [ 'base' => 0.02, 'label' => 'خرید تجهیزات', 'type' => 'percent' ],
                'pm_annual'          => [ 'base' => 500, 'label' => 'قرارداد PM سالانه' ],
                'review'             => [ 'base' => 20,  'label' => 'ثبت نظر' ],
                'referral'           => [ 'base' => 200, 'label' => 'معرفی مشتری جدید' ],
                'online_payment'     => [ 'base' => 30,  'label' => 'پرداخت آنلاین' ],
                'performance_report' => [ 'base' => 20,  'label' => 'بارگذاری گزارش عملکرد' ],
                'welcome_bonus'      => [ 'base' => 100, 'label' => 'امتیاز خوش‌آمدگویی (معرفی‌شده)' ],
            ],
            'technician' => [
                'mission_done'       => [ 'base' => 100, 'label' => 'انجام مأموریت' ],
                'high_satisfaction'  => [ 'base' => 50,  'label' => 'رضایت بالای مشتری' ],
                'no_revisit'         => [ 'base' => 200, 'label' => 'تعمیر بدون مراجعه مجدد' ],
                'full_report'        => [ 'base' => 30,  'label' => 'ثبت کامل گزارش' ],
                'photos_uploaded'    => [ 'base' => 20,  'label' => 'بارگذاری تصاویر' ],
                'pm_sold'            => [ 'base' => 300, 'label' => 'فروش قرارداد PM' ],
                'parts_sold'         => [ 'base' => 0.02, 'label' => 'فروش قطعات', 'type' => 'percent' ],
                'equipment_sold'     => [ 'base' => 0.03, 'label' => 'فروش دستگاه', 'type' => 'percent' ],
                'fast_response'      => [ 'base' => 20,  'label' => 'پاسخ سریع' ],
                'training'           => [ 'base' => 50,  'label' => 'آموزش فنی' ],
            ],
            'supplier' => [
                'on_time_delivery' => [ 'base' => 50,  'label' => 'تحویل به‌موقع' ],
                'high_quality'     => [ 'base' => 100, 'label' => 'کیفیت بالا' ],
                'competitive_price'  => [ 'base' => 50,  'label' => 'قیمت رقابتی' ],
                'fast_shipping'      => [ 'base' => 80,  'label' => 'ارسال فوری' ],
                'valid_warranty'     => [ 'base' => 70,  'label' => 'گارانتی معتبر' ],
                'stable_stock'       => [ 'base' => 30,  'label' => 'موجودی پایدار' ],
                'fast_quote'         => [ 'base' => 20,  'label' => 'پاسخ سریع استعلام' ],
            ],
        ];
    }

    public static function tier_config(): array {
        return [
            'customer' => [
                'normal' => [ 'label' => 'عادی',   'mult' => 1.0,  'min_points' => 0 ],
                'silver' => [ 'label' => 'نقره‌ای', 'mult' => 1.2,  'min_points' => 500 ],
                'gold'   => [ 'label' => 'طلایی',  'mult' => 1.5,  'min_points' => 2000 ],
                'vip'    => [ 'label' => 'VIP',    'mult' => 2.0,  'min_points' => 5000 ],
            ],
            'technician' => [
                'junior'   => [ 'label' => 'Junior (برنز)',   'mult' => 1.0,  'min_points' => 0,    'rank' => 1, 'alias' => 'bronze' ],
                'expert'   => [ 'label' => 'Expert (نقره)',  'mult' => 1.15, 'min_points' => 800,  'rank' => 2, 'alias' => 'silver' ],
                'senior'   => [ 'label' => 'Senior (طلا)',   'mult' => 1.3,  'min_points' => 2500, 'rank' => 3, 'alias' => 'gold' ],
                'master'   => [ 'label' => 'Master (پلاتین)', 'mult' => 1.5, 'min_points' => 6000, 'rank' => 4, 'alias' => 'platinum' ],
                'elite'    => [ 'label' => 'Elite (الماس)',  'mult' => 1.8,  'min_points' => 12000,'rank' => 5, 'alias' => 'diamond' ],
            ],
            'supplier' => [
                'regular'   => [ 'label' => 'معمولی',   'mult' => 1.0, 'min_points' => 0 ],
                'selected'  => [ 'label' => 'منتخب',    'mult' => 1.2, 'min_points' => 600 ],
                'premium'   => [ 'label' => 'ممتاز',    'mult' => 1.45,'min_points' => 2000 ],
                'strategic' => [ 'label' => 'استراتژیک','mult' => 1.7, 'min_points' => 5000 ],
            ],
        ];
    }

    public static function redeem_catalog(): array {
        return [
            [ 'key' => 'service_discount',   'label' => 'تخفیف خدمات',           'cost' => 200,  'actor' => 'customer' ],
            [ 'key' => 'parts_discount',     'label' => 'تخفیف خرید قطعات',      'cost' => 150,  'actor' => 'customer' ],
            [ 'key' => 'pm_extend',          'label' => 'تمدید قرارداد PM',      'cost' => 400,  'actor' => 'customer' ],
            [ 'key' => 'free_consult',       'label' => 'مشاوره رایگان',         'cost' => 100,  'actor' => 'customer' ],
            [ 'key' => 'priority_dispatch',  'label' => 'اولویت اعزام تکنسین',   'cost' => 250,  'actor' => 'customer' ],
            [ 'key' => 'free_shipping',      'label' => 'ارسال رایگان قطعات',    'cost' => 180,  'actor' => 'customer' ],
            [ 'key' => 'warranty_extend',    'label' => 'افزایش گارانتی خدمات',  'cost' => 350,  'actor' => 'customer' ],
            [ 'key' => 'cash_bonus',         'label' => 'پاداش نقدی',            'cost' => 1000, 'actor' => 'technician' ],
            [ 'key' => 'free_training',      'label' => 'دوره آموزشی رایگان',    'cost' => 500,  'actor' => 'technician' ],
            [ 'key' => 'top_search',         'label' => 'نمایش اول جستجو',       'cost' => 300,  'actor' => 'supplier' ],
            [ 'key' => 'premium_badge',      'label' => 'نشان تأمین‌کننده ممتاز', 'cost' => 600,  'actor' => 'supplier' ],
        ];
    }

    public static function normalize_phone( string $phone ): string {
        $p = preg_replace( '/\D+/', '', $phone );
        if ( str_starts_with( $p, '98' ) && strlen( $p ) === 12 ) $p = '0' . substr( $p, 2 );
        return $p;
    }

    public static function get_or_create_account( string $actor_type, array $data ): array {
        global $wpdb;
        $pre = $wpdb->prefix;
        $actor_type = in_array( $actor_type, [ 'customer', 'technician', 'supplier' ], true ) ? $actor_type : 'customer';

        $user_id = (int) ( $data['user_id'] ?? 0 );
        $phone   = self::normalize_phone( (string) ( $data['phone'] ?? '' ) );

        if ( $user_id ) {
            $row = $wpdb->get_row( $wpdb->prepare(
                "SELECT * FROM {$pre}svc_reward_accounts WHERE actor_type=%s AND user_id=%d LIMIT 1",
                $actor_type, $user_id
            ), ARRAY_A );
            if ( $row ) return $row;
        }
        if ( $phone ) {
            $row = $wpdb->get_row( $wpdb->prepare(
                "SELECT * FROM {$pre}svc_reward_accounts WHERE actor_type=%s AND phone=%s LIMIT 1",
                $actor_type, $phone
            ), ARRAY_A );
            if ( $row ) return $row;
        }

        $name = sanitize_text_field( $data['display_name'] ?? $data['name'] ?? '' );
        if ( ! $name && $user_id ) {
            $name = (string) $wpdb->get_var( $wpdb->prepare(
                "SELECT display_name FROM {$wpdb->users} WHERE ID=%d", $user_id
            ) );
        }

        $ref_code = strtoupper( substr( md5( uniqid( (string) $user_id . $phone, true ) ), 0, 8 ) );
        $default_tier = $actor_type === 'technician' ? 'junior' : ( $actor_type === 'supplier' ? 'regular' : 'normal' );

        $wpdb->insert( "{$pre}svc_reward_accounts", [
            'actor_type'       => $actor_type,
            'user_id'          => $user_id ?: null,
            'phone'            => $phone ?: null,
            'display_name'     => $name,
            'tier'             => $default_tier,
            'points_total'     => 0,
            'points_available' => 0,
            'hvac_credits'     => 0,
            'lifetime_earned'  => 0,
            'rank_score'       => 0,
            'referral_code'    => $ref_code,
            'referred_by'      => (int) ( $data['referred_by'] ?? 0 ) ?: null,
        ] );
        $id = (int) $wpdb->insert_id;
        return $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$pre}svc_reward_accounts WHERE id=%d", $id ), ARRAY_A ) ?: [];
    }

    public static function calc_points( string $actor_type, string $event_key, array $opts = [] ): int {
        $cat = self::activity_catalog()[ $actor_type ][ $event_key ] ?? null;
        if ( ! $cat ) return 0;

        $base = (float) $cat['base'];
        $order_value = (float) ( $opts['order_value'] ?? 0 );
        if ( ( $cat['type'] ?? '' ) === 'percent' && $order_value > 0 ) {
            $base = $order_value * $base;
        }

        $tier = (string) ( $opts['tier'] ?? 'normal' );
        $tiers = self::tier_config()[ $actor_type ] ?? [];
        $tier_mult = (float) ( $tiers[ $tier ]['mult'] ?? 1.0 );

        $value_mult = 1.0;
        if ( $order_value > 0 ) {
            if ( $order_value >= 50000000 ) $value_mult = 1.5;
            elseif ( $order_value >= 20000000 ) $value_mult = 1.3;
            elseif ( $order_value >= 5000000 ) $value_mult = 1.15;
        }

        $quality_mult = max( 0.5, min( 2.0, (float) ( $opts['quality_mult'] ?? 1.0 ) ) );

        return (int) round( $base * $tier_mult * $value_mult * $quality_mult );
    }

    public static function earn( int $account_id, string $event_key, array $opts = [] ): ?array {
        global $wpdb;
        if ( $account_id <= 0 ) return null;

        $account = $wpdb->get_row( $wpdb->prepare(
            "SELECT * FROM {$wpdb->prefix}svc_reward_accounts WHERE id=%d", $account_id
        ), ARRAY_A );
        if ( ! $account ) return null;

        $actor = $account['actor_type'];
        $opts['tier'] = $account['tier'];
        $points = self::calc_points( $actor, $event_key, $opts );
        if ( $points <= 0 ) return null;

        $dedupe = $opts['dedupe_key'] ?? '';
        if ( $dedupe ) {
            $exists = (int) $wpdb->get_var( $wpdb->prepare(
                "SELECT COUNT(*) FROM {$wpdb->prefix}svc_reward_ledger
                 WHERE account_id=%d AND event_key=%s AND dedupe_key=%s",
                $account_id, $event_key, $dedupe
            ) );
            if ( $exists ) return null;
        }

        $credits = $points;
        $wpdb->insert( "{$wpdb->prefix}svc_reward_ledger", [
            'account_id'   => $account_id,
            'direction'    => 'earn',
            'event_key'    => $event_key,
            'points'       => $points,
            'credits'      => $credits,
            'order_value'  => (float) ( $opts['order_value'] ?? 0 ),
            'tier_mult'    => (float) ( self::tier_config()[ $actor ][ $account['tier'] ]['mult'] ?? 1 ),
            'quality_mult' => (float) ( $opts['quality_mult'] ?? 1 ),
            'ref_type'     => sanitize_text_field( $opts['ref_type'] ?? '' ),
            'ref_id'       => (int) ( $opts['ref_id'] ?? 0 ) ?: null,
            'dedupe_key'   => $dedupe,
            'note'         => sanitize_text_field( $opts['note'] ?? '' ),
        ] );

        $wpdb->query( $wpdb->prepare(
            "UPDATE {$wpdb->prefix}svc_reward_accounts SET
                points_total = points_total + %d,
                points_available = points_available + %d,
                hvac_credits = hvac_credits + %d,
                lifetime_earned = lifetime_earned + %d,
                rank_score = rank_score + %d,
                updated_at = %s
             WHERE id = %d",
            $points, $points, $credits, $points, $points, current_time( 'mysql' ), $account_id
        ) );

        self::recalc_tier( $account_id );
        self::maybe_award_badge( $account_id, $event_key );

        return [ 'account_id' => $account_id, 'event_key' => $event_key, 'points' => $points, 'credits' => $credits ];
    }

    public static function redeem( int $account_id, string $redeem_key ): array {
        global $wpdb;
        $catalog = array_column( self::redeem_catalog(), null, 'key' );
        $item = $catalog[ $redeem_key ] ?? null;
        if ( ! $item ) return [ 'success' => false, 'message' => 'جایزه نامعتبر' ];

        $account = $wpdb->get_row( $wpdb->prepare(
            "SELECT * FROM {$wpdb->prefix}svc_reward_accounts WHERE id=%d", $account_id
        ), ARRAY_A );
        if ( ! $account ) return [ 'success' => false, 'message' => 'حساب یافت نشد' ];
        if ( $item['actor'] !== $account['actor_type'] ) {
            return [ 'success' => false, 'message' => 'این جایزه برای نقش شما نیست' ];
        }
        $cost = (int) $item['cost'];
        if ( (int) $account['points_available'] < $cost ) {
            return [ 'success' => false, 'message' => 'امتیاز کافی نیست' ];
        }

        $wpdb->insert( "{$wpdb->prefix}svc_reward_ledger", [
            'account_id' => $account_id,
            'direction'  => 'redeem',
            'event_key'  => $redeem_key,
            'points'     => -$cost,
            'credits'    => -$cost,
            'note'       => $item['label'],
        ] );
        $wpdb->query( $wpdb->prepare(
            "UPDATE {$wpdb->prefix}svc_reward_accounts SET
                points_available = points_available - %d,
                hvac_credits = GREATEST(0, hvac_credits - %d),
                updated_at = %s WHERE id = %d",
            $cost, $cost, current_time( 'mysql' ), $account_id
        ) );

        self::apply_redeem_benefit( $account_id, $redeem_key );

        return [ 'success' => true, 'redeem_key' => $redeem_key, 'cost' => $cost, 'label' => $item['label'] ];
    }

    public static function recalc_tier( int $account_id ): void {
        global $wpdb;
        $account = $wpdb->get_row( $wpdb->prepare(
            "SELECT * FROM {$wpdb->prefix}svc_reward_accounts WHERE id=%d", $account_id
        ), ARRAY_A );
        if ( ! $account ) return;

        $account['tier'] = self::normalize_tier( $account['actor_type'], $account['tier'] );
        if ( $account['tier'] !== ( $wpdb->get_var( $wpdb->prepare(
            "SELECT tier FROM {$wpdb->prefix}svc_reward_accounts WHERE id=%d", $account_id
        ) ) ) ) {
            $wpdb->update( "{$wpdb->prefix}svc_reward_accounts", [
                'tier' => $account['tier'], 'updated_at' => current_time( 'mysql' ),
            ], [ 'id' => $account_id ] );
        }

        $tiers = self::tier_config()[ $account['actor_type'] ] ?? [];
        $lifetime = (int) $account['lifetime_earned'];
        $new_tier = $account['tier'];
        foreach ( $tiers as $key => $cfg ) {
            if ( $lifetime >= (int) $cfg['min_points'] ) $new_tier = $key;
        }
        if ( $new_tier !== $account['tier'] ) {
            $wpdb->update( "{$wpdb->prefix}svc_reward_accounts", [
                'tier' => $new_tier, 'updated_at' => current_time( 'mysql' ),
            ], [ 'id' => $account_id ] );
        }
    }

    public static function normalize_tier( string $actor_type, string $tier ): string {
        $legacy = [
            'bronze' => 'junior', 'silver' => 'expert', 'gold' => 'senior',
            'platinum' => 'master', 'diamond' => 'elite',
        ];
        if ( isset( $legacy[ $tier ] ) && $actor_type === 'technician' ) {
            return $legacy[ $tier ];
        }
        $valid = array_keys( self::tier_config()[ $actor_type ] ?? [] );
        return in_array( $tier, $valid, true ) ? $tier : ( $valid[0] ?? $tier );
    }

    public static function migrate_legacy_tiers(): int {
        global $wpdb;
        $map = [
            'bronze' => 'junior', 'silver' => 'expert', 'gold' => 'senior',
            'platinum' => 'master', 'diamond' => 'elite',
        ];
        $updated = 0;
        foreach ( $map as $old => $new ) {
            $updated += (int) $wpdb->query( $wpdb->prepare(
                "UPDATE {$wpdb->prefix}svc_reward_accounts SET tier=%s, updated_at=%s
                 WHERE actor_type='technician' AND tier=%s",
                $new, current_time( 'mysql' ), $old
            ) );
        }
        return $updated;
    }

    public static function consume_benefit( string $phone, string $benefit_key ): bool {
        global $wpdb;
        $phone = self::normalize_phone( $phone );
        $row = $wpdb->get_row( $wpdb->prepare(
            "SELECT id, active_benefits FROM {$wpdb->prefix}svc_reward_accounts
             WHERE actor_type='customer' AND phone=%s",
            $phone
        ), ARRAY_A );
        if ( ! $row ) return false;

        $benefits = self::get_active_benefits( $row );
        $kept = [];
        $consumed = false;
        foreach ( $benefits as $b ) {
            if ( ! $consumed && ( $b['key'] ?? '' ) === $benefit_key ) {
                $consumed = true;
                continue;
            }
            $kept[] = $b;
        }
        if ( ! $consumed ) return false;

        $wpdb->update( "{$wpdb->prefix}svc_reward_accounts", [
            'active_benefits' => wp_json_encode( $kept, JSON_UNESCAPED_UNICODE ),
            'updated_at'      => current_time( 'mysql' ),
        ], [ 'id' => (int) $row['id'] ] );
        return true;
    }

    /** @return array<string,float> */
    public static function apply_invoice_discounts( string $phone, array &$data ): array {
        $phone = self::normalize_phone( $phone );
        if ( ! $phone ) return [];

        $applied = [];
        $discount = (float) ( $data['discount'] ?? 0 );
        $services = (float) ( $data['subtotal_services'] ?? 0 );
        $parts    = (float) ( $data['subtotal_parts'] ?? 0 );

        if ( self::has_benefit( $phone, 'service_discount' ) && $services > 0 ) {
            $extra = round( $services * 0.10 );
            $discount += $extra;
            $applied['service_discount'] = $extra;
            self::consume_benefit( $phone, 'service_discount' );
        }
        if ( self::has_benefit( $phone, 'parts_discount' ) && $parts > 0 ) {
            $extra = round( $parts * 0.05 );
            $discount += $extra;
            $applied['parts_discount'] = $extra;
            self::consume_benefit( $phone, 'parts_discount' );
        }

        $data['discount'] = $discount;
        return $applied;
    }

    public static function maybe_award_badge( int $account_id, string $event_key ): void {
        global $wpdb;
        $badges = [
            'first_request' => 'first_case',
            'pm_annual'       => 'pm_member',
            'referral'        => 'referrer',
            'mission_done'    => 'field_hero',
            'high_satisfaction' => 'five_star',
            'on_time_delivery' => 'reliable_supplier',
        ];
        $badge = $badges[ $event_key ] ?? '';
        if ( ! $badge ) return;
        $wpdb->query( $wpdb->prepare(
            "INSERT IGNORE INTO {$wpdb->prefix}svc_reward_badges (account_id, badge_key) VALUES (%d, %s)",
            $account_id, $badge
        ) );
    }

    public static function process_referral( int $referrer_id, int $new_account_id ): void {
        self::earn( $referrer_id, 'referral', [ 'ref_type' => 'account', 'ref_id' => $new_account_id, 'dedupe_key' => 'ref_' . $new_account_id ] );
        $acct = $GLOBALS['wpdb']->get_row( $GLOBALS['wpdb']->prepare(
            "SELECT actor_type FROM {$GLOBALS['wpdb']->prefix}svc_reward_accounts WHERE id=%d", $new_account_id
        ), ARRAY_A );
        if ( $acct ) {
            self::earn( $new_account_id, 'welcome_bonus', [ 'dedupe_key' => 'welcome', 'note' => 'پاداش معرفی' ] );
        }
    }

    public static function dashboard( int $account_id ): array {
        global $wpdb;
        $pre = $wpdb->prefix;
        $account = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$pre}svc_reward_accounts WHERE id=%d", $account_id ), ARRAY_A );
        if ( ! $account ) return [];

        $ledger = $wpdb->get_results( $wpdb->prepare(
            "SELECT * FROM {$pre}svc_reward_ledger WHERE account_id=%d ORDER BY created_at DESC LIMIT 50",
            $account_id
        ), ARRAY_A ) ?: [];

        $badges = $wpdb->get_results( $wpdb->prepare(
            "SELECT badge_key, earned_at FROM {$pre}svc_reward_badges WHERE account_id=%d", $account_id
        ), ARRAY_A ) ?: [];

        $actor = $account['actor_type'];
        $tiers = self::tier_config()[ $actor ] ?? [];
        $tier_cfg = $tiers[ $account['tier'] ] ?? [];
        $next_tier = null;
        foreach ( $tiers as $k => $cfg ) {
            if ( (int) $cfg['min_points'] > (int) $account['lifetime_earned'] ) {
                $next_tier = [ 'key' => $k, 'label' => $cfg['label'], 'need' => (int) $cfg['min_points'] - (int) $account['lifetime_earned'] ];
                break;
            }
        }

        $rank = 0;
        if ( $actor === 'technician' ) {
            $rank = 1 + (int) $wpdb->get_var( $wpdb->prepare(
                "SELECT COUNT(*) FROM {$pre}svc_reward_accounts WHERE actor_type='technician' AND rank_score > %d",
                (int) $account['rank_score']
            ) );
        }

        $redeem = array_values( array_filter( self::redeem_catalog(), fn( $r ) => $r['actor'] === $actor ) );

        return [
            'account'      => $account,
            'tier_label'   => $tier_cfg['label'] ?? $account['tier'],
            'tier_rank'    => $tier_cfg['rank'] ?? null,
            'rank_position'=> $rank ?: null,
            'next_tier'    => $next_tier,
            'ledger'       => $ledger,
            'badges'       => $badges,
            'redeem_catalog' => $redeem,
            'activities'   => self::activity_catalog()[ $actor ] ?? [],
            'credit_label' => self::CREDIT_LABEL,
            'ai_suggestions' => self::ai_suggestions( $account ),
            'active_benefits' => self::get_active_benefits( $account ),
        ];
    }

    /** هوک‌های یکپارچه با SR_Controller */
    public static function on_visitor_request( array $request_data, int $request_id ): void {
        $phone = self::normalize_phone( $request_data['phone'] ?? '' );
        if ( ! $phone ) return;

        $account = self::get_or_create_account( 'customer', [
            'phone' => $phone,
            'display_name' => $request_data['customer_name'] ?? '',
        ] );

        $is_first = (int) $GLOBALS['wpdb']->get_var( $GLOBALS['wpdb']->prepare(
            "SELECT COUNT(*) FROM {$GLOBALS['wpdb']->prefix}svc_requests WHERE phone=%s", $phone
        ) ) <= 1;

        if ( $is_first ) {
            self::earn( (int) $account['id'], 'register', [ 'dedupe_key' => 'reg_' . $phone ] );
            self::earn( (int) $account['id'], 'first_request', [
                'ref_type' => 'request', 'ref_id' => $request_id,
                'dedupe_key' => 'first_' . $phone,
            ] );
        }
    }

    public static function on_report_finalized( array $report ): void {
        $tech_id = (int) ( $report['technician_id'] ?? 0 );
        if ( ! $tech_id ) {
            $req_id = (int) ( $report['request_id'] ?? 0 );
            if ( $req_id ) {
                $tech_id = (int) $GLOBALS['wpdb']->get_var( $GLOBALS['wpdb']->prepare(
                    "SELECT assigned_technician_id FROM {$GLOBALS['wpdb']->prefix}svc_requests WHERE id=%d", $req_id
                ) );
            }
        }
        if ( $tech_id ) {
            $tacct = self::get_or_create_account( 'technician', [ 'user_id' => $tech_id ] );
            self::earn( (int) $tacct['id'], 'mission_done', [
                'ref_type' => 'report', 'ref_id' => (int) $report['id'],
                'dedupe_key' => 'mission_' . $report['id'],
                'order_value' => (float) ( $report['total'] ?? 0 ),
            ] );
            self::earn( (int) $tacct['id'], 'full_report', [
                'dedupe_key' => 'report_' . $report['id'],
            ] );
        }

        $phone = self::normalize_phone( $report['phone'] ?? '' );
        if ( $phone ) {
            $cacct = self::get_or_create_account( 'customer', [ 'phone' => $phone, 'display_name' => $report['customer_name'] ?? '' ] );
            $parts_val = (float) ( $report['subtotal_parts'] ?? 0 );
            if ( $parts_val > 0 ) {
                self::earn( (int) $cacct['id'], 'parts_purchase', [
                    'order_value' => $parts_val, 'ref_type' => 'report', 'ref_id' => (int) $report['id'],
                    'dedupe_key' => 'parts_' . $report['id'],
                ] );
            }
        }
    }

    public static function on_report_extras( array $report ): void {
        global $wpdb;
        $tech_id = (int) ( $report['created_by'] ?? 0 );
        if ( ! $tech_id && ! empty( $report['request_id'] ) ) {
            $tech_id = (int) $wpdb->get_var( $wpdb->prepare(
                "SELECT assigned_technician_id FROM {$wpdb->prefix}svc_requests WHERE id=%d", (int) $report['request_id']
            ) );
        }
        if ( ! $tech_id ) return;

        $tacct = self::get_or_create_account( 'technician', [ 'user_id' => $tech_id ] );
        $tid = (int) $tacct['id'];
        $rid = (int) ( $report['id'] ?? 0 );

        $pm = is_array( $report['pm_offer'] ?? null ) ? $report['pm_offer'] : json_decode( $report['pm_offer'] ?? '{}', true );
        if ( ! empty( $pm['selected'] ) ) {
            self::earn( $tid, 'pm_sold', [ 'dedupe_key' => 'pmsold_' . $rid, 'order_value' => 5000000 ] );
        }

        $parts = (float) ( $report['subtotal_parts'] ?? 0 );
        if ( $parts > 0 ) {
            self::earn( $tid, 'parts_sold', [ 'order_value' => $parts, 'dedupe_key' => 'tsell_' . $rid ] );
        }

        $photos_b = $report['photos_before'] ?? '[]';
        $photos_a = $report['photos_after'] ?? '[]';
        if ( ( is_string( $photos_b ) && strlen( $photos_b ) > 4 ) || ( is_string( $photos_a ) && strlen( $photos_a ) > 4 ) ) {
            self::earn( $tid, 'photos_uploaded', [ 'dedupe_key' => 'photo_' . $rid ] );
        }

        if ( ! empty( $report['customer_signature'] ) ) {
            self::earn( $tid, 'no_revisit', [ 'dedupe_key' => 'nrv_' . $rid, 'quality_mult' => 1.2 ] );
        }

        $req_id = (int) ( $report['request_id'] ?? 0 );
        if ( $req_id ) {
            $revisits = (int) $wpdb->get_var( $wpdb->prepare(
                "SELECT COUNT(*) FROM {$wpdb->prefix}svc_requests WHERE phone=%s AND service_type='repair' AND id!=%d",
                self::normalize_phone( $report['phone'] ?? '' ), $req_id
            ) );
            if ( $revisits === 0 ) {
                self::earn( $tid, 'no_revisit', [ 'dedupe_key' => 'nrv2_' . $rid ] );
            }
        }
    }

    public static function on_customer_event( string $phone, string $event_key, array $opts = [] ): void {
        $phone = self::normalize_phone( $phone );
        if ( ! $phone ) return;
        $acct = self::get_or_create_account( 'customer', [ 'phone' => $phone ] );
        self::earn( (int) $acct['id'], $event_key, $opts );
    }

    public static function on_device_profile_complete( string $phone, int $device_id ): void {
        self::on_customer_event( $phone, 'profile_complete', [
            'ref_type' => 'device', 'ref_id' => $device_id, 'dedupe_key' => 'prof_' . $device_id,
        ] );
    }

    public static function on_performance_report( string $phone, int $device_id ): void {
        self::on_customer_event( $phone, 'performance_report', [
            'ref_type' => 'device', 'ref_id' => $device_id, 'dedupe_key' => 'perf_' . $device_id,
        ] );
    }

    public static function on_supplier_event( string $event_key, array $opts = [] ): void {
        global $wpdb;
        $uid = (int) ( $opts['supplier_user_id'] ?? 0 );
        if ( ! $uid ) {
            $uid = (int) $wpdb->get_var(
                "SELECT user_id FROM {$wpdb->prefix}svc_reward_accounts WHERE actor_type='supplier' AND user_id IS NOT NULL ORDER BY rank_score DESC LIMIT 1"
            );
        }
        if ( ! $uid ) return;
        $acct = self::get_or_create_account( 'supplier', [ 'user_id' => $uid ] );
        self::earn( (int) $acct['id'], $event_key, $opts );
    }

    public static function on_tech_fast_response( int $tech_user_id, int $request_id ): void {
        $acct = self::get_or_create_account( 'technician', [ 'user_id' => $tech_user_id ] );
        self::earn( (int) $acct['id'], 'fast_response', [
            'ref_type' => 'request', 'ref_id' => $request_id, 'dedupe_key' => 'fast_' . $request_id,
        ] );
    }

    public static function on_tech_training( int $tech_user_id ): void {
        $acct = self::get_or_create_account( 'technician', [ 'user_id' => $tech_user_id ] );
        self::earn( (int) $acct['id'], 'training', [ 'dedupe_key' => 'train_' . $tech_user_id . '_' . date( 'Ym' ) ] );
    }

    public static function apply_redeem_benefit( int $account_id, string $redeem_key ): void {
        global $wpdb;
        $row = $wpdb->get_row( $wpdb->prepare(
            "SELECT * FROM {$wpdb->prefix}svc_reward_accounts WHERE id=%d", $account_id
        ), ARRAY_A );
        if ( ! $row ) return;

        $benefits = json_decode( $row['active_benefits'] ?? '[]', true ) ?: [];
        $expires = date( 'Y-m-d H:i:s', strtotime( '+90 days' ) );
        $benefits[] = [ 'key' => $redeem_key, 'expires' => $expires, 'applied_at' => current_time( 'mysql' ) ];
        $wpdb->update( "{$wpdb->prefix}svc_reward_accounts", [
            'active_benefits' => wp_json_encode( $benefits, JSON_UNESCAPED_UNICODE ),
            'updated_at'      => current_time( 'mysql' ),
        ], [ 'id' => $account_id ] );

        if ( $redeem_key === 'premium_badge' ) {
            $wpdb->query( $wpdb->prepare(
                "INSERT IGNORE INTO {$wpdb->prefix}svc_reward_badges (account_id, badge_key) VALUES (%d, %s)",
                $account_id, 'premium_supplier'
            ) );
        }
    }

    public static function get_active_benefits( array $account ): array {
        $benefits = json_decode( $account['active_benefits'] ?? '[]', true ) ?: [];
        $now = time();
        return array_values( array_filter( $benefits, function ( $b ) use ( $now ) {
            return empty( $b['expires'] ) || strtotime( $b['expires'] ) > $now;
        } ) );
    }

    public static function has_benefit( string $phone, string $benefit_key ): bool {
        global $wpdb;
        $phone = self::normalize_phone( $phone );
        $row = $wpdb->get_row( $wpdb->prepare(
            "SELECT active_benefits FROM {$wpdb->prefix}svc_reward_accounts WHERE actor_type='customer' AND phone=%s",
            $phone
        ), ARRAY_A );
        if ( ! $row ) return false;
        foreach ( self::get_active_benefits( $row ) as $b ) {
            if ( ( $b['key'] ?? '' ) === $benefit_key ) return true;
        }
        return false;
    }

    public static function ai_suggestions( array $account ): array {
        $tips = [];
        $lifetime = (int) ( $account['lifetime_earned'] ?? 0 );
        $avail = (int) ( $account['points_available'] ?? 0 );
        $actor = $account['actor_type'] ?? 'customer';
        $tiers = self::tier_config()[ $actor ] ?? [];

        foreach ( $tiers as $k => $cfg ) {
            if ( (int) $cfg['min_points'] > $lifetime ) {
                $need = (int) $cfg['min_points'] - $lifetime;
                $tips[] = "فقط {$need} امتیاز تا سطح {$cfg['label']} — یک فعالیت بیشتر کافی است.";
                break;
            }
        }

        if ( $actor === 'customer' ) {
            if ( $avail >= 100 && $avail < 200 ) $tips[] = 'می‌توانید «مشاوره رایگان» را فعال کنید.';
            if ( $avail >= 250 ) $tips[] = 'با «اولویت اعزام» درخواست بعدی شما زودتر پردازش می‌شود.';
            if ( empty( $account['referred_by'] ) ) $tips[] = 'با معرفی دوست، ۲۰۰ امتیاز بگیرید.';
        }
        if ( $actor === 'technician' ) {
            if ( $lifetime < 800 ) $tips[] = 'ثبت عکس قبل/بعد و گزارش کامل = امتیاز بیشتر.';
            if ( $avail >= 500 ) $tips[] = 'دوره آموزشی رایگان قابل دریافت است.';
        }
        if ( $actor === 'supplier' ) {
            $tips[] = 'تحویل به‌موقع و پاسخ سریع استعلام = رتبه بالاتر در جستجو.';
        }

        return $tips;
    }

    public static function on_payment_success( int $report_id, float $amount ): void {
        global $wpdb;
        $report = $wpdb->get_row( $wpdb->prepare(
            "SELECT * FROM {$wpdb->prefix}svc_reports WHERE id=%d", $report_id
        ), ARRAY_A );
        if ( ! $report ) return;

        $phone = self::normalize_phone( $report['phone'] ?? '' );
        if ( ! $phone ) return;

        $account = self::get_or_create_account( 'customer', [
            'phone' => $phone, 'display_name' => $report['customer_name'] ?? '',
        ] );
        self::earn( (int) $account['id'], 'online_payment', [
            'ref_type' => 'payment', 'ref_id' => $report_id,
            'dedupe_key' => 'pay_' . $report_id,
            'order_value' => $amount,
        ] );
    }

    public static function on_pm_contract( array $contract ): void {
        $phone = self::normalize_phone( $contract['phone'] ?? '' );
        if ( ! $phone ) return;
        $account = self::get_or_create_account( 'customer', [ 'phone' => $phone ] );
        self::earn( (int) $account['id'], 'pm_annual', [
            'ref_type' => 'pm', 'ref_id' => (int) ( $contract['id'] ?? 0 ),
            'dedupe_key' => 'pm_' . ( $contract['id'] ?? 0 ),
        ] );
    }
}
