<?php
/**
 * Processes the email form, logs the download, and serves the file securely.
 *
 * @package email-gated-downloads
 */

defined('ABSPATH') || exit;

add_action('admin_post_nopriv_spdfed_download', 'spdfed_handle_download');
add_action('admin_post_spdfed_download', 'spdfed_handle_download');

/**
 * Handles the download request after form submission.
 *
 * @throws Exception On validation or file handling errors.
 */
function spdfed_handle_download() {
	try {
		$post_id = isset($_POST['post_id']) ? absint($_POST['post_id']) : 0;

		// Verify nonce first, using the post_id.
		// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash
		if (! isset($_POST['spdfed_nonce_field']) || ! wp_verify_nonce($_POST['spdfed_nonce_field'], 'spdfed_download_nonce_' . $post_id)) {
			throw new Exception(__('Invalid security token', 'email-gated-downloads'));
		}

		// Validate inputs.
		$email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
		if (! $email) {
			throw new Exception(__('Invalid email address', 'email-gated-downloads'));
		}

		$is_premium = function_exists('sped_fs') && sped_fs()->can_use_premium_code__premium_only();
		
		// Validate file source based on user type
		if ($is_premium && $post_id > 0) {
			// Premium user with CPT
			if (get_post_type($post_id) !== 'spdfed_secure_file') {
				throw new Exception(__('Invalid file ID', 'email-gated-downloads'));
			}
		} elseif ((!$is_premium && $post_id === 0) || ($is_premium && $post_id === 0)) {
			// Free user with settings-based file OR premium user using free shortcode
			$free_file = get_option('spdfed_free_storage_filename');
			if (empty($free_file)) {
				throw new Exception(__('No file configured', 'email-gated-downloads'));
			}
		} else {
			throw new Exception(__('Invalid configuration', 'email-gated-downloads'));
		}

		// --- Fetch settings based on user type with override support ---.
		$submitter_name_for_db = null;
		
		if ($is_premium && $post_id > 0) {
			// Premium: Check if this file overrides global settings
			$override_settings = get_post_meta($post_id, '_spdfed_override_settings', true) === '1';
			
			if ($override_settings) {
				// Use CPT-specific settings
				$enable_name_field     = get_post_meta($post_id, '_spdfed_enable_name_field', true) === '1';
				$name_field_required   = get_post_meta($post_id, '_spdfed_name_field_required', true) === '1';
			} else {
				// Use global settings (same as free settings)
				$enable_name_field     = get_option('spdfed_free_enable_name_field') == 1;
				$name_field_required   = get_option('spdfed_free_name_field_required') == 1;
			}
		} else {
			// Free or premium using free shortcode: Get settings from options
			$enable_name_field     = get_option('spdfed_free_enable_name_field') == 1;
			$name_field_required   = get_option('spdfed_free_name_field_required') == 1;
		}

		if ($enable_name_field) {
			$name_value = isset($_POST['spdfed_name']) ? sanitize_text_field(wp_unslash($_POST['spdfed_name'])) : '';
			if ($name_field_required && empty($name_value)) {
				throw new Exception(__('Full name is required.', 'email-gated-downloads'));
			}
			if (! empty($name_value)) {
				$submitter_name_for_db = $name_value;
			}
		}

		if ($is_premium && $post_id > 0) {
			// Premium: Check if this file overrides global settings
			$override_settings = get_post_meta($post_id, '_spdfed_override_settings', true) === '1';
			
			if ($override_settings) {
				// Use CPT-specific GDPR settings
				$gdpr_enabled = get_post_meta($post_id, '_spdfed_enable_gdpr', true);
				if ('' === $gdpr_enabled) {
					$gdpr_enabled = '1'; // Default to on if not set.
				}
			} else {
				// Use global GDPR settings (same as free settings)
				$gdpr_enabled = get_option('spdfed_free_enable_gdpr', 1) ? '1' : '0';
			}
		} else {
			// Free or premium using free shortcode: Get GDPR settings from options
			$gdpr_enabled = get_option('spdfed_free_enable_gdpr', 1) ? '1' : '0';
		}

		if ('1' === $gdpr_enabled) {
			if (! isset($_POST['gdpr']) || '1' !== $_POST['gdpr']) {
				throw new Exception(__('GDPR consent is required.', 'email-gated-downloads'));
			}
		}

		if ($is_premium && $post_id > 0) {
			// Premium: Get file info from CPT meta
			$storage_filename      = get_post_meta($post_id, '_spdfed_storage_filename', true);
			$download_display_name = get_post_meta($post_id, '_spdfed_file_name', true); // Sanitized original, for Content-Disposition and logs.
			$mime_type_from_meta   = get_post_meta($post_id, '_spdfed_mime_type', true);
			
			// Fallback: If _spdfed_file_name is empty, try _spdfed_original_file_name
			if (empty($download_display_name)) {
				$download_display_name = get_post_meta($post_id, '_spdfed_original_file_name', true);
			}
			
			// Last resort fallback: If still empty, extract original name from storage filename if it looks like original name
			if (empty($download_display_name) && !empty($storage_filename)) {
				// If storage filename doesn't look like a UUID (contains no hyphens or is not 36 chars), use it as display name
				if (strlen($storage_filename) !== 40 || substr_count($storage_filename, '-') < 4) {
					$download_display_name = $storage_filename;
				} else {
					// This is a UUID filename, we need a better fallback
					$download_display_name = 'download.pdf'; // Generic fallback
				}
			}
		} else {
			// Free or premium using free shortcode: Get file info from options
			$storage_filename      = get_option('spdfed_free_storage_filename');
			$download_display_name = get_option('spdfed_free_file_name'); // Sanitized original, for Content-Disposition and logs.
			$mime_type_from_meta   = get_option('spdfed_free_mime_type');
			
			// Fallback for free version too
			if (empty($download_display_name) && !empty($storage_filename)) {
				// If storage filename doesn't look like a UUID, use it as display name
				if (strlen($storage_filename) !== 40 || substr_count($storage_filename, '-') < 4) {
					$download_display_name = $storage_filename;
				} else {
					$download_display_name = 'download.pdf'; // Generic fallback
				}
			}
		}

		if (empty($storage_filename)) {
			throw new Exception(__('File not properly configured for this ID', 'email-gated-downloads'));
		}
		
		// Ensure we have a download name (this should not happen with proper setup)
		if (empty($download_display_name)) {
			$download_display_name = 'secure-download.pdf';
		}

		// Use a default MIME type if not found, though it should always be there.
		$content_type = ! empty($mime_type_from_meta) ? $mime_type_from_meta : 'application/octet-stream';

		// Rate limiting (use different key for free vs premium).
		$rate_limit_identifier = $is_premium && $post_id > 0 ? $post_id : 'free_download';
		$rate_limit_key = 'spdfed_rate_' . md5($email . $rate_limit_identifier . current_time('Y-m-d-H', true)); // Include identifier in key, use GMT time.
		$attempt_count  = (int) get_transient($rate_limit_key);
		if ($attempt_count > 5) {
			throw new Exception(__('Too many download attempts. Please try again later.', 'email-gated-downloads'));
		}
		set_transient($rate_limit_key, $attempt_count + 1, HOUR_IN_SECONDS);

		// Verify file path using the unique storage filename.
                $file_path = WP_CONTENT_DIR . '/private-uploads/' . $storage_filename;
                if (! file_exists($file_path) || strpos(realpath($file_path), realpath(WP_CONTENT_DIR . '/private-uploads/')) !== 0) {
			throw new Exception(__('File not found or access denied', 'email-gated-downloads'));
		}

		// Log download.
		global $wpdb;
		$log_data = [
			'email'         => $email,
			'file_name'     => $download_display_name, // Log the user-friendly download name.
			'downloaded_at' => current_time('mysql'),
			'post_id'       => $post_id, // Track which download this is (0 for free, post_id for premium)
		];
		$log_formats = ['%s', '%s', '%s', '%d'];

		if ($enable_name_field && null !== $submitter_name_for_db) {
			$log_data['submitter_name'] = $submitter_name_for_db;
			$log_formats[]              = '%s';
		}

		// Add analytics data for premium users or when analytics columns exist
		$table_has_analytics = spdfed_table_has_analytics_columns();
		if ($table_has_analytics) {
			// Local date for timezone-aware grouping
			$log_data['occurred_local_date'] = wp_date('Y-m-d');
			$log_formats[] = '%s';
			
			// Privacy-friendly hashes (only for premium users)
			if ($is_premium || ($post_id === 0 && function_exists('sped_fs'))) {
				$salt = wp_salt('auth');
				$norm = function($v) { return strtolower(trim((string)$v)); };
				
				$log_data['email_hash'] = hash_hmac('sha256', $norm($email), $salt, true);
				$log_formats[] = '%s';
				
				// Optional: referrer, user agent, and IP hashes
				$log_data['referrer'] = isset($_SERVER['HTTP_REFERER']) ? substr(sanitize_text_field(wp_unslash($_SERVER['HTTP_REFERER'])), 0, 255) : null;
				$log_formats[] = '%s';
				
				if (isset($_SERVER['HTTP_USER_AGENT'])) {
					$log_data['ua_hash'] = hash_hmac('sha256', $norm(sanitize_text_field(wp_unslash($_SERVER['HTTP_USER_AGENT']))), $salt, true);
					$log_formats[] = '%s';
				}
				
				if (isset($_SERVER['REMOTE_ADDR'])) {
					$log_data['ip_hash'] = hash_hmac('sha256', $norm(sanitize_text_field(wp_unslash($_SERVER['REMOTE_ADDR']))), $salt, true);
					$log_formats[] = '%s';
				}
			}
		}

		// Direct query is required to insert into our custom log table.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
		$wpdb->insert(
			$wpdb->prefix . 'spdfed_logs',
			$log_data,
			$log_formats
		);
		
		// Clear analytics caches since we just added a new download
		wp_cache_delete('spdfed_file_analytics_options');
		
		// Note: Analytics REST API caches use 5-minute TTL for near real-time accuracy
		// Individual parameterized cache invalidation would be complex, so we rely on short TTL

		// Send file.
		header('Content-Description: File Transfer');
		header('Content-Type: ' . $content_type);
		header('Content-Disposition: attachment; filename="' . basename($download_display_name) . '"'); // Use the display name for download.
		header('Expires: 0');
		header('Cache-Control: must-revalidate');
		header('Pragma: public');
		header('Content-Length: ' . filesize($file_path));

		// Clean output buffer before reading file.
		if (ob_get_level()) {
			ob_end_clean();
		}

		// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_readfile
		readfile($file_path);
		exit;

	} catch (Exception $e) {
		$submitted_post_id_for_redirect = isset($_POST['post_id']) ? absint($_POST['post_id']) : 0;
		$redirect_url_base              = home_url(); // Default fallback.

		if (isset($_POST['spdfed_redirect_url'])) {
			$posted_redirect_url = esc_url_raw(wp_unslash($_POST['spdfed_redirect_url']));
			if ($posted_redirect_url && str_starts_with($posted_redirect_url, 'http') && wp_parse_url($posted_redirect_url, PHP_URL_HOST) === wp_parse_url(home_url(), PHP_URL_HOST)) {
				$redirect_url_base = $posted_redirect_url;
			}
		}

		// If the redirect URL wasn't valid or wasn't provided, fall back to the referer.
		if (home_url() === $redirect_url_base) {
			$referer = wp_get_referer();
			if ($referer && wp_parse_url($referer, PHP_URL_HOST) === wp_parse_url(home_url(), PHP_URL_HOST)) {
				$redirect_url_base = $referer;
			}
		}

		// Store error data in a transient.
		$transient_key  = 'spdfed_error_' . md5(wp_rand() . time());
		$error_data = [
			'message'   => $e->getMessage(),
			'post_id'   => $submitted_post_id_for_redirect,
			'email'     => isset($_POST['email']) ? sanitize_email(wp_unslash($_POST['email'])) : '',
			'name'      => isset($_POST['spdfed_name']) ? sanitize_text_field(wp_unslash($_POST['spdfed_name'])) : '',
		];
		set_transient($transient_key, $error_data, MINUTE_IN_SECONDS * 5); // Store for 5 minutes.

		$redirect_url_base = remove_query_arg(['spdfed_error_key'], $redirect_url_base);
		$redirect_url      = add_query_arg('spdfed_error_key', $transient_key, $redirect_url_base);

		wp_safe_redirect($redirect_url);
		exit;
	}
}
