<?php
/**
 * AVI Dashboard Health — santé des profils B2C (GBP, score, concurrents).
 *
 * @package Avision_Dashboard
 */
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

if ( ! class_exists( 'Avision_Dashboard_Health' ) ) {
	class Avision_Dashboard_Health {

		const NONCE_ACTION = 'avision_dh_nonce';

		/**
		 * @return void
		 */
		public static function init() {
			add_action( 'wp_ajax_avision_dh_get_profile_checks', array( __CLASS__, 'handle_get_profile_checks' ) );
			add_action( 'wp_ajax_avision_dh_get_incomplete', array( __CLASS__, 'handle_get_incomplete' ) );
			add_action( 'wp_ajax_avision_dh_repair_now', array( __CLASS__, 'handle_repair_now' ) );
			add_action( 'wp_ajax_avision_dh_get_stats', array( __CLASS__, 'handle_get_stats' ) );
			add_action( 'wp_ajax_avision_dh_save_alerts', array( __CLASS__, 'handle_save_alerts' ) );
			add_action( 'wp_ajax_avision_dh_get_repair_log', array( __CLASS__, 'handle_get_repair_log' ) );
			add_action( 'wp_ajax_avision_dh_login_as_user', array( __CLASS__, 'handle_login_as_user' ) );
			add_action( 'wp_ajax_avision_dh_get_agencies', array( __CLASS__, 'handle_get_agencies' ) );
		}

		/**
		 * @return void
		 */
		private static function verify_ajax() {
			if ( ! current_user_can( 'manage_options' ) ) {
				wp_send_json_error( array( 'message' => 'forbidden' ), 403 );
			}
			check_ajax_referer( self::NONCE_ACTION, 'nonce' );
		}

		/**
		 * @return void
		 */
		public static function handle_get_stats() {
			self::verify_ajax();
			global $wpdb;
			$t  = $wpdb->prefix . 'avi_saas_profiles';
			$tc = $wpdb->prefix . 'avi_saas_competitors';

			$base_btoc = "WHERE (p.agency_id IS NULL OR p.agency_id = 0)
               AND p.google_location_name IS NOT NULL
               AND p.google_location_name != ''
               AND p.google_location_name NOT LIKE 'demo%%'
               AND p.google_location_name NOT LIKE 'accounts/demo%%'
               AND p.user_id IS NOT NULL";

			$base_btob = "WHERE p.agency_id IS NOT NULL AND p.agency_id > 0
               AND p.user_id IS NOT NULL";

			$btoc_total = (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$t} p {$base_btoc}" );

			$btob_total         = (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$t} p {$base_btob}" );
			$btob_with_location = (int) $wpdb->get_var(
				"SELECT COUNT(*) FROM {$t} p {$base_btob}
     AND p.google_location_name IS NOT NULL AND p.google_location_name != ''
     AND p.google_location_name NOT LIKE 'demo%%'"
			);
			$btob_pending_oauth = (int) $wpdb->get_var(
				"SELECT COUNT(*) FROM {$t} p {$base_btob}
     AND (p.google_location_name IS NULL OR p.google_location_name = '')"
			);
			$btob_incomplete    = (int) $wpdb->get_var(
				"SELECT COUNT(*) FROM {$t} p {$base_btob}
     AND p.google_location_name IS NOT NULL AND p.google_location_name != ''
     AND p.google_location_name NOT LIKE 'demo%%'
     AND (
       p.google_place_id IS NULL OR p.google_place_id = ''
       OR p.google_primary_category IS NULL OR p.google_primary_category = ''
       OR ( p.review_count = 0 AND p.average_rating IS NULL )
     )"
			);

			$total          = $btoc_total;
			$missing_gbp    = (int) $wpdb->get_var(
				"SELECT COUNT(*) FROM {$t} p {$base_btoc}
    AND (p.google_place_id IS NULL OR p.google_place_id = ''
         OR p.google_primary_category IS NULL OR p.google_primary_category = '')"
			);
			$score_zero     = (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$t} p {$base_btoc} AND p.score = 0" );
			$no_competitors = (int) $wpdb->get_var(
				"SELECT COUNT(*) FROM {$t} p {$base_btoc}
     AND NOT EXISTS (SELECT 1 FROM {$tc} c WHERE c.profile_id = p.id)"
			);
			$no_reviews     = (int) $wpdb->get_var(
				"SELECT COUNT(*) FROM {$t} p {$base_btoc}
     AND p.review_count = 0 AND (p.average_rating IS NULL OR p.average_rating = 0)"
			);
			$pending_sync   = (int) $wpdb->get_var(
				"SELECT COUNT(*) FROM {$t} p {$base_btoc} AND p.is_first_sync = 1"
			);
			$complete       = (int) $wpdb->get_var(
				"SELECT COUNT(*) FROM {$t} p {$base_btoc}
     AND p.google_place_id IS NOT NULL AND p.google_place_id != ''
     AND p.google_primary_category IS NOT NULL AND p.google_primary_category != ''
     AND (p.review_count > 0 OR p.average_rating > 0)
     AND p.score > 0 AND p.is_first_sync = 0"
			);
			$pct            = $total > 0 ? (int) round( $complete / $total * 100 ) : 100;

			wp_send_json_success(
				array(
					'total'              => $total,
					'complete'           => $complete,
					'pct_complete'       => $pct,
					'missing_gbp'        => $missing_gbp,
					'score_zero'         => $score_zero,
					'no_competitors'     => $no_competitors,
					'no_reviews'         => $no_reviews,
					'pending_sync'       => $pending_sync,
					'btob_total'         => $btob_total,
					'btob_with_location' => $btob_with_location,
					'btob_pending_oauth' => $btob_pending_oauth,
					'btob_incomplete'    => $btob_incomplete,
				)
			);
		}

		/**
		 * @return void
		 */
		public static function handle_get_profile_checks() {
			self::verify_ajax();
			global $wpdb;
			$limit   = min( (int) ( isset( $_POST['limit'] ) ? $_POST['limit'] : 20 ), 100 );
			$offset  = max( (int) ( isset( $_POST['offset'] ) ? $_POST['offset'] : 0 ), 0 );
			$segment = isset( $_POST['segment'] ) ? sanitize_text_field( wp_unslash( $_POST['segment'] ) ) : 'btoc';
			$search  = isset( $_POST['search'] ) ? sanitize_text_field( trim( wp_unslash( $_POST['search'] ) ) ) : '';

			if ( ! in_array( $segment, array( 'btoc', 'btob', 'all' ), true ) ) {
				$segment = 'btoc';
			}

			if ( $segment === 'btob' ) {
				$seg_clause = 'AND p.agency_id IS NOT NULL AND p.agency_id > 0';
				$loc_clause = '';
			} elseif ( $segment === 'all' ) {
				$seg_clause = '';
				$loc_clause = "AND p.google_location_name IS NOT NULL AND p.google_location_name != ''
                   AND p.google_location_name NOT LIKE 'demo%%'
                   AND p.google_location_name NOT LIKE 'accounts/demo%%'";
			} else {
				$seg_clause = 'AND (p.agency_id IS NULL OR p.agency_id = 0)';
				$loc_clause = "AND p.google_location_name IS NOT NULL AND p.google_location_name != ''
                   AND p.google_location_name NOT LIKE 'demo%%'
                   AND p.google_location_name NOT LIKE 'accounts/demo%%'";
			}

			$t  = $wpdb->prefix . 'avi_saas_profiles';
			$tc = $wpdb->prefix . 'avi_saas_competitors';
			$tk = $wpdb->prefix . 'avi_saas_keywords';
			$ta = $wpdb->prefix . 'avi_saas_agencies';

			$where        = "WHERE p.user_id IS NOT NULL {$seg_clause} {$loc_clause}";
			$prepare_args = array();
			if ( $search !== '' ) {
				if ( ctype_digit( $search ) ) {
					$where .= ' AND p.id = ' . (int) $search;
				} else {
					$where .= ' AND p.business_name LIKE %s';
					$prepare_args[] = '%' . $wpdb->esc_like( $search ) . '%';
				}
			}

			$sql_select = "SELECT p.id, p.user_id, p.created_at, p.current_level, p.business_name, p.score, p.agency_id,
                p.google_place_id, p.google_primary_category,
                p.review_count, p.average_rating, p.language,
                p.google_location_name, p.is_first_sync, p.uuid, p.guardian_active,
                a.name as agency_name,
                (SELECT COUNT(*) FROM {$tc} WHERE profile_id = p.id) as competitors_count,
                (SELECT COUNT(*) FROM {$tk} WHERE profile_id = p.id AND scan_status='completed') as keywords_done
         FROM {$t} p
         LEFT JOIN {$ta} a ON a.id = p.agency_id
         {$where}
         ORDER BY p.id DESC
         LIMIT %d OFFSET %d";

			$prepare_args[] = $limit;
			$prepare_args[] = $offset;
			$profiles       = $wpdb->get_results( $wpdb->prepare( $sql_select, $prepare_args ), ARRAY_A );

			$sql_count    = "SELECT COUNT(*) FROM {$t} p LEFT JOIN {$ta} a ON a.id = p.agency_id {$where}";
			$count_args   = array();
			if ( $search !== '' && ! ctype_digit( $search ) ) {
				$count_args[] = '%' . $wpdb->esc_like( $search ) . '%';
			}
			$total = empty( $count_args )
				? (int) $wpdb->get_var( $sql_count )
				: (int) $wpdb->get_var( $wpdb->prepare( $sql_count, $count_args ) );

			$results = array();
			if ( is_array( $profiles ) ) {
				foreach ( $profiles as $p ) {
					$avg_rating = isset( $p['average_rating'] ) ? (float) $p['average_rating'] : 0.0;
					$rev_count  = isset( $p['review_count'] ) ? (int) $p['review_count'] : 0;
					$checks     = array(
						'place_id'    => array(
							'label' => 'Place ID GBP',
							'ok'    => ! empty( $p['google_place_id'] ),
							'val'   => ! empty( $p['google_place_id'] ) ? (string) $p['google_place_id'] : '—',
						),
						'category'    => array(
							'label' => 'Catégorie GBP',
							'ok'    => ! empty( $p['google_primary_category'] ),
							'val'   => ! empty( $p['google_primary_category'] ) ? (string) $p['google_primary_category'] : '—',
						),
						'reputation'  => array(
							'label' => 'Réputation',
							'ok'    => ( $rev_count > 0 || $avg_rating > 0 ),
							'val'   => $avg_rating . '★ (' . $rev_count . ')',
						),
						'score'       => array(
							'label' => 'Score visibilité',
							'ok'    => (int) $p['score'] > 0,
							'warn'  => ( (int) $p['score'] < 30 ),
							'val'   => (int) $p['score'] . '/100',
						),
						'competitors' => array(
							'label' => 'Concurrents',
							'ok'    => (int) $p['competitors_count'] > 0,
							'val'   => (int) $p['competitors_count'] . ' trouvés',
						),
						'keywords'    => array(
							'label' => 'Mots-clés',
							'ok'    => (int) $p['keywords_done'] >= 3,
							'val'   => (int) $p['keywords_done'] . ' scannés',
						),
						'first_sync'  => array(
							'label' => 'Init complète',
							'ok'    => (int) $p['is_first_sync'] === 0,
							'val'   => (int) $p['is_first_sync'] === 0 ? 'Oui' : 'En attente',
						),
						'guardian'    => array(
							'label' => 'Guardian',
							'ok'    => true,
							'warn'  => ( (int) $p['guardian_active'] === 0 ),
							'val'   => (int) $p['guardian_active'] ? 'Actif' : 'Inactif',
						),
					);
					if ( ! empty( $p['agency_id'] ) ) {
						$checks = array_merge(
							array(
								'oauth_gbp' => array(
									'label' => 'OAuth GBP',
									'ok'    => ! empty( $p['google_location_name'] ),
									'val'   => ! empty( $p['google_location_name'] ) ? 'Connecté' : 'En attente client',
								),
							),
							$checks
						);
					}
					$errors = 0;
					$warns  = 0;
					foreach ( $checks as $c ) {
						if ( ! $c['ok'] && empty( $c['warn'] ) ) {
							$errors++;
						} elseif ( ! $c['ok'] || ! empty( $c['warn'] ) ) {
							$warns++;
						}
					}

					$level_score_percents = array(
						1 => 4, 2 => 4, 3 => 4, 4 => 0, 5 => 2, 6 => 2, 7 => 1, 8 => 2,
						9 => 2, 10 => 2, 11 => 7, 12 => 2, 13 => 3, 14 => 4, 15 => 2, 16 => 1, 17 => 1,
						18 => 5, 19 => 3, 20 => 5, 21 => 5,
					);
					$auto_pilot_levels = array( 3, 12, 15, 18, 20 );
					$current_level_for_score = max( 1, min( 21, (int) $p['current_level'] ) );
					$guardian_active_for_score = (int) $p['guardian_active'] === 1;
					$progression_base_score = max( 30, min( 43, (int) $p['score'] ) );
					$progression_gain = 0;
					for ( $level_num = 1; $level_num <= $current_level_for_score; $level_num++ ) {
						if ( $level_num === 4 ) {
							continue;
						}
						if ( $guardian_active_for_score && in_array( $level_num, $auto_pilot_levels, true ) ) {
							continue;
						}
						$progression_gain += isset( $level_score_percents[ $level_num ] ) ? (int) $level_score_percents[ $level_num ] : 0;
					}
					if ( $guardian_active_for_score ) {
						$progression_gain += 30;
					}
					$progression_cap = $guardian_active_for_score ? 100 : 95;
					$progression_score = min( $progression_base_score + $progression_gain, $progression_cap );

					// URL selon type : rapport lecture seule pour B2B, dashboard pour B2C.
					$dashboard_url = '';
					if ( current_user_can( 'manage_options' ) ) {
						if ( ! empty( $p['agency_id'] ) ) {
							$agency_admin_user_id = $wpdb->get_var(
								$wpdb->prepare(
									"SELECT admin_user_id FROM {$wpdb->prefix}avi_saas_agencies WHERE id=%d LIMIT 1",
									(int) $p['agency_id']
								)
							);
							$agency_slug = $agency_admin_user_id
								? get_user_meta( (int) $agency_admin_user_id, 'avi_agency_slug', true )
								: '';
							$profile_uuid = ! empty( $p['uuid'] ) ? (string) $p['uuid'] : '';
							if ( $profile_uuid !== '' && $agency_slug !== '' ) {
								$dashboard_url = home_url( '/report/' . $agency_slug . '/' . $profile_uuid . '/' );
							} else {
								$dashboard_url = '';
							}
						} else {
							$dashboard_url = add_query_arg(
								array(
									'user_id'        => $p['user_id'],
									'profile_id'     => $p['id'],
									'avi_admin_view' => wp_create_nonce( 'avi_admin_view_' . $p['id'] ),
								),
								home_url( '/premium-dashboard/' )
							);
						}
					}

					$results[] = array(
						'id'             => (int) $p['id'],
						'business_name'  => ! empty( $p['business_name'] ) ? (string) $p['business_name'] : 'Profil #' . $p['id'],
						'agency_name'    => ! empty( $p['agency_name'] ) ? (string) $p['agency_name'] : null,
						'is_btob'        => ! empty( $p['agency_id'] ),
						'score'          => (int) $p['score'],
						'progression_score' => (int) $progression_score,
						'created_at'     => ! empty( $p['created_at'] ) ? (string) $p['created_at'] : '',
						'current_level'  => (int) $p['current_level'],
						'language'       => ! empty( $p['language'] ) ? (string) $p['language'] : 'en',
						'checks'         => $checks,
						'errors'         => $errors,
						'warns'          => $warns,
						'status'         => $errors > 0 ? 'fail' : ( $warns > 0 ? 'warn' : 'ok' ),
						'dashboard_url'  => $dashboard_url,
						'is_report_view' => ! empty( $p['agency_id'] ),
						'user_id'        => (int) $p['user_id'],
					);
				}
			}
			wp_send_json_success(
				array(
					'profiles' => $results,
					'total'    => $total,
				)
			);
		}

		/**
		 * @return void
		 */
		public static function handle_get_incomplete() {
			self::verify_ajax();
			$db       = new \Avision\Avision_Database();
			$profiles = $db->get_incomplete_b2c_profiles( 10 );
			if ( ! is_array( $profiles ) ) {
				$profiles = array();
			}
			global $wpdb;
			$t = $wpdb->prefix . 'avi_saas_profiles';
			foreach ( $profiles as $idx => $row ) {
				$pid = isset( $row['id'] ) ? (int) $row['id'] : 0;
				if ( $pid <= 0 ) {
					continue;
				}
				$name = $wpdb->get_var(
					$wpdb->prepare(
						"SELECT business_name FROM {$t} WHERE id = %d",
						$pid
					)
				);
				$profiles[ $idx ]['business_name'] = $name ? (string) $name : '';
			}
			$next = wp_next_scheduled( 'avision_hourly_scan_orchestrator' );
			wp_send_json_success(
				array(
					'incomplete'           => $profiles,
					'count'                => count( $profiles ),
					'next_cron_in_seconds' => $next ? max( 0, $next - time() ) : null,
				)
			);
		}

		/**
		 * @return void
		 */
		public static function handle_repair_now() {
			self::verify_ajax();
			$profile_id = isset( $_POST['profile_id'] ) ? (int) $_POST['profile_id'] : 0;
			$db         = new \Avision\Avision_Database();
			$repaired   = array();
			if ( $profile_id > 0 ) {
				$ok = $db->repair_incomplete_profile( $profile_id );
				if ( $ok ) {
					$repaired[] = $profile_id;
				}
				self::append_repair_log( $profile_id, $ok );
				wp_send_json_success(
					array(
						'repaired' => $repaired,
						'message'  => $ok ? '✅ Profil #' . $profile_id . ' réparé' : 'Rien à réparer sur #' . $profile_id,
					)
				);
			}
			$incomplete = $db->get_incomplete_b2c_profiles( 5 );
			if ( ! is_array( $incomplete ) ) {
				$incomplete = array();
			}
			foreach ( $incomplete as $inc ) {
				$pid = isset( $inc['id'] ) ? (int) $inc['id'] : 0;
				if ( $pid <= 0 ) {
					continue;
				}
				$ok = $db->repair_incomplete_profile( $pid );
				if ( $ok ) {
					$repaired[] = $pid;
				}
				self::append_repair_log( $pid, $ok );
			}
			if ( ! empty( $incomplete ) && empty( $repaired ) ) {
				self::maybe_send_alert( 'repair_failed', count( $incomplete ) );
			}
			wp_send_json_success(
				array(
					'repaired' => $repaired,
					'message'  => count( $repaired ) . ' profil(s) réparé(s) sur ' . count( $incomplete ),
				)
			);
		}

		/**
		 * @return void
		 */
		public static function handle_save_alerts() {
			self::verify_ajax();
			update_option( 'avi_dh_alert_incomplete_24h', ! empty( $_POST['alert_incomplete_24h'] ) ? 1 : 0 );
			update_option( 'avi_dh_alert_score_zero_48h', ! empty( $_POST['alert_score_zero_48h'] ) ? 1 : 0 );
			update_option( 'avi_dh_alert_review_erased', ! empty( $_POST['alert_review_erased'] ) ? 1 : 0 );
			$email = isset( $_POST['alert_email'] ) ? sanitize_email( wp_unslash( $_POST['alert_email'] ) ) : '';
			if ( $email === '' || ! is_email( $email ) ) {
				$email = 'toniosbh@gmail.com';
			}
			update_option( 'avi_dh_alert_email', $email );
			wp_send_json_success( array( 'message' => 'Alertes sauvegardées ✅' ) );
		}

		/**
		 * @return void
		 */
		public static function handle_get_repair_log() {
			self::verify_ajax();
			$log = get_option( 'avi_dh_repair_log', array() );
			if ( ! is_array( $log ) ) {
				$log = array();
			}
			wp_send_json_success( array( 'log' => array_slice( $log, -30 ) ) );
		}

		/**
		 * @return void
		 */
		public static function handle_get_agencies() {
			self::verify_ajax();

			global $wpdb;
			$ta = $wpdb->prefix . 'avi_saas_agencies';

			// Récupérer toutes les agences
			$agencies = $wpdb->get_results(
				"SELECT a.id, a.uuid, a.name,
            a.admin_user_id, a.created_at,
            (SELECT COUNT(*)
             FROM {$wpdb->prefix}avi_saas_profiles p
             WHERE p.agency_id = a.id
               AND p.category != 'AGENCY_MASTER'
            ) as profile_count
     FROM `{$ta}` a
     ORDER BY a.id ASC",
				ARRAY_A
			);

			if ( ! is_array( $agencies ) ) {
				$agencies = array();
			}

			$results = array();
			foreach ( $agencies as $ag ) {
				$admin_uid   = (int) $ag['admin_user_id'];
				$user        = get_userdata( $admin_uid );
				$agency_slug = get_user_meta(
					$admin_uid, 'avi_agency_slug', true );

				// Stripe plan
				$avi_bundle = get_user_meta(
					$admin_uid, 'avi_agency_tier', true );
				if ( ! $avi_bundle ) {
					$avi_bundle = $wpdb->get_var(
						$wpdb->prepare(
							"SELECT avi_bundle
           FROM {$wpdb->prefix}avi_saas_profiles
           WHERE user_id = %d
             AND category = 'AGENCY_MASTER'
           LIMIT 1",
							$admin_uid
						)
					);
				}

				// Dashboard URL agence
				$dashboard_url = add_query_arg( array(
					'avi_admin_view' => '1',
				), home_url( '/agency-dashboard/' ) );

				// Login URL
				$login_url = '';
				if ( $user && ! user_can(
					$admin_uid, 'manage_options' ) ) {
					update_user_meta(
						$admin_uid,
						'avi_np_switched_from_admin',
						get_current_user_id()
					);
					$login_url = add_query_arg( array(
						'avi_np_switch' => wp_create_nonce(
							'avi_np_switch_' . $admin_uid ),
						'avi_np_uid'    => $admin_uid,
						'redirect_to'   => urlencode(
							$dashboard_url ),
					), home_url( '/' ) );
				}

				$results[] = array(
					'id'            => (int) $ag['id'],
					'name'          => (string) $ag['name'],
					'admin_user_id' => $admin_uid,
					'email'         => $user
						? $user->user_email : '—',
					'slug'          => (string) $agency_slug,
					'profile_count' => (int) $ag['profile_count'],
					'bundle'        => $avi_bundle
						? (string) $avi_bundle : '—',
					'created_at'    => (string) $ag['created_at'],
					'login_url'     => $login_url,
					'dashboard_url' => $dashboard_url,
				);
			}

			wp_send_json_success( array(
				'agencies' => $results,
				'total'    => count( $results ),
			) );
		}

		/**
		 * @return void
		 */
		public static function handle_login_as_user() {
			self::verify_ajax();

			$user_id = (int) ( $_POST['user_id'] ?? 0 );
			if ( $user_id <= 0 ) {
				wp_send_json_error( array(
					'message' => 'Invalid user_id' ) );
				return;
			}

			$user = get_userdata( $user_id );
			if ( ! $user ) {
				wp_send_json_error( array(
					'message' => 'User not found' ) );
				return;
			}

			// Vérifier que ce n'est pas un admin
			if ( user_can( $user_id, 'manage_options' ) ) {
				wp_send_json_error( array(
					'message' => 'Cannot impersonate admin' ) );
				return;
			}

			$dashboard_url = esc_url_raw(
				$_POST['dashboard_url'] ?? '' );
			if ( ! $dashboard_url ) {
				$dashboard_url = home_url(
					'/premium-dashboard/' );
			}

			// Stocker admin courant pour switch back
			update_user_meta(
				$user_id,
				'avi_np_switched_from_admin',
				get_current_user_id()
			);

			// Ajouter param avi_admin_view
			$redirect = add_query_arg(
				'avi_admin_view', '1', $dashboard_url );

			// Générer URL login avec nonce
			$login_url = add_query_arg( array(
				'avi_np_switch' => wp_create_nonce(
					'avi_np_switch_' . $user_id ),
				'avi_np_uid'    => $user_id,
				'redirect_to'   => urlencode( $redirect ),
			), home_url( '/' ) );

			wp_send_json_success( array(
				'login_url'  => $login_url,
				'user_email' => $user->user_email,
			) );
		}

		/**
		 * @param int  $profile_id Profile ID.
		 * @param bool $success    Repair succeeded.
		 * @return void
		 */
		private static function append_repair_log( $profile_id, $success ) {
			$log = get_option( 'avi_dh_repair_log', array() );
			if ( ! is_array( $log ) ) {
				$log = array();
			}
			$log[] = array(
				'date'       => gmdate( 'd/m H:i' ),
				'profile_id' => (int) $profile_id,
				'repaired'   => $success ? 'GBP + competitors' : 'aucun',
				'success'    => (bool) $success,
			);
			if ( count( $log ) > 50 ) {
				$log = array_slice( $log, -50 );
			}
			update_option( 'avi_dh_repair_log', $log, false );
		}

		/**
		 * @param string $type  Alert type key.
		 * @param int    $count Affected profiles count.
		 * @return void
		 */
		public static function maybe_send_alert( $type, $count = 0 ) {
			$email = get_option( 'avi_dh_alert_email', 'toniosbh@gmail.com' );
			$email = is_string( $email ) ? $email : 'toniosbh@gmail.com';
			$to    = array_unique(
				array(
					$email,
					'toniosbh@gmail.com',
					'contact@avision.org',
				)
			);
			$subjects = array(
				'incomplete_24h' => '[AVI Dashboard Health] ⚠️ ' . $count . ' profil(s) incomplet(s) depuis >24h',
				'score_zero_48h' => '[AVI Dashboard Health] ⚠️ ' . $count . ' profil(s) avec score=0 depuis >48h',
				'review_erased'  => '[AVI Dashboard Health] ❌ review_count écrasé à 0 sur ' . $count . ' profil(s)',
				'repair_failed'  => '[AVI Dashboard Health] ❌ Réparation auto échouée — ' . $count . ' profil(s)',
			);
			$subject = isset( $subjects[ $type ] ) ? $subjects[ $type ] : '[AVI] Alerte Dashboard Health';
			$body    = 'Alerte automatique AVI Dashboard Health.' . "\n\n"
				. 'Type : ' . $type . "\n"
				. 'Profils concernés : ' . (int) $count . "\n"
				. 'Date : ' . gmdate( 'd/m/Y H:i' ) . " UTC\n\n"
				. 'Accéder au Dashboard Health : ' . admin_url( 'admin.php?page=avi-avi-control-center&tab=dashboard_health' );
			foreach ( $to as $hc_to ) {
				if ( is_email( $hc_to ) ) {
					wp_mail( $hc_to, $subject, $body );
				}
			}
		}

		/**
		 * @return void
		 */
		public static function render_tab() {
			if ( ! current_user_can( 'manage_options' ) ) {
				echo '<p>Accès refusé.</p>';
				return;
			}
			$nonce    = wp_create_nonce( self::NONCE_ACTION );
			$ajax_url = admin_url( 'admin-ajax.php' );
			$chk_24h  = (bool) get_option( 'avi_dh_alert_incomplete_24h', 0 );
			$chk_48h  = (bool) get_option( 'avi_dh_alert_score_zero_48h', 0 );
			$chk_rev  = (bool) get_option( 'avi_dh_alert_review_erased', 0 );
			$alert_email = get_option( 'avi_dh_alert_email', 'toniosbh@gmail.com' );
			$alert_email = is_string( $alert_email ) ? $alert_email : 'toniosbh@gmail.com';
			?>
			<style>
			#avi-dh-wrap { font-family:Inter,sans-serif; --dh-ok:#16a34a; --dh-warn:#d97706; --dh-fail:#dc2626; --dh-ok-bg:#dcfce7; --dh-warn-bg:#fef3c7; --dh-fail-bg:#fee2e2; --dh-navy:#0f2744; }
			#avi-dh-wrap .avi-dh-toolbar { display:flex;gap:10px;align-items:center;margin-bottom:20px;flex-wrap:wrap; }
			#avi-dh-wrap .avi-dh-btn { background:var(--dh-navy);color:#fff;border:none;padding:8px 16px;border-radius:6px;cursor:pointer;font-size:13px;font-weight:500; }
			#avi-dh-wrap .avi-dh-btn:hover { background:#1e3a5f; }
			#avi-dh-wrap .avi-dh-btn-green { background:var(--dh-ok) !important; }
			#avi-dh-wrap .avi-dh-btn-sm { background:#f3f4f6;color:#374151;border:none;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:11px;font-weight:500; }
			#avi-dh-wrap .avi-dh-btn-sm:hover { background:#e5e7eb; }
			#avi-dh-wrap .avi-dh-btn-sm.repair { background:var(--dh-ok-bg);color:var(--dh-ok); }
			#avi-dh-wrap .avi-dh-section { background:#fff;border:1px solid #e5e7eb;border-radius:10px;padding:20px;margin-bottom:20px; }
			#avi-dh-wrap .avi-dh-section-title { font-size:14px;font-weight:700;color:var(--dh-navy);margin-bottom:16px;display:flex;align-items:center;gap:8px; }
			#avi-dh-wrap .avi-dh-stats-grid { display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:12px;margin-bottom:4px; }
			#avi-dh-wrap .avi-dh-stat-card { background:#f8fafc;border:1px solid #e5e7eb;border-radius:10px;padding:14px;text-align:center;transition:border-color .2s; }
			#avi-dh-wrap .avi-dh-stat-card .num { font-size:26px;font-weight:700;color:var(--dh-navy); }
			#avi-dh-wrap .avi-dh-stat-card .lbl { font-size:11px;color:#6b7280;margin-top:3px; }
			#avi-dh-wrap .avi-dh-stat-card.s-ok  { border-color:var(--dh-ok);  background:var(--dh-ok-bg);  } #avi-dh-wrap .avi-dh-stat-card.s-ok  .num { color:var(--dh-ok); }
			#avi-dh-wrap .avi-dh-stat-card.s-warn { border-color:var(--dh-warn);background:var(--dh-warn-bg);} #avi-dh-wrap .avi-dh-stat-card.s-warn .num { color:var(--dh-warn); }
			#avi-dh-wrap .avi-dh-stat-card.s-fail { border-color:var(--dh-fail);background:var(--dh-fail-bg);} #avi-dh-wrap .avi-dh-stat-card.s-fail .num { color:var(--dh-fail); }
			#avi-dh-wrap .avi-dh-table { width:100%;border-collapse:collapse;font-size:13px; }
			#avi-dh-wrap .avi-dh-table th { background:var(--dh-navy);color:#fff;padding:10px;text-align:left;font-weight:500; }
			#avi-dh-wrap .avi-dh-table td { padding:9px 10px;border-bottom:1px solid #f3f4f6;vertical-align:middle; }
			#avi-dh-wrap .avi-dh-table tr:hover td { background:#f8fafc; }
			#avi-dh-wrap .avi-dh-checks { display:flex;gap:3px;flex-wrap:wrap; }
			#avi-dh-wrap .ahc-st { width:20px;height:20px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;font-size:11px;font-weight:700;cursor:default;position:relative;flex-shrink:0; }
			#avi-dh-wrap .ahc-st.st-ok   { background:var(--dh-ok-bg);  color:var(--dh-ok); }
			#avi-dh-wrap .ahc-st.st-warn { background:var(--dh-warn-bg);color:var(--dh-warn); }
			#avi-dh-wrap .ahc-st.st-fail { background:var(--dh-fail-bg);color:var(--dh-fail); }
			#avi-dh-wrap .ahc-st[data-tip]:hover::after { content:attr(data-tip);position:absolute;bottom:26px;left:50%;transform:translateX(-50%);background:#1e293b;color:#fff;padding:4px 8px;border-radius:6px;font-size:11px;white-space:nowrap;z-index:9999;pointer-events:none; }
			#avi-dh-wrap .dh-badge { padding:3px 8px;border-radius:999px;font-size:11px;font-weight:600;display:inline-block; }
			#avi-dh-wrap .dh-badge.ok   { background:var(--dh-ok-bg);  color:var(--dh-ok); }
			#avi-dh-wrap .dh-badge.warn { background:var(--dh-warn-bg);color:var(--dh-warn); }
			#avi-dh-wrap .dh-badge.fail { background:var(--dh-fail-bg);color:var(--dh-fail); }
			#avi-dh-wrap .avi-dh-pages { display:flex;gap:6px;align-items:center;margin-top:12px;flex-wrap:wrap; }
			#avi-dh-wrap .avi-dh-pages button { background:#f3f4f6;border:none;padding:5px 10px;border-radius:5px;cursor:pointer;font-size:12px; }
			#avi-dh-wrap .avi-dh-pages button.cur { background:var(--dh-navy);color:#fff; }
			#avi-dh-wrap .avi-dh-alert-row { display:flex;align-items:center;gap:10px;font-size:13px;margin-bottom:8px; }
			#avi-dh-wrap .avi-dh-log-t { width:100%;border-collapse:collapse;font-size:12px; }
			#avi-dh-wrap .avi-dh-log-t th { background:#f8fafc;color:#374151;padding:8px;text-align:left;border-bottom:2px solid #e5e7eb;font-weight:600; }
			#avi-dh-wrap .avi-dh-log-t td { padding:7px 8px;border-bottom:1px solid #f3f4f6; }
			</style>

			<div id="avi-dh-wrap">
			<input type="hidden" id="avi-dh-nonce"
				value="<?php echo esc_attr( wp_create_nonce( 'avision_dh_nonce' ) ); ?>">

				<div class="avi-dh-toolbar">
					<button type="button" class="avi-dh-btn" id="avi-dh-refresh">🔄 Actualiser</button>
					<button type="button" class="avi-dh-btn avi-dh-btn-green" id="avi-dh-repair-all">🔧 Réparer les profils incomplets</button>
					<span id="avi-dh-repair-msg" style="font-size:13px;color:#6b7280;"></span>
					<span style="font-size:12px;color:#9ca3af;margin-left:auto;">Auto-refresh : 5 min</span>
				</div>

				<div id="avi-dh-tabs" style="display:flex;gap:0;margin-bottom:20px;border-bottom:2px solid #e5e7eb;flex-wrap:wrap;">
					<button type="button" class="avi-dh-seg" data-seg="btoc"
						style="padding:10px 20px;border:none;background:none;cursor:pointer;font-size:13px;font-weight:600;color:#0f2744;border-bottom:3px solid #4f46e5;margin-bottom:-2px;">
						👤 B2C Direct
						<span class="avi-dh-seg-count" id="avi-dh-seg-count-btoc"
							style="background:#e0e7ff;color:#4f46e5;padding:2px 7px;border-radius:999px;font-size:11px;margin-left:6px;">—</span>
					</button>
					<button type="button" class="avi-dh-seg" data-seg="btob"
						style="padding:10px 20px;border:none;background:none;cursor:pointer;font-size:13px;font-weight:500;color:#6b7280;border-bottom:3px solid transparent;margin-bottom:-2px;">
						🏢 Clients Agences
						<span class="avi-dh-seg-count" id="avi-dh-seg-count-btob"
							style="background:#f3f4f6;color:#6b7280;padding:2px 7px;border-radius:999px;font-size:11px;margin-left:6px;">—</span>
					</button>
					<button type="button" class="avi-dh-seg" data-seg="all"
						style="padding:10px 20px;border:none;background:none;cursor:pointer;font-size:13px;font-weight:500;color:#6b7280;border-bottom:3px solid transparent;margin-bottom:-2px;">
						🌐 Tous
					</button>
					<button type="button"
						class="avi-dh-seg"
						data-seg="agency"
						id="avi-dh-tab-agency"
						style="padding:10px 20px;border:none;
						background:none;cursor:pointer;
						font-size:13px;font-weight:500;
						color:#6b7280;
						border-bottom:3px solid transparent;
						margin-bottom:-2px;">
						🏭 Agences B2B
						<span class="avi-dh-seg-count"
							id="avi-dh-seg-count-agency"
							style="background:#f3f4f6;color:#6b7280;
							padding:2px 7px;border-radius:999px;
							font-size:11px;margin-left:6px;">—</span>
					</button>
				</div>

				<div class="avi-dh-section" style="padding:12px 20px;margin-bottom:16px;">
					<div style="display:flex;align-items:center;gap:10px;">
						<span style="font-size:18px;color:#9ca3af;">🔍</span>
						<input type="text" id="avi-dh-search"
							placeholder="Rechercher par ID numérique ou nom de business..."
							style="flex:1;border:1px solid #d1d5db;border-radius:8px;padding:9px 14px;font-size:13px;outline:none;">
						<button type="button" id="avi-dh-search-clear"
							class="avi-dh-btn-sm" style="display:none;">✕</button>
						<span id="avi-dh-search-info"
							style="font-size:12px;color:#9ca3af;white-space:nowrap;"></span>
					</div>
				</div>

				<div class="avi-dh-section">
					<div class="avi-dh-section-title">📊 Vue d'ensemble — B2C Direct &amp; clients agence</div>
					<div class="avi-dh-stats-grid">
						<div class="avi-dh-stat-card" id="avi-dh-card-total">
							<div class="num" id="avi-dh-stat-total">—</div>
							<div class="lbl">Profils actifs</div>
						</div>
						<div class="avi-dh-stat-card" id="avi-dh-card-pct">
							<div class="num" id="avi-dh-stat-pct">—</div>
							<div class="lbl">100% complets</div>
						</div>
						<div class="avi-dh-stat-card" id="avi-dh-card-gbp">
							<div class="num" id="avi-dh-stat-gbp">—</div>
							<div class="lbl">GBP manquant</div>
						</div>
						<div class="avi-dh-stat-card" id="avi-dh-card-score">
							<div class="num" id="avi-dh-stat-score">—</div>
							<div class="lbl">Score = 0</div>
						</div>
						<div class="avi-dh-stat-card" id="avi-dh-card-comp">
							<div class="num" id="avi-dh-stat-comp">—</div>
							<div class="lbl">Sans concurrents</div>
						</div>
						<div class="avi-dh-stat-card" id="avi-dh-card-rev">
							<div class="num" id="avi-dh-stat-rev">—</div>
							<div class="lbl">Sans avis/note</div>
						</div>
						<div class="avi-dh-stat-card" id="avi-dh-card-sync">
							<div class="num" id="avi-dh-stat-sync">—</div>
							<div class="lbl">Init en attente</div>
						</div>
						<div class="avi-dh-stat-card" id="avi-dh-card-btob-t" style="border-top:3px solid #6366f1;">
							<div class="num" id="avi-dh-stat-btob-t">—</div>
							<div class="lbl">Fiches clients agence</div>
						</div>
						<div class="avi-dh-stat-card" id="avi-dh-card-btob-ok" style="border-top:3px solid #6366f1;">
							<div class="num" id="avi-dh-stat-btob-ok">—</div>
							<div class="lbl">OAuth GBP connecté</div>
						</div>
						<div class="avi-dh-stat-card" id="avi-dh-card-btob-wait" style="border-top:3px solid #f59e0b;">
							<div class="num" id="avi-dh-stat-btob-wait">—</div>
							<div class="lbl">En attente OAuth client</div>
						</div>
						<div class="avi-dh-stat-card" id="avi-dh-card-btob-inc" style="border-top:3px solid #dc2626;">
							<div class="num" id="avi-dh-stat-btob-inc">—</div>
							<div class="lbl">B2B GBP incomplet</div>
						</div>
					</div>
				</div>

				<div class="avi-dh-section">
					<div class="avi-dh-section-title">
						🛡️ État détaillé par profil
						<span id="avi-dh-error-badge" style="display:none;background:var(--dh-fail-bg);color:var(--dh-fail);padding:2px 8px;border-radius:999px;font-size:11px;font-weight:600;"></span>
					</div>
					<p style="font-size:12px;color:#9ca3af;margin-bottom:12px;">
						Légende checks : 🔗 OAuth GBP (B2B) · 📍 Place ID · 🏷️ Catégorie · ⭐ Réputation · 📊 Score · 👥 Concurrents · 🔑 Mots-clés · 🔄 Init · 🛡️ Guardian
					</p>
					<table class="avi-dh-table">
						<thead>
							<tr>
								<th>ID</th>
								<th>Business</th>
								<th>Langue</th>
								<th>Score</th>
								<th>Score progression</th>
								<th>Niveau</th>
								<th>Créé le</th>
								<th>Checks 📍🏷️⭐📊👥🔑🔄🛡️</th>
								<th>Statut</th>
								<th>Action</th>
							</tr>
						</thead>
						<tbody id="avi-dh-profiles-body">
							<tr><td colspan="10" style="text-align:center;padding:20px;color:#9ca3af;">⏳ Chargement...</td></tr>
						</tbody>
					</table>
					<div class="avi-dh-pages" id="avi-dh-pagination"></div>
				</div>

				<div class="avi-dh-section">
					<div class="avi-dh-section-title">🔧 File d'attente réparation automatique</div>
					<p style="font-size:13px;color:#374151;margin-bottom:10px;">
						<strong id="avi-dh-inc-count">—</strong> profil(s) incomplet(s) détecté(s) —
						Prochain cron : <strong id="avi-dh-next-cron">—</strong>
					</p>
					<ul id="avi-dh-incomplete-list" style="font-size:13px;color:#374151;padding-left:16px;"></ul>
				</div>

				<div class="avi-dh-section">
					<div class="avi-dh-section-title">🔔 Alertes email automatiques</div>
					<div class="avi-dh-alert-row">
						<input type="checkbox" id="avi-dh-chk-24h" <?php checked( $chk_24h ); ?>>
						<label for="avi-dh-chk-24h">Profil incomplet depuis > 24h sans réparation automatique</label>
					</div>
					<div class="avi-dh-alert-row">
						<input type="checkbox" id="avi-dh-chk-48h" <?php checked( $chk_48h ); ?>>
						<label for="avi-dh-chk-48h">Score = 0 depuis plus de 48h</label>
					</div>
					<div class="avi-dh-alert-row">
						<input type="checkbox" id="avi-dh-chk-rev" <?php checked( $chk_rev ); ?>>
						<label for="avi-dh-chk-rev">review_count écrasé à 0 (régression détectée)</label>
					</div>
					<div class="avi-dh-alert-row" style="margin-top:10px;">
						<label for="avi-dh-email" style="min-width:110px;">Destinataire :</label>
						<input type="email" id="avi-dh-email"
							value="<?php echo esc_attr( $alert_email ); ?>"
							style="border:1px solid #d1d5db;border-radius:6px;padding:6px 12px;font-size:13px;width:260px;">
						<button type="button" class="avi-dh-btn" id="avi-dh-save-alerts" style="background:#4f46e5;">💾 Sauvegarder</button>
						<span id="avi-dh-alert-saved" style="font-size:12px;color:var(--dh-ok);display:none;">✅ Sauvegardé</span>
					</div>
				</div>

				<div class="avi-dh-section">
					<div class="avi-dh-section-title">📋 Historique réparations (30 dernières)</div>
					<table class="avi-dh-log-t">
						<thead>
							<tr><th>Date</th><th>Profil</th><th>Éléments réparés</th><th>Résultat</th></tr>
						</thead>
						<tbody id="avi-dh-log-body">
							<tr><td colspan="4" style="text-align:center;padding:12px;color:#9ca3af;">Chargement...</td></tr>
						</tbody>
					</table>
				</div>

			</div>

			<script>
			(function($) {
				var nonce    = <?php echo wp_json_encode( $nonce ); ?>;
				var ajaxurl  = <?php echo wp_json_encode( $ajax_url ); ?>;
				var page         = 1;
				var perPage      = 20;
				var total        = 0;
				var currentSeg   = 'btoc';
				var searchTimer  = null;

				function loadStats() {
					$.post(ajaxurl, { action:'avision_dh_get_stats', nonce:nonce }, function(r) {
						if (!r.success) return;
						var d = r.data;
						$('#avi-dh-stat-total').text(d.total);
						$('#avi-dh-stat-pct').text(d.pct_complete + '%');
						$('#avi-dh-stat-gbp').text(d.missing_gbp);
						$('#avi-dh-stat-score').text(d.score_zero);
						$('#avi-dh-stat-comp').text(d.no_competitors);
						$('#avi-dh-stat-rev').text(d.no_reviews);
						$('#avi-dh-stat-sync').text(d.pending_sync);
						$('#avi-dh-stat-btob-t').text(d.btob_total || 0);
						$('#avi-dh-stat-btob-ok').text(d.btob_with_location || 0);
						$('#avi-dh-stat-btob-wait').text(d.btob_pending_oauth || 0);
						$('#avi-dh-stat-btob-inc').text(d.btob_incomplete || 0);
						$('#avi-dh-seg-count-btoc').text(d.total || 0);
						$('#avi-dh-seg-count-btob').text(d.btob_total || 0);
						$('#avi-dh-card-pct').attr('class','avi-dh-stat-card ' + (d.pct_complete>=90?'s-ok':d.pct_complete>=70?'s-warn':'s-fail'));
						var statMap = { gbp:'missing_gbp', score:'score_zero', comp:'no_competitors', rev:'no_reviews', sync:'pending_sync' };
						['gbp','score','comp','rev','sync'].forEach(function(k) {
							var v = d[statMap[k]];
							$('#avi-dh-card-'+k).attr('class','avi-dh-stat-card '+(v>0?'s-fail':'s-ok'));
						});
						$('#avi-dh-card-btob-wait').attr('class','avi-dh-stat-card '+((d.btob_pending_oauth||0)>0?'s-warn':'s-ok'));
						$('#avi-dh-card-btob-inc').attr('class','avi-dh-stat-card '+((d.btob_incomplete||0)>0?'s-fail':'s-ok'));
					});
				}

				function updateTableHeaders(seg) {
					if (seg === 'agency') {
						$('#avi-dh-wrap thead tr').html(
							'<th>ID</th>'
							+ '<th>Agence</th>'
							+ '<th>Email admin</th>'
							+ '<th>Fiches</th>'
							+ '<th>Slug</th>'
							+ '<th>Créée le</th>'
							+ '<th>Actions</th>'
						);
					} else {
						$('#avi-dh-wrap thead tr').html(
							'<th>ID</th>'
							+ '<th>Business</th>'
							+ '<th>Langue</th>'
							+ '<th>Score</th>'
							+ '<th>Score progression</th>'
							+ '<th>Niveau</th>'
							+ '<th>Créé le</th>'
							+ '<th>Checks</th>'
							+ '<th>Statut</th>'
							+ '<th>Actions</th>'
						);
					}
				}

				function loadAgencies() {
					$('#avi-dh-profiles-body').html(
						'<tr><td colspan="7" style="text-align:'
						+ 'center;padding:20px;color:#9ca3af;">'
						+ 'Chargement...</td></tr>');

					$.post(ajaxurl, {
						action: 'avision_dh_get_agencies',
						nonce:  $('#avi-dh-nonce').val()
					}, function(res) {
						if (!res.success) return;

						var agencies = res.data.agencies || [];
						var html = '';

						// Mettre à jour compteur onglet
						$('#avi-dh-seg-count-agency')
							.text(agencies.length);

						if (!agencies.length) {
							html = '<tr><td colspan="7" '
								+ 'style="text-align:center;'
								+ 'padding:20px;color:#9ca3af;">'
								+ 'Aucune agence</td></tr>';
						} else {
							agencies.forEach(function(ag) {
								var bundleBadge = ag.bundle !== '—'
									? '<span style="font-size:10px;'
										+ 'background:#dbeafe;color:#1e40af;'
										+ 'padding:2px 6px;border-radius:4px;'
										+ 'margin-left:4px;">'
										+ escHtml(ag.bundle) + '</span>'
									: '';

								var loginBtn = ag.login_url
									? '<a href="' + escAttr(ag.login_url)
										+ '" target="_blank" rel="noopener" '
										+ 'style="background:rgba(139,92,246,0.15);'
										+ 'color:#7c3aed;border:1px solid '
										+ 'rgba(139,92,246,0.3);padding:4px 10px;'
										+ 'border-radius:4px;font-size:11px;'
										+ 'text-decoration:none;display:'
										+ 'inline-block;" '
										+ 'title="Login as agency admin">'
										+ '🔑 Login as</a> '
									: '';

								html += '<tr>'
									+ '<td style="font-weight:600;">'
										+ '#' + ag.id + '</td>'
									+ '<td>' + escHtml(ag.name)
										+ bundleBadge + '</td>'
									+ '<td style="font-size:12px;'
										+ 'color:#6b7280;">'
										+ escHtml(ag.email) + '</td>'
									+ '<td style="text-align:center;">'
										+ '<strong>' + ag.profile_count
										+ '</strong> fiches</td>'
									+ '<td style="font-family:monospace;'
										+ 'font-size:11px;color:#9ca3af;">'
										+ escHtml(ag.slug) + '</td>'
									+ '<td style="font-size:11px;'
										+ 'color:#9ca3af;">'
										+ escHtml((ag.created_at||'')
											.substring(0,10)) + '</td>'
									+ '<td>' + loginBtn + '</td>'
									+ '</tr>';
							});
						}

						$('#avi-dh-profiles-body').html(html);

						// Cacher pagination (non applicable)
						$('.avi-dh-pages').hide();
					});
				}

				function loadProfiles(p) {
					// Segment agences — handler dédié
					if (currentSeg === 'agency') {
						loadAgencies();
						return;
					}

					page = p || 1;
					var q = $('#avi-dh-search').val().trim();
					$('#avi-dh-profiles-body').html('<tr><td colspan="10" style="text-align:center;padding:20px;color:#9ca3af;">⏳ Chargement...</td></tr>');
					$.post(ajaxurl, {
						action:  'avision_dh_get_profile_checks',
						nonce:   nonce,
						limit:   perPage,
						offset:  (page-1)*perPage,
						segment: currentSeg,
						search:  q
					}, function(r) {
						if (!r.success) return;
						total = r.data.total;
						var profs = r.data.profiles;
						var html = '';
						var errCount = 0;
						if (q) {
							$('#avi-dh-search-info').text(total + ' résultat(s)');
						} else {
							$('#avi-dh-search-info').text('');
						}
						if (!profs.length) {
							html = '<tr><td colspan="10" style="text-align:center;padding:20px;color:#9ca3af;">Aucun profil</td></tr>';
						} else {
							profs.forEach(function(p) {
								if (p.status==='fail') errCount++;
								var icons = ['place_id','category','reputation','score','competitors','keywords','first_sync','guardian'];
								if (p.checks && p.checks.oauth_gbp) {
									icons = ['oauth_gbp'].concat(icons);
								}
								var checks_html = '<div class="avi-dh-checks">';
								icons.forEach(function(k) {
									var c = p.checks[k];
									if (!c) return;
									var cls = c.ok ? 'st-ok' : (c.warn ? 'st-warn' : 'st-fail');
									var ic  = c.ok ? '✓' : (c.warn ? '⚠' : '✗');
									checks_html += '<span class="ahc-st '+cls+'" data-tip="'+escAttr(c.label+': '+c.val)+'">'+ic+'</span>';
								});
								checks_html += '</div>';
								var badge = p.status==='ok'
									? '<span class="dh-badge ok">✓ Complet</span>'
									: p.status==='warn'
									? '<span class="dh-badge warn">⚠ Attention</span>'
									: '<span class="dh-badge fail">✗ Incomplet</span>';
								var viewLabel = p.is_report_view ? '📊 Rapport' : '👁 Voir';
								var viewTitle = p.is_report_view ? 'Rapport client lecture seule' : 'Voir le dashboard client';
								var viewBtn = p.dashboard_url
									? ' <a href="' + p.dashboard_url + '" target="_blank" rel="noopener" '
									  + 'style="background:#ede9fe;color:#4f46e5;border:none;padding:4px 10px;'
									  + 'border-radius:4px;cursor:pointer;font-size:11px;text-decoration:none;'
									  + 'display:inline-block;" title="' + escAttr(viewTitle) + '">' + viewLabel + '</a>'
									: '';
								var repairBtn = p.status !== 'ok'
									? '<button type="button" class="avi-dh-btn-sm repair dh-repair-one" '
									  + 'data-id="' + p.id + '">🔧 Réparer</button>'
									: '<span style="color:#9ca3af;font-size:11px;">✓</span>';
								var loginBtn = '';
								if (p.user_id && !p.is_btob) {
									loginBtn = '<button type="button" '
										+ 'class="avi-dh-btn-sm avi-dh-login-as" '
										+ 'data-uid="' + p.user_id + '" '
										+ 'data-url="' + escAttr(p.dashboard_url||'') + '" '
										+ 'style="background:rgba(139,92,246,0.15);'
										+ 'color:#7c3aed;border:1px solid '
										+ 'rgba(139,92,246,0.3);margin-left:3px;" '
										+ 'title="Login as this user">🔑</button>';
								}
								var action = repairBtn + viewBtn + loginBtn;
								var agencyBadge = p.agency_name
									? '<span style="font-size:10px;background:#ede9fe;color:#6d28d9;padding:2px 6px;border-radius:4px;margin-left:4px;">🏢 ' + escHtml(p.agency_name) + '</span>'
									: '';
								html += '<tr>'
									+'<td style="font-weight:600;">#'+p.id+'</td>'
									+'<td>'+escHtml(p.business_name)+agencyBadge+'</td>'
									+'<td>'+escHtml((p.language||'en').toUpperCase())+'</td>'
									+'<td>'+p.score+'/100</td>'
									+'<td>'+(p.progression_score||p.score||0)+'/100</td>'
									+'<td>'+(p.current_level||1)+'</td>'
									+'<td>'+escHtml((p.created_at||'').substring(0,10))+'</td>'
									+'<td>'+checks_html+'</td>'
									+'<td>'+badge+'</td>'
									+'<td>'+action+'</td>'
									+'</tr>';
							});
						}
						$('#avi-dh-profiles-body').html(html);
						renderPages();
						if (errCount > 0) {
							$('#avi-dh-error-badge').text(errCount+' incomplet'+(errCount>1?'s':'')).show();
						} else {
							$('#avi-dh-error-badge').hide();
						}
					});
				}

				function renderPages() {
					var pages = Math.ceil(total/perPage);
					if (pages<=1) { $('#avi-dh-pagination').html(''); return; }
					var html = '';
					if (page>1) html += '<button type="button" data-p="'+(page-1)+'">‹</button>';
					for (var i=1;i<=pages;i++) html += '<button type="button" class="'+(i===page?'cur':'')+'" data-p="'+i+'">'+i+'</button>';
					if (page<pages) html += '<button type="button" data-p="'+(page+1)+'">›</button>';
					html += '<span style="font-size:12px;color:#9ca3af;margin-left:6px;">'+total+' profils</span>';
					$('#avi-dh-pagination').html(html);
				}

				function loadIncomplete() {
					$.post(ajaxurl, { action:'avision_dh_get_incomplete', nonce:nonce }, function(r) {
						if (!r.success) return;
						var d = r.data;
						$('#avi-dh-inc-count').text(d.count);
						var next = d.next_cron_in_seconds!==null ? 'dans '+Math.round(d.next_cron_in_seconds/60)+' min' : '—';
						$('#avi-dh-next-cron').text(next);
						var html = '';
						if (!d.incomplete.length) {
							html = '<li style="list-style:none;color:var(--dh-ok);">✅ Aucun profil incomplet</li>';
						} else {
							d.incomplete.forEach(function(p) {
								var name = p.business_name ? escHtml(p.business_name) : ('Profil #' + p.id);
								html += '<li>Profil #'+p.id+' — '+name+'</li>';
							});
						}
						$('#avi-dh-incomplete-list').html(html);
					});
				}

				function loadLog() {
					$.post(ajaxurl, { action:'avision_dh_get_repair_log', nonce:nonce }, function(r) {
						if (!r.success) return;
						var log = r.data.log.slice().reverse();
						var html = '';
						if (!log.length) {
							html = '<tr><td colspan="4" style="text-align:center;padding:12px;color:#9ca3af;">Aucune réparation enregistrée</td></tr>';
						} else {
							log.forEach(function(e) {
								html += '<tr>'
									+'<td>'+escHtml(e.date||'—')+'</td>'
									+'<td>#'+e.profile_id+'</td>'
									+'<td>'+escHtml(e.repaired||'—')+'</td>'
									+'<td><span class="dh-badge '+(e.success?'ok':'fail')+'">'+(e.success?'✓ OK':'✗ Échec')+'</span></td>'
									+'</tr>';
							});
						}
						$('#avi-dh-log-body').html(html);
					});
				}

				function escHtml(s) {
					return String(s||'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
				}
				function escAttr(s) {
					return escHtml(s).replace(/'/g,'&#39;');
				}
				function repairProfile(id, btn) {
					btn.prop('disabled',true).text('⏳...');
					$.post(ajaxurl, { action:'avision_dh_repair_now', nonce:nonce, profile_id:id }, function(r) {
						btn.prop('disabled',false).text('🔧 Réparer');
						if (r.success) {
							$('#avi-dh-repair-msg').text(r.data.message).css('color','var(--dh-ok)');
							refreshAll();
						}
					});
				}

				function refreshAll() {
					loadStats(); loadProfiles(page); loadIncomplete(); loadLog();
				}

				if (document.readyState==='loading') {
					document.addEventListener('DOMContentLoaded', refreshAll);
				} else {
					refreshAll();
				}

				$('#avi-dh-refresh').on('click', refreshAll);

				$(document).on('click', '.avi-dh-seg', function() {
					$('.avi-dh-seg').css({
						'font-weight':'500','color':'#6b7280','border-bottom':'3px solid transparent'
					});
					$(this).css({
						'font-weight':'600','color':'#0f2744','border-bottom':'3px solid #4f46e5'
					});
					$('.avi-dh-seg-count').css({'background':'#f3f4f6','color':'#6b7280'});
					$(this).find('.avi-dh-seg-count').css({'background':'#e0e7ff','color':'#4f46e5'});
					var seg = $(this).data('seg');
					currentSeg = seg;
					page = 1;
					$('#avi-dh-search').val('');
					$('#avi-dh-search-clear').hide();
					$('#avi-dh-search-info').text('');
					updateTableHeaders(seg);
					if (seg !== 'agency') {
						$('.avi-dh-pages').show();
					}
					loadProfiles(1);
				});

				$('#avi-dh-search').on('input', function() {
					var q = $(this).val().trim();
					$('#avi-dh-search-clear').toggle(q.length > 0);
					clearTimeout(searchTimer);
					searchTimer = setTimeout(function() { page = 1; loadProfiles(1); }, 350);
				});
				$('#avi-dh-search-clear').on('click', function() {
					$('#avi-dh-search').val('').trigger('input');
				});

				$('#avi-dh-repair-all').on('click', function() {
					if (!confirm('Lancer la réparation sur tous les profils incomplets (max 5) ?')) return;
					var btn = $(this);
					btn.prop('disabled',true).text('⏳ Réparation...');
					$.post(ajaxurl, { action:'avision_dh_repair_now', nonce:nonce }, function(r) {
						btn.prop('disabled',false).text('🔧 Réparer les profils incomplets');
						if (r.success) {
							$('#avi-dh-repair-msg').text(r.data.message).css('color','var(--dh-ok)');
							refreshAll();
						}
					});
				});

				$(document).on('click', '.dh-repair-one', function() {
					repairProfile($(this).data('id'), $(this));
				});

				$(document).on('click', '.avi-dh-login-as',
					function() {
						var uid = $(this).data('uid');
						var url = $(this).data('url');
						if (!confirm(
							'Login as user #' + uid + '?\n'
							+ 'A new tab will open with their dashboard.\n'
							+ 'Use "Switch back to Admin" to return.'))
							return;
						var btn = $(this);
						btn.prop('disabled', true).text('...');
						$.post(ajaxurl, {
							action: 'avision_dh_login_as_user',
							nonce: $('#avi-dh-nonce').val(),
							user_id: uid,
							dashboard_url: url
						}, function(res) {
							if (res.success && res.data.login_url) {
								window.open(res.data.login_url, '_blank');
							} else {
								alert(res.data?.message || 'Error');
							}
							btn.prop('disabled', false).text('🔑');
						});
					});

				$(document).on('click', '#avi-dh-pagination button', function() {
					var p = parseInt($(this).data('p'), 10);
					if (p) loadProfiles(p);
				});

				$('#avi-dh-save-alerts').on('click', function() {
					$.post(ajaxurl, {
						action: 'avision_dh_save_alerts', nonce: nonce,
						alert_incomplete_24h: $('#avi-dh-chk-24h').is(':checked') ? 1 : 0,
						alert_score_zero_48h: $('#avi-dh-chk-48h').is(':checked') ? 1 : 0,
						alert_review_erased:  $('#avi-dh-chk-rev').is(':checked') ? 1 : 0,
						alert_email: $('#avi-dh-email').val(),
					}, function(r) {
						if (r.success) {
							$('#avi-dh-alert-saved').show();
							setTimeout(function(){ $('#avi-dh-alert-saved').hide(); }, 3000);
						}
					});
				});

				setInterval(refreshAll, 300000);

			})(jQuery);
			</script>
			<?php
		}
	}
}
