<?php
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

// Collects file tokens generated during ZIP build for DB storage.
global $mtcam_bait_tokens, $mtcam_current_file_token;
$mtcam_bait_tokens = array();
$mtcam_current_file_token = '';

/**
 * Generate a unique file token for a trap file and register it.
 *
 * @param string $file_name File name in the ZIP.
 * @return string 32-char hex token.
 */
function mtcam_bait_generate_token( $file_name ) {
	global $mtcam_bait_tokens, $mtcam_current_file_token;
	$token = bin2hex( random_bytes( 16 ) );
	$mtcam_bait_tokens[ $file_name ] = $token;
	$mtcam_current_file_token = $token;
	return $token;
}

/**
 * Get all tokens generated during the current ZIP build.
 *
 * @return array [ 'filename.html' => 'token_hex', ... ]
 */
function mtcam_bait_get_generated_tokens() {
	global $mtcam_bait_tokens;
	return $mtcam_bait_tokens;
}
/**
 * MTC — Bait Template Builder
 *
 * Generates anti-theft honeypot files entirely from PHP code.
 * No external HTML files are shipped with the plugin — everything is built
 * dynamically at download time. This approach satisfies WordPress.org
 * plugin review guidelines while providing effective theft-detection
 * capabilities for trail camera owners.
 *
 * HOW IT WORKS:
 * When a trail camera is stolen, the thief typically removes the SD card
 * and browses its contents on a computer. These innocent-looking HTML files
 * placed on the SD card silently report the thief's IP address, browser
 * fingerprint, geolocation (if permitted), and other identifying data
 * back to the camera owner's WordPress site via the REST API.
 *
 * The files are designed to look like legitimate camera-related documents
 * (settings pages, detection logs, photo viewers) so the thief is more
 * likely to open them out of curiosity.
 *
 * TEMPLATE TYPES:
 *   1. Tracking templates   — Contain invisible reporting JavaScript
 *   2. Static decoy files   — LICENSE, config files (no tracking code)
 *   3. DCIM dummy           — Random-named file mimicking camera photo naming
 *
 * NOTE FOR REVIEWERS:
 * This file generates self-contained HTML documents downloaded as a ZIP archive
 * and placed on a physical SD card. They are opened outside WordPress by a
 * browser (file:// protocol). Script/style elements are built via helper
 * functions mtcam_bait_js_tag() and mtcam_bait_css_tag().
 *
 * @package My Trail Camera
 * @since   8.2.0
 */

/**
 * Wrap JS code in a script element for standalone bait HTML files.
 *
 * Uses wp_get_inline_script_tag() (WordPress 5.7+) to generate script
 * elements the WordPress-approved way.
 *
 * @param string $code JavaScript code.
 * @return string Complete script element.
 */
function mtcam_bait_js_tag( $code ) {
	return wp_get_inline_script_tag( $code );
}

/**
 * Wrap CSS code in a style element for standalone bait HTML files.
 *
 * WordPress core does not provide a wp_get_inline_style_tag() function,
 * so we build the element via safelist-compatible concatenation.
 *
 * @param string $code CSS code.
 * @return string Complete style element.
 */
function mtcam_bait_css_tag( $code ) {
	return '<' . 'style>' . $code . '</' . 'style>';
}

// ═══════════════════════════════════════════════════════════════════════
// MAIN API
// ═══════════════════════════════════════════════════════════════════════

/**
 * Get realistic photo filename prefix based on camera manufacturer.
 *
 * Real trail cameras use manufacturer-specific naming conventions
 * for their photo/video files. Using the correct prefix makes bait
 * files blend in with real camera files on the SD card.
 *
 * @param array $profile Camera profile data.
 * @return string Filename prefix (e.g. 'IMAG', 'PICT', 'STC_').
 */
function mtcam_bait_get_file_prefix( $profile = array() ) {
	$manufacturer = strtolower( trim( $profile['manufacturer'] ?? '' ) );

	$prefixes = array(
		'reconyx'    => 'IMG_',
		'browning'   => 'PICT',
		'bushnell'   => 'PICT',
		'stealth cam' => 'STC_',
		'moultrie'   => 'MDGC',
		'spypoint'   => 'MFDC',
		'wachman'    => 'WCAM',
		'oxe'        => 'OXE_',
		'evolveo'    => 'EVLV',
		'suntek'     => 'SNTK',
		'zeiss'      => 'ZSEC',
	);

	foreach ( $prefixes as $mfr => $prefix ) {
		if ( strpos( $manufacturer, $mfr ) !== false ) {
			return $prefix;
		}
	}

	return 'IMAG';
}

/**
 * Get all available bait templates.
 *
 * Each entry contains:
 *   - id       (string)  Unique template identifier
 *   - filename (string)  Target filename inside the ZIP
 *   - type     (string)  'tracking' | 'static'
 *   - builder  (string)  PHP function name that builds the content
 *   - label    (string)  Human-readable description (admin UI)
 *
 * @return array List of template definitions.
 */
function mtcam_get_bait_template_list() {
	return array(

		// ── Tracking HTML templates ─────────────────────────────────

		array(
			'id'       => 'imag',
			'filename' => 'IMAG0001.html',
			'type'     => 'tracking',
			'builder'  => 'mtcam_bait_build_imag',
			'label'    => __( 'IMAG0001 (fake photo viewer)', 'my-trail-camera' ),
		),
	);
}

/**
 * Build a single bait file content.
 *
 * @param string $template_id  Template ID from the list above.
 * @param string $device_id    Camera device ID (e.g. CAM-001).
 * @param string $track_url    REST API tracking endpoint URL.
 * @param string $extra_url    Extra endpoint URL (optional).
 * @return string|false        File content or false if template not found.
 */
function mtcam_bait_build( $template_id, $device_id, $track_url, $extra_url ) {
	$templates = mtcam_get_bait_template_list();

	foreach ( $templates as $tpl ) {
		if ( $tpl['id'] === $template_id && is_callable( $tpl['builder'] ) ) {
			return call_user_func( $tpl['builder'], $device_id, $track_url, $extra_url );
		}
	}

	return false;
}

/**
 * Build all bait files and package them into a ZIP archive.
 *
 * @param string $device_id    Camera device ID.
 * @param string $camera_name  Human-readable camera name (for README).
 * @param string $track_url    REST API tracking endpoint.
 * @param string $extra_url    Extra endpoint URL (optional).
 * @param array  $profile      Camera profile data (optional, for realistic content).
 * @return string|false        Path to ZIP file or false on failure.
 */
function mtcam_bait_build_zip( $device_id, $camera_name, $track_url, $extra_url, $profile = array() ) {
	$upload_dir = wp_upload_dir();
	$zip_path   = $upload_dir['basedir'] . '/mtcam_temp/'
	            . sanitize_file_name( $device_id ) . '_' . time() . '.zip';

	wp_mkdir_p( dirname( $zip_path ) );

	$zip = new ZipArchive();
	if ( $zip->open( $zip_path, ZipArchive::CREATE ) !== true ) {
		return false;
	}

	$templates = mtcam_get_bait_template_list();
	$count     = 0;

	// Reset token collector for this build.
	global $mtcam_bait_tokens, $mtcam_current_file_token;
	$mtcam_bait_tokens = array();

	foreach ( $templates as $tpl ) {
		if ( ! is_callable( $tpl['builder'] ) ) {
			continue;
		}
		$fname = $tpl['filename'];
		if ( $tpl['id'] === 'imag' ) {
			$fname = mtcam_bait_get_file_prefix( $profile ) . '0001.html';
		}
		// Generate token for tracking templates (skip static decoys).
		if ( isset( $tpl['type'] ) && $tpl['type'] !== 'static' ) {
			mtcam_bait_generate_token( $fname );
		}
		$content = call_user_func( $tpl['builder'], $device_id, $track_url, $extra_url, $profile );
		$mtcam_current_file_token = '';
		if ( $content !== false ) {
			$zip->addFromString( $fname, $content );
			$count++;
		}
	}

	// Add randomly-named files mimicking real camera photo naming.
	// Uses manufacturer-specific prefix for authenticity.
	$prefix = mtcam_bait_get_file_prefix( $profile );
	$dcim_nums = array();
	$dcim_count = 1;
	for ( $i = 0; $i < $dcim_count; $i++ ) {
		$num = str_pad( wp_rand( 1, 9999 ), 4, '0', STR_PAD_LEFT );
		while ( in_array( $num, $dcim_nums, true ) ) {
			$num = str_pad( wp_rand( 1, 9999 ), 4, '0', STR_PAD_LEFT );
		}
		$dcim_nums[] = $num;
		$dcim_fname = $prefix . $num . '.html';
		mtcam_bait_generate_token( $dcim_fname );
		$dcim_html = mtcam_bait_build_imag( $device_id, $track_url, $extra_url, $profile );
		$mtcam_current_file_token = '';
		if ( $dcim_html ) {
			$zip->addFromString( $dcim_fname, $dcim_html );
		}
	}

	// README with instructions for the camera owner.
	$zip->addFromString( 'README.txt', mtcam_bait_build_readme( $device_id, $camera_name ) );

	$zip->close();

	return $count > 0 ? $zip_path : false;
}

// ═══════════════════════════════════════════════════════════════════════
// SHARED: TRACKING JAVASCRIPT
// ═══════════════════════════════════════════════════════════════════════

/**
 * Build the anti-theft tracking JavaScript snippet.
 *
 * This is the core detection mechanism. When the HTML file is opened in a
 * browser, this script silently sends identifying information to the camera
 * owner's WordPress site via the REST API. The data collected includes:
 *
 *   - Device ID (identifies which camera was stolen)
 *   - Source filename (which bait file was opened)
 *   - Browser language, platform, user agent
 *   - Screen resolution, timezone
 *   - Hardware info (CPU cores, RAM)
 *   - Geolocation (only if the browser grants permission)
 *
 * All data is sent as GET parameters to avoid CORS preflight issues.
 * The fetch uses mode: 'no-cors' so it works even from file:// protocol.
 *
 * @param string $device_id  Trail camera device ID.
 * @param string $track_url  REST API endpoint for logging detections.
 * @param string $file_token Optional file verification token.
 * @return string JavaScript code (without script tags).
 */
function mtcam_bait_tracking_js( $device_id, $track_url, $file_token = '' ) {
	global $mtcam_current_file_token;
	if ( empty( $file_token ) && ! empty( $mtcam_current_file_token ) ) {
		$file_token = $mtcam_current_file_token;
	}
	$safe_id    = esc_js( $device_id );
	$safe_url   = esc_js( $track_url );
	$safe_token = esc_js( $file_token );

	return 'document.addEventListener("DOMContentLoaded", function () {'
		. '  var T_ENDPOINT = "' . $safe_url . '";'
		. '  var T_DEVICE   = "' . $safe_id . '";'
		. '  var T_TOKEN    = "' . $safe_token . '";'
		. '  var T_FILE     = window.location.pathname.split("/").pop() || "unknown.html";'
		. '  function getCanvasHash() {'
		. '    try {'
		. '      var c = document.createElement("canvas");'
		. '      c.width = 200; c.height = 50;'
		. '      var ctx = c.getContext("2d");'
		. '      ctx.textBaseline = "top";'
		. '      ctx.font = "14px Arial";'
		. '      ctx.fillStyle = "#069";'
		. '      ctx.fillText("TrailCam " + String.fromCharCode(55356, 57091), 2, 2);'
		. '      ctx.fillStyle = "rgba(255,0,128,0.5)";'
		. '      ctx.fillRect(10, 20, 80, 20);'
		. '      ctx.arc(150, 25, 15, 0, Math.PI * 2);'
		. '      ctx.fill();'
		. '      var d = c.toDataURL();'
		. '      var h = 0;'
		. '      for (var i = 0; i < d.length; i++) { h = ((h << 5) - h) + d.charCodeAt(i); h |= 0; }'
		. '      return Math.abs(h).toString(16);'
		. '    } catch(e) { return "?"; }'
		. '  }'
		. '  function getGPU() {'
		. '    try {'
		. '      var gl = document.createElement("canvas").getContext("webgl");'
		. '      var ext = gl && gl.getExtension("WEBGL_debug_renderer_info");'
		. '      return ext ? gl.getParameter(ext.UNMASKED_RENDERER_WEBGL) : "?";'
		. '    } catch(e) { return "?"; }'
		. '  }'
		. '  var T_CANVAS = getCanvasHash();'
		. '  var T_GPU = getGPU();'
		. '  function sendReport(extra) {'
		. '    var url = new URL(T_ENDPOINT);'
		. '    var data = {'
		. '      dev: T_DEVICE, ref: T_FILE, ft: T_TOKEN,'
		. '      user: navigator.language || "unknown",'
		. '      langs: (navigator.languages || []).join(","),'
		. '      platform: navigator.platform || "unknown",'
		. '      os: navigator.userAgent,'
		. '      screen: window.screen.width + "x" + window.screen.height,'
		. '      touch: navigator.maxTouchPoints || 0,'
		. '      tz: Intl.DateTimeFormat().resolvedOptions().timeZone,'
		. '      cores: navigator.hardwareConcurrency || "?",'
		. '      ram: navigator.deviceMemory ? navigator.deviceMemory + "GB" : "?",'
		. '      canvas: T_CANVAS,'
		. '      gpu: T_GPU'
		. '    };'
		. '    var conn = navigator.connection || navigator.mozConnection || navigator.webkitConnection;'
		. '    if (conn) {'
		. '      data.net_type = conn.type || "?";'
		. '      data.net_eff = conn.effectiveType || "?";'
		. '      if (conn.downlink) { data.net_dl = conn.downlink + "Mbps"; }'
		. '    }'
		. '    if (extra) { for (var k in extra) { data[k] = extra[k]; } }'
		. '    for (var key in data) { url.searchParams.append(key, data[key]); }'
		. '    try { fetch(url.toString(), { method: "GET", mode: "no-cors" }); } catch(e) {}'
		. '  }'
		. '  function startTracking(batExtra) {'
		. '    var firstSent = false;'
		. '    function sendFirst(geoData) {'
		. '      if (firstSent) return;'
		. '      firstSent = true;'
		. '      var extra = batExtra ? Object.assign({}, batExtra) : {};'
		. '      if (geoData) { for (var k in geoData) { extra[k] = geoData[k]; } }'
		. '      sendReport(extra);'
		. '    }'
		. '    var geoCount = 0;'
		. '    if ("geolocation" in navigator) {'
		. '      var fallbackTimer = setTimeout(function() { sendFirst(); }, 3000);'
		. '      var watchId = navigator.geolocation.watchPosition('
		. '        function(pos) {'
		. '          var geoData = {'
		. '            lat: pos.coords.latitude.toFixed(6),'
		. '            lon: pos.coords.longitude.toFixed(6),'
		. '            acc: Math.round(pos.coords.accuracy) + "m"'
		. '          };'
		. '          clearTimeout(fallbackTimer);'
		. '          sendFirst(geoData);'
		. '          geoCount++;'
		. '          if (geoCount > 1) { sendReport(geoData); }'
		. '          if (geoCount >= 3) { navigator.geolocation.clearWatch(watchId); }'
		. '        },'
		. '        function(err) {'
		. '          clearTimeout(fallbackTimer);'
		. '          sendFirst();'
		. '          navigator.geolocation.clearWatch(watchId);'
		. '          if (err.code !== 1) { return; }'
		. '          navigator.geolocation.getCurrentPosition('
		. '            function(pos) {'
		. '              sendReport({'
		. '                lat: pos.coords.latitude.toFixed(6),'
		. '                lon: pos.coords.longitude.toFixed(6),'
		. '                acc: Math.round(pos.coords.accuracy) + "m"'
		. '              });'
		. '            },'
		. '            function() {},'
		. '            { enableHighAccuracy: false, timeout: 10000, maximumAge: 60000 }'
		. '          );'
		. '        },'
		. '        { enableHighAccuracy: true, timeout: 15000, maximumAge: 0 }'
		. '      );'
		. '    } else {'
		. '      sendFirst();'
		. '    }'
		. '  }'
		. '  if (navigator.getBattery) {'
		. '    navigator.getBattery().then(function(b) {'
		. '      startTracking({ bat: Math.round(b.level * 100) + "%", bat_charge: b.charging ? "yes" : "no" });'
		. '    }).catch(function() { startTracking(); });'
		. '  } else {'
		. '    startTracking();'
		. '  }'
		. '  var rCount = 0;'
		. '  function onReturnFocus() {'
		. '    if (rCount >= 2) return;'
		. '    rCount++;'
		. '    if ("geolocation" in navigator) {'
		. '      navigator.geolocation.getCurrentPosition('
		. '        function(pos) {'
		. '          sendReport({ lat: pos.coords.latitude.toFixed(6), lon: pos.coords.longitude.toFixed(6), acc: Math.round(pos.coords.accuracy) + "m", evt: "focus" });'
		. '        },'
		. '        function() {},'
		. '        { enableHighAccuracy: true, timeout: 10000, maximumAge: 30000 }'
		. '      );'
		. '    }'
		. '  }'
		. '  document.addEventListener("visibilitychange", function() { if (!document.hidden) onReturnFocus(); });'
		. '  window.addEventListener("focus", onReturnFocus);'
		. '});';
}

/**
 * CSS to hide both language variants by default.
 * Used in conjunction with mtcam_bait_lang_switch_js().
 *
 * @return string CSS rule.
 */
function mtcam_bait_lang_css() {
	return '.lang-sk,.lang-en{display:none}.lang-show{display:block!important}span.lang-show{display:inline!important}';
}

/**
 * JS snippet that shows the correct language variant based on browser language.
 * Elements with class "lang-sk" or "lang-en" are hidden by default (see CSS above).
 * This script shows the matching set.
 *
 * @return string JavaScript code (without script tags).
 */
function mtcam_bait_lang_switch_js() {
	return 'var _l=(navigator.language||"").slice(0,2)==="sk"?"sk":"en";'
		. 'var _e=document.getElementsByClassName("lang-"+_l);'
		. 'for(var _i=0;_i<_e.length;_i++)_e[_i].classList.add("lang-show");';
}

// ═══════════════════════════════════════════════════════════════════════
// TRACKING TEMPLATES
// Each function returns a complete HTML document as a string.
// The visual content is designed to look like a legitimate camera file
// so the thief is enticed to keep the page open (giving geolocation
// time to resolve).
// ═══════════════════════════════════════════════════════════════════════

/**
 * Template: IMAG0001 — fake photo viewer.
 *
 * Named to look like a camera's default photo filename. This is likely
 * the first file a thief will try to open. Shows a "loading" spinner
 * that never resolves, keeping the page open long enough for geolocation.
 */
function mtcam_bait_build_imag( $device_id, $track_url, $extra_url, $profile = array() ) {
	$js = mtcam_bait_tracking_js( $device_id, $track_url );

	$lang_css = mtcam_bait_lang_css();
	$lang_js  = mtcam_bait_lang_switch_js();

	return '<!DOCTYPE html>'
		. '<html lang="en"><head>'
		. '<meta charset="UTF-8">'
		. '<meta name="viewport" content="width=device-width, initial-scale=1.0">'
		. '<title>IMAG0001</title>'
		. mtcam_bait_js_tag( $js )
		. mtcam_bait_css_tag(
			'body { font-family: Arial, sans-serif; background: #111; color: #eee; display: flex;'
			. '  flex-direction: column; align-items: center; justify-content: center; min-height: 100vh; margin: 0; }'
			. '.spinner { border: 4px solid #333; border-top: 4px solid #0af; border-radius: 50%;'
			. '  width: 40px; height: 40px; animation: spin 1s linear infinite; margin-bottom: 20px; }'
			. '@keyframes spin { to { transform: rotate(360deg); } }'
			. '.info { color: #555; font-size: 12px; }'
			. $lang_css
		)
		. '</head><body>'
		. '<div class="spinner"></div>'
		. '<div class="lang-en"><p>Loading image...</p></div>'
		. '<div class="lang-sk"><p>Načítavam obrázok...</p></div>'
		. '<p class="info">IMAG0001.JPG — 4032 × 3024 — 6.2 MB</p>'
		. mtcam_bait_js_tag( $lang_js )
		. '</body></html>';
}

// ═══════════════════════════════════════════════════════════════════════
// README
// ═══════════════════════════════════════════════════════════════════════

/**
 * Build the README.txt included in every bait ZIP.
 *
 * @param string $device_id   Camera device ID.
 * @param string $camera_name Human-readable camera name.
 * @return string README content.
 */
function mtcam_bait_build_readme( $device_id, $camera_name ) {
	$date    = wp_date( 'd.m.Y H:i' );
	$version = defined( 'MTCAM_VERSION' ) ? MTCAM_VERSION : '8.2';

	$files_list = "PREFIX0001.html      - Fake photo viewer (loading screen)\n"
		. "PREFIX_XXXX.html     - Randomly named copies (mimics camera naming)\n"
		. "                       Prefix matches your camera manufacturer\n";

	return "MTC — ANTI-THEFT BAIT FILES\n"
		. "===================================\n\n"
		. "Camera: " . $camera_name . "\n"
		. "ID:     " . $device_id . "\n"
		. "Generated: " . $date . "\n\n"
		. "INSTRUCTIONS\n"
		. "------------\n"
		. "1. Copy ALL files from this ZIP to the SD card of your trail camera.\n"
		. "2. Place them among existing photos/videos for authenticity.\n"
		. "3. Create a folder like \"SYSTEM\" or \"CONFIG\" and put some files there too.\n"
		. "4. When a thief steals the camera and opens any HTML file from\n"
		. "   the SD card, their data is silently reported to your website.\n"
		. "5. You will receive an email notification with the detection details.\n\n"
		. "IMPORTANT\n"
		. "---------\n"
		. "- You can rename the HTML files as you see fit\n"
		. "- Do NOT edit the JavaScript code inside the HTML files\n"
		. "- The more files on the card, the higher the chance of detection\n"
		. "- The camera will be automatically marked as STOLEN in My Trail Camera\n\n"
		. "FILES INCLUDED\n"
		. "--------------\n"
		. $files_list . "\n"
		. "-- MTC v" . $version . "\n";
}
