<?php

if (!defined('ABSPATH')) {
	exit;
}

/** Remote sync via license.icomm.systems (same REST base as other icomm plugins). */
final class IComm_SM_Remote {

	public static function api_base(): string {
		return rtrim((string) apply_filters('icomm_sm_remote_api', IComm_SM_License::endpoint_base()), '/');
	}

	private static function site_host(): string {
		$h = (string) wp_parse_url(home_url('/'), PHP_URL_HOST);
		return trim($h);
	}

	public static function ensure_link_token(): string {
		$key = (string) get_option('icomm_sm_link_token', '');
		if ($key === '') {
			$key = wp_generate_password(32, false, false);
			update_option('icomm_sm_link_token', $key, false);
		}
		return $key;
	}

	/**
	 * Local admin bounce: mints a short-lived connect code (POST) so the permanent
	 * link_token never appears in browser URLs / access logs.
	 */
	public static function connect_url(string $service): string {
		$service = sanitize_key($service);
		return wp_nonce_url(
			add_query_arg(
				[
					'action' => 'icomm_sm_connect',
					'service' => $service,
				],
				admin_url('admin-post.php')
			),
			'icomm_sm_connect_' . $service
		);
	}

	/**
	 * Exchange permanent link_token (POST body) for a one-time connect code, then
	 * return the Google OAuth start URL on the license server.
	 *
	 * @return string Absolute URL or empty string on failure.
	 */
	public static function mint_connect_redirect(string $service): string {
		$service = sanitize_key($service);
		if (!in_array($service, ['search', 'stats', 'gsc', 'ga4'], true)) {
			return '';
		}
		$body = [
			'link_token' => self::ensure_link_token(),
			'site' => self::site_host(),
			'service' => $service,
			'return' => admin_url('admin.php?page=icomm-metrix-connections'),
		];
		$res = wp_remote_post(self::api_base() . '/sm-connect-ticket', [
			'timeout' => 20,
			'headers' => ['Content-Type' => 'application/json; charset=UTF-8'],
			'body' => wp_json_encode($body),
		]);
		if (is_wp_error($res)) {
			return '';
		}
		$data = json_decode((string) wp_remote_retrieve_body($res), true);
		$code = is_array($data) ? sanitize_text_field((string) ($data['connect'] ?? '')) : '';
		if ($code === '' || empty($data['ok'])) {
			return '';
		}
		return add_query_arg(['connect' => $code], self::api_base() . '/sm-link');
	}

	public static function disconnect(string $service): array {
		return self::post('/sm-disconnect', ['service' => sanitize_key($service)]);
	}

	public static function save_property(string $gsc = '', string $ga4 = ''): array {
		$body = [];
		if ($gsc !== '') {
			$body['gsc_property'] = $gsc;
		}
		if ($ga4 !== '') {
			$body['ga4_property_id'] = $ga4;
		}
		return self::post('/sm-property', $body);
	}

	public static function catalog(string $type): array {
		return self::post('/sm-catalog', ['type' => sanitize_key($type)]);
	}

	public static function sync(int $days, bool $with_ga4): array {
		return self::post('/sm-sync', [
			'days' => $days,
			'ga4' => $with_ga4 ? 1 : 0,
		]);
	}

	/**
	 * @param string[] $urls
	 * @return array<string, mixed>
	 */
	public static function inspect_urls(array $urls, int $limit = 10): array {
		$clean = [];
		foreach ($urls as $u) {
			$u = trim((string) $u);
			if ($u !== '' && filter_var($u, FILTER_VALIDATE_URL)) {
				$clean[] = $u;
			}
		}
		$clean = array_values(array_unique($clean));
		$limit = max(1, min(30, (int) $limit));
		$clean = array_slice($clean, 0, $limit);
		if ($clean === []) {
			return ['ok' => false, 'error' => 'no_urls'];
		}
		// URL Inspection is sequential on the license server; allow enough time for a batch.
		return self::post('/sm-inspect', [
			'urls' => $clean,
			'lang' => 'he-IL',
		], 120);
	}

	public static function apply_sync_to_db(array $payload): array {
		$gsc_ok = true;
		$ga4_ok = true;

		if (!empty($payload['gsc']) && is_array($payload['gsc'])) {
			$gsc = $payload['gsc'];
			if (empty($gsc['ok'])) {
				$gsc_ok = false;
				IComm_SM_Settings::save(array_merge(IComm_SM_Settings::get(), [
					'gsc_last_error' => self::friendly_google_error((string) ($gsc['error'] ?? 'sync_failed'), 'gsc'),
				]));
			} else {
				$start = (string) ($gsc['start'] ?? '');
				$end = (string) ($gsc['end'] ?? '');
				$detail_start = (string) ($gsc['detail_start'] ?? '');
				if ($detail_start === '') {
					$detail_start = $start;
				}
				$stamp = $end !== '' ? $end : gmdate('Y-m-d');
				if ($start !== '' && $end !== '') {
					IComm_SM_DB::purge_range($start, $end, 'gsc');
				}
				// Query/page detail rows are only for the shorter detail window.
				if ($detail_start !== '' && $end !== '') {
					IComm_SM_DB::purge_queries_range($detail_start, $end);
					IComm_SM_DB::purge_pages_range($detail_start, $end);
					IComm_SM_DB::purge_query_pages_range($detail_start, $end);
				}
				foreach (($gsc['daily'] ?? []) as $row) {
					if (!is_array($row) || empty($row['keys'][0])) {
						continue;
					}
					$date = (string) $row['keys'][0];
					IComm_SM_DB::upsert_daily($date, 'gsc', 'clicks', (float) ($row['clicks'] ?? 0));
					IComm_SM_DB::upsert_daily($date, 'gsc', 'impressions', (float) ($row['impressions'] ?? 0));
					if (isset($row['position'])) {
						IComm_SM_DB::upsert_daily($date, 'gsc', 'position', (float) $row['position']);
					}
				}
				foreach (($gsc['queries'] ?? []) as $row) {
					if (!is_array($row) || empty($row['keys'][0])) {
						continue;
					}
					IComm_SM_DB::upsert_query(
						$stamp,
						(string) $row['keys'][0],
						(int) ($row['clicks'] ?? 0),
						(int) ($row['impressions'] ?? 0),
						(float) ($row['position'] ?? 0),
						(float) ($row['ctr'] ?? 0)
					);
				}
				foreach (($gsc['pages'] ?? []) as $row) {
					if (!is_array($row) || empty($row['keys'][0])) {
						continue;
					}
					IComm_SM_DB::upsert_page(
						$stamp,
						(string) $row['keys'][0],
						(int) ($row['clicks'] ?? 0),
						(int) ($row['impressions'] ?? 0),
						(float) ($row['position'] ?? 0),
						(float) ($row['ctr'] ?? 0)
					);
				}
				foreach (($gsc['query_pages'] ?? []) as $row) {
					if (!is_array($row) || empty($row['keys'][0]) || empty($row['keys'][1])) {
						continue;
					}
					IComm_SM_DB::upsert_query_page(
						$stamp,
						(string) $row['keys'][0],
						(string) $row['keys'][1],
						(int) ($row['clicks'] ?? 0),
						(int) ($row['impressions'] ?? 0),
						(float) ($row['position'] ?? 0),
						(float) ($row['ctr'] ?? 0)
					);
				}
				$s = IComm_SM_Settings::get();
				$s['gsc_last_sync_at'] = current_time('timestamp');
				$s['gsc_last_error'] = '';
				IComm_SM_Settings::save($s);
				if (class_exists('IComm_SM_Recommendations')) {
					IComm_SM_Recommendations::clear_cache();
				}
			}
		}

		if (!empty($payload['ga4']) && is_array($payload['ga4'])) {
			$ga4 = $payload['ga4'];
			if (empty($ga4['ok'])) {
				$ga4_ok = false;
				IComm_SM_Settings::save(array_merge(IComm_SM_Settings::get(), [
					'ga4_last_error' => self::friendly_google_error((string) ($ga4['error'] ?? 'sync_failed'), 'ga4'),
				]));
			} else {
				$start = (string) ($ga4['start'] ?? '');
				$end = (string) ($ga4['end'] ?? '');
				if ($start !== '' && $end !== '') {
					IComm_SM_DB::purge_range($start, $end, 'ga4');
				}
				foreach (($ga4['rows'] ?? []) as $row) {
					if (!is_array($row)) {
						continue;
					}
					$date_raw = (string) ($row['dimensionValues'][0]['value'] ?? '');
					if ($date_raw === '' || strlen($date_raw) !== 8) {
						continue;
					}
					$date = substr($date_raw, 0, 4) . '-' . substr($date_raw, 4, 2) . '-' . substr($date_raw, 6, 2);
					IComm_SM_DB::upsert_daily($date, 'ga4', 'sessions', (float) ($row['metricValues'][0]['value'] ?? 0));
					IComm_SM_DB::upsert_daily($date, 'ga4', 'users', (float) ($row['metricValues'][1]['value'] ?? 0));
				}
				if ($start !== '' && $end !== '') {
					$channel_rows = [];
					foreach (($ga4['channels'] ?? []) as $row) {
						if (!is_array($row)) {
							continue;
						}
						$key = (string) ($row['dimensionValues'][0]['value'] ?? '');
						if ($key === '') {
							continue;
						}
						$bounce = (float) ($row['metricValues'][2]['value'] ?? 0);
						// GA4 bounceRate is usually 0-1; normalize to percent when needed.
						if ($bounce > 0 && $bounce <= 1) {
							$bounce *= 100;
						}
						$channel_rows[] = [
							'dim_key' => $key,
							'dim_label' => self::ga4_channel_label_he($key),
							'sessions' => (int) round((float) ($row['metricValues'][0]['value'] ?? 0)),
							'users' => (int) round((float) ($row['metricValues'][1]['value'] ?? 0)),
							'bounce_rate' => round($bounce, 2),
						];
					}
					IComm_SM_DB::replace_ga4_breakdown($start, $end, 'channel', $channel_rows);

					$device_rows = [];
					foreach (($ga4['devices'] ?? []) as $row) {
						if (!is_array($row)) {
							continue;
						}
						$cat = (string) ($row['dimensionValues'][0]['value'] ?? '');
						$model = trim((string) ($row['dimensionValues'][1]['value'] ?? ''));
						if ($model === '' || strcasecmp($model, '(not set)') === 0) {
							$key = $cat !== '' ? $cat : 'unknown';
							$label = self::ga4_device_label_he($cat);
						} else {
							$key = $cat . '|' . $model;
							$label = $model;
						}
						$bounce = (float) ($row['metricValues'][2]['value'] ?? 0);
						if ($bounce > 0 && $bounce <= 1) {
							$bounce *= 100;
						}
						$device_rows[] = [
							'dim_key' => $key,
							'dim_label' => $label,
							'sessions' => (int) round((float) ($row['metricValues'][0]['value'] ?? 0)),
							'users' => (int) round((float) ($row['metricValues'][1]['value'] ?? 0)),
							'bounce_rate' => round($bounce, 2),
						];
					}
					IComm_SM_DB::replace_ga4_breakdown($start, $end, 'device', $device_rows);
				}
				$s = IComm_SM_Settings::get();
				$s['ga4_last_sync_at'] = current_time('timestamp');
				$s['ga4_last_error'] = '';
				IComm_SM_Settings::save($s);
			}
		}

		return ['ok' => $gsc_ok && $ga4_ok];
	}

	public static function ga4_channel_label_he(string $channel): string {
		$map = [
			'Direct' => 'תנועה ישירה',
			'Paid Search' => 'תנועה ממומנת',
			'Paid Social' => 'תנועה ממומנת (רשתות)',
			'Paid Other' => 'תנועה ממומנת',
			'Display' => 'תנועה ממומנת (תצוגה)',
			'Organic Search' => 'תנועה אורגנית',
			'Referral' => 'תנועת הפניה',
			'Organic Social' => 'רשתות חברתיות',
			'Email' => 'אימייל',
			'Affiliates' => 'שותפים',
			'Unassigned' => 'לא משויך',
			'Cross-network' => 'רשתות צולבות',
		];
		return $map[$channel] ?? $channel;
	}

	public static function ga4_device_label_he(string $category): string {
		$map = [
			'desktop' => 'מחשב שולחני',
			'mobile' => 'נייד',
			'tablet' => 'טאבלט',
			'smart tv' => 'טלוויזיה חכמה',
		];
		$low = strtolower(trim($category));
		return $map[$low] ?? ($category !== '' ? $category : 'לא ידוע');
	}

	/** Human-readable Google / sync errors (Hebrew). */
	public static function friendly_google_error(string $raw, string $service = 'gsc'): string {
		$raw = trim($raw);
		$low = mb_strtolower($raw);
		if ($raw === '' || $raw === 'sync_failed') {
			return $service === 'ga4'
				? __('סנכרון GA4 נכשל. בדקו חיבור ונסו שוב.', 'icomm-metrix')
				: __('סנכרון GSC נכשל. בדקו חיבור ונסו שוב.', 'icomm-metrix');
		}
		if (strpos($low, 'insufficient') !== false && strpos($low, 'scope') !== false) {
			return $service === 'ga4'
				? __('חסרות הרשאות Analytics. לחצו «חבר אנליטיקס» מחדש (בלי לנתק את GSC).', 'icomm-metrix')
				: __('חסרות הרשאות Search Console, לרוב כי חובר רק Analytics אחרי GSC. לחצו «חבר נתוני חיפוש» מחדש ואז סנכרנו.', 'icomm-metrix');
		}
		if ($raw === 'gsc_not_connected' || $raw === 'not_connected') {
			return __('GSC לא מחובר. חברו «נתוני חיפוש» ואז סנכרנו.', 'icomm-metrix');
		}
		if ($raw === 'ga4_not_connected') {
			return __('GA4 לא מחובר. חברו אנליטיקס ואז סנכרנו.', 'icomm-metrix');
		}
		if ($raw === 'property_domain_mismatch') {
			return __('נכס GSC שייך לדומיין אחר. בחרו Property של האתר הנוכחי.', 'icomm-metrix');
		}
		if ($raw === 'missing_refresh_token' || $raw === 'refresh_failed') {
			return __('פג תוקף החיבור לגוגל. התחברו מחדש לשירות הרלוונטי.', 'icomm-metrix');
		}
		return $raw;
	}

	/**
	 * @param array<string, mixed> $extra
	 * @return array<string, mixed>
	 */
	private static function post(string $path, array $extra = [], int $timeout = 45): array {
		$timeout = max(15, min(180, (int) $timeout));
		$body = array_merge([
			'link_token' => self::ensure_link_token(),
			// NOTE: avoid `site_url` key to reduce false-positive WAF blocks.
			'site' => self::site_host(),
		], $extra);
		$res = wp_remote_post(self::api_base() . $path, [
			'timeout' => $timeout,
			'headers' => ['Content-Type' => 'application/json; charset=UTF-8'],
			'body' => wp_json_encode($body),
		]);
		if (is_wp_error($res)) {
			return ['ok' => false, 'error' => $res->get_error_message()];
		}
		$data = json_decode((string) wp_remote_retrieve_body($res), true);
		return is_array($data) ? $data : ['ok' => false, 'error' => 'bad_response'];
	}
}
