<?php

namespace MOWCPS\Common\Handlers\File_Save_Handler;

use MOWCPS\Common\Utils\Utils;
use MOWCPS\Common\Utils\Response_Builder;
use MOWCPS\Common\Constants\Constants;

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

/**
 * Class File_Save_Handler
 *
 * Handles file save operations
 */
class File_Save_Handler {

	/**
	 * Export file name
	 *
	 * @var string
	 */
	private $export_file_name;

	/**
	 * Constructor
	 */
	public function __construct() {}

	/**
	 * Export data to file
	 *
	 * @param array  $exported_data Data to export
	 * @param string $file_dir File directory path
	 * @param string $file_name Export file name
	 * @return WP_REST_Response|bool Response object or boolean on success
	 */
	public function export_data_to_file( $exported_data, $file_dir, $file_name = null ) {
		if ( ! empty( $file_name ) ) {
			$this->export_file_name = $file_name;
		}

		if ( ! empty( $exported_data ) ) {
			$json_data = wp_json_encode( $exported_data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES );

			if ( false === $json_data ) {
				return Response_Builder::error(
					'json_encode_failed',
					'Export completed but failed to encode JSON data',
					array(
						'status_code' => 400,
					)
				);
			}

			if ( empty( $this->export_file_name ) ) {
				return Response_Builder::error(
					'missing_filename',
					'Export file name is required',
					array(
						'status_code' => 400,
					)
				);
			}

			$save_result = Utils::store_data_in_file( $this->export_file_name, $file_dir, $json_data );

			if ( ! $save_result ) {
				return Response_Builder::error(
					'export_failed',
					'Export completed but failed to save file',
					array(
						'status_code' => 400,
					)
				);
			}

			return $save_result;
		} else {
			return Response_Builder::error(
				'export_failed',
				'No data to export',
				array(
					'status_code' => 400,
				)
			);
		}
	}

	public static function download_file_from_browser( $file_dir, $export_filename ) {
		$file_dir  = wp_normalize_path( $file_dir );
		$file_path = wp_normalize_path( $file_dir . '/' . $export_filename );

		// Verify file exists before attempting to prepare for download
		global $wp_filesystem;
		WP_Filesystem();

		if ( ! $wp_filesystem->exists( $file_path ) ) {
			return Response_Builder::error(
				'file_not_found',
				'Export file was not found at: ' . basename( $file_path ),
				array(
					'status_code' => 404,
					'file_path'   => $file_path,
				)
			);
		}

		$download_data = self::prepare_file_for_download( $file_path, $export_filename );

		if ( is_wp_error( $download_data ) ) {
			return Response_Builder::from_wp_error( $download_data );
		}

		$file_content_base64 = $download_data['file_content_base64'];
		$download_url        = $download_data['download_url'];

		return Response_Builder::success(
			array(
				'file_content'        => $file_content_base64, // Frontend expects 'file_content'
				'file_content_base64' => $file_content_base64, // Keep for backward compatibility
				'download_url'        => $download_url,
				'file_name'           => $export_filename,
				'export_file_path'    => '/resources/supplier-data/export-config/' . $export_filename,
			)
		);
	}

	/**
	 * Get download URL for browser download
	 * Returns a URL that can be used to trigger a browser download via REST API
	 *
	 * @param string $file_path Full file path of the source file.
	 * @param string $file_name Optional filename for download.
	 * @return string|WP_Error Download URL or WP_Error on failure.
	 */
	public static function get_download_url( $file_path, $file_name = '' ) {
		global $wp_filesystem;
		WP_Filesystem();

		$file_path = wp_normalize_path( $file_path );

		$file_exists = false;
		if ( $wp_filesystem->exists( $file_path ) ) {
			$file_exists = true;
		}

		if ( empty( $file_path ) || ! $file_exists ) {
			return new \WP_Error( 'file_not_found', 'Source file does not exist: ' . $file_path );
		}

		if ( empty( $file_name ) ) {
			$file_name = basename( $file_path );
		}

		$file_name = sanitize_file_name( $file_name );
		$namespace = Constants::REST_API_NAMESPACE;

		$rest_url = rest_url( $namespace . '/suppliers/download-file' );

		$normalized_path = wp_normalize_path( $file_path );

		$download_url = add_query_arg(
			array(
				'file_path' => rawurlencode( $normalized_path ),
				'file_name' => rawurlencode( $file_name ),
			),
			$rest_url
		);

		return $download_url;
	}

	/**
	 * Trigger browser download by sending appropriate HTTP headers
	 * This method sends headers and file content to trigger browser download
	 *
	 * @param string $file_path Full file path of the source file.
	 * @param string $file_name Optional filename for download.
	 * @return void|WP_Error Exits on success or returns WP_Error on failure.
	 */
	public static function trigger_browser_download( $file_path, $file_name = '' ) {
		global $wp_filesystem;
		WP_Filesystem();

		$file_path = wp_normalize_path( $file_path );

		if ( empty( $file_path ) ) {
			return new \WP_Error( 'file_not_found', 'File path is empty' );
		}

		$file_exists = false;
		$actual_path = $file_path;

		if ( $wp_filesystem->exists( $file_path ) ) {
			$file_exists = true;
		}

		if ( ! $file_exists ) {
			return new \WP_Error(
				'file_not_found',
				'Source file does not exist: ' . $file_path,
				array(
					'file_path'            => $file_path,
					'wp_filesystem_exists' => $wp_filesystem->exists( $file_path ),
					'file_exists'          => file_exists( $file_path ),
					'realpath'             => realpath( $file_path ),
				)
			);
		}

		if ( empty( $file_name ) ) {
			$file_name = basename( $actual_path );
		}

		$file_name = sanitize_file_name( $file_name );

		// Try to read file content - use actual_path if different
		$file_content = $wp_filesystem->get_contents( $actual_path );
		if ( false === $file_content ) {
			// Fallback to regular file_get_contents if wp_filesystem fails
			$file_content = @file_get_contents( $actual_path );
			if ( false === $file_content ) {
				return new \WP_Error( 'read_failed', 'Failed to read source file: ' . $actual_path );
			}
		}

		$extension    = strtolower( pathinfo( $file_name, PATHINFO_EXTENSION ) );
		$content_type = self::get_content_type( $extension );

		while ( ob_get_level() ) {
			ob_end_clean();
		}

		if ( ! headers_sent() ) {
			remove_all_actions( 'rest_pre_serve_request' );
			remove_all_filters( 'rest_pre_serve_request' );

			// Set headers for download.
			header( 'Content-Type: ' . $content_type );
			header( 'Content-Disposition: attachment; filename="' . esc_attr( $file_name ) . '"' );
			header( 'Content-Length: ' . strlen( $file_content ) );
			header( 'Cache-Control: no-cache, must-revalidate' );
			header( 'Pragma: no-cache' );
			header( 'Expires: 0' );

			echo $file_content; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Binary file content.
			exit;
		}

		return new \WP_Error( 'headers_sent', 'Headers already sent, cannot trigger download' );
	}

	/**
	 * Validate and prepare file path for download
	 * Validates file path, checks security, and normalizes paths
	 *
	 * @param string $file_path File path (may be URL encoded).
	 * @param string $file_name Optional filename for download.
	 * @return array|WP_Error Array with normalized 'file_path' and 'file_name' keys, or WP_Error on failure.
	 */
	public static function validate_file_for_download( $file_path, $file_name = '' ) {
		if ( empty( $file_path ) ) {
			return new \WP_Error( 'missing_file_path', 'file_path parameter is required.', array( 'status' => 400 ) );
		}

		$decoded_path = $file_path;
		while ( $decoded_path !== urldecode( $decoded_path ) ) {
			$decoded_path = urldecode( $decoded_path );
		}
		$file_path = $decoded_path;

		$file_path  = wp_normalize_path( $file_path );
		$plugin_dir = wp_normalize_path( MOWCPS_PLUGIN_DIR );

		global $wp_filesystem;
		WP_Filesystem();

		$file_exists = false;
		$actual_path = $file_path;

		if ( $wp_filesystem->exists( $file_path ) ) {
			$file_exists = true;
		}

		if ( ! $file_exists ) {
			$real_plugin_dir = realpath( $plugin_dir );
			$real_file_path  = realpath( $file_path );

			if ( $real_plugin_dir ) {
				if ( $real_file_path && strpos( $real_file_path, $real_plugin_dir ) !== 0 ) {
					return new \WP_Error(
						'invalid_file_path',
						'File path is not within allowed directory.',
						array(
							'status'          => 403,
							'file_path'       => $file_path,
							'real_file_path'  => $real_file_path,
							'plugin_dir'      => $plugin_dir,
							'real_plugin_dir' => $real_plugin_dir,
						)
					);
				}
			} elseif ( strpos( $file_path, $plugin_dir ) !== 0 ) {
					return new \WP_Error(
						'invalid_file_path',
						'File path is not within allowed directory.',
						array(
							'status'     => 403,
							'file_path'  => $file_path,
							'plugin_dir' => $plugin_dir,
						)
					);
			}

			return new \WP_Error(
				'file_not_found',
				'File does not exist: ' . basename( $file_path ),
				array(
					'status'               => 404,
					'file_path'            => $file_path,
					'plugin_dir'           => $plugin_dir,
					'wp_filesystem_exists' => $wp_filesystem->exists( $file_path ),
					'file_exists'          => file_exists( $file_path ),
					'realpath'             => realpath( $file_path ),
				)
			);
		}

		// Verify file is within plugin directory for security
		$real_file_path  = realpath( $actual_path );
		$real_plugin_dir = realpath( $plugin_dir );

		if ( $real_file_path && $real_plugin_dir ) {
			if ( strpos( $real_file_path, $real_plugin_dir ) !== 0 ) {
				return new \WP_Error(
					'invalid_file_path',
					'File path is not within allowed directory.',
					array(
						'status'          => 403,
						'file_path'       => $file_path,
						'real_file_path'  => $real_file_path,
						'plugin_dir'      => $plugin_dir,
						'real_plugin_dir' => $real_plugin_dir,
					)
				);
			}
		} elseif ( strpos( $actual_path, $plugin_dir ) !== 0 ) {
				return new \WP_Error(
					'invalid_file_path',
					'File path is not within allowed directory.',
					array(
						'status'      => 403,
						'file_path'   => $file_path,
						'actual_path' => $actual_path,
						'plugin_dir'  => $plugin_dir,
					)
				);
		}

		if ( empty( $file_name ) ) {
			$file_name = basename( $actual_path );
		}

		$file_name = sanitize_file_name( $file_name );

		return array(
			'file_path' => $actual_path,
			'file_name' => $file_name,
		);
	}

	/**
	 * Handle REST API file download request
	 * Validates, clears output buffers, and triggers browser download
	 *
	 * @param string $file_path File path (may be URL encoded).
	 * @param string $file_name Optional filename for download.
	 * @return void|WP_Error Exits on success or returns WP_Error on failure.
	 */
	public static function handle_rest_api_file_download( $file_path, $file_name = '' ) {
		$validation_result = self::validate_file_for_download( $file_path, $file_name );

		if ( is_wp_error( $validation_result ) ) {
			return $validation_result;
		}

		$validated_file_path = $validation_result['file_path'];
		$validated_file_name = $validation_result['file_name'];

		while ( ob_get_level() ) {
			ob_end_clean();
		}

		$result = self::trigger_browser_download( $validated_file_path, $validated_file_name );

		if ( is_wp_error( $result ) ) {
			return $result;
		}

		return new \WP_Error( 'download_failed', 'Failed to trigger download.', array( 'status' => 500 ) );
	}

	/**
	 * Prepare file for download by reading content and generating download data
	 * Returns file content as base64 and download URL for browser download
	 *
	 * @param string $file_path Full file path of the source file.
	 * @param string $file_name Optional filename for download.
	 * @return array|WP_Error Array with 'file_content_base64' and 'download_url' keys, or WP_Error on failure.
	 */
	public static function prepare_file_for_download( $file_path, $file_name = '' ) {
		global $wp_filesystem;
		WP_Filesystem();

		$file_path = wp_normalize_path( $file_path );

		if ( empty( $file_name ) ) {
			$file_name = basename( $file_path );
		}

		$file_name = sanitize_file_name( $file_name );

		$file_exists = false;
		$actual_path = $file_path;

		if ( $wp_filesystem->exists( $file_path ) ) {
			$file_exists = true;
		} else {
			$real_file_path = realpath( $file_path );
			if ( $real_file_path && file_exists( $real_file_path ) ) {
				$file_exists = true;
				$actual_path = $real_file_path;
			} elseif ( file_exists( $file_path ) ) {
				$file_exists = true;
			}
		}

		if ( ! $file_exists ) {
			return new \WP_Error(
				'file_not_found',
				'File does not exist: ' . basename( $file_path ),
				array(
					'file_path'            => $file_path,
					'wp_filesystem_exists' => $wp_filesystem->exists( $file_path ),
					'file_exists'          => file_exists( $file_path ),
					'realpath'             => realpath( $file_path ),
				)
			);
		}

		$file_content = $wp_filesystem->get_contents( $actual_path );
		if ( false === $file_content ) {
			$file_content = @file_get_contents( $actual_path );
			if ( false === $file_content ) {
				return new \WP_Error( 'read_failed', 'Export file was created but cannot be read: ' . $actual_path );
			}
		}

		$file_content_base64 = base64_encode( $file_content ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode -- Used for file transfer.

		$download_url = self::get_download_url( $file_path, $file_name );
		if ( is_wp_error( $download_url ) ) {
			$download_url = null;
		}

		return array(
			'file_content_base64' => $file_content_base64,
			'download_url'        => $download_url,
		);
	}

	/**
	 * Get content type based on file extension
	 *
	 * @param string $extension File extension.
	 * @return string Content type.
	 */
	private static function get_content_type( $extension ) {
		$content_types = array(
			'json' => 'application/json',
			'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
			'xls'  => 'application/vnd.ms-excel',
			'ods'  => 'application/vnd.oasis.opendocument.spreadsheet',
			'csv'  => 'text/csv',
			'html' => 'text/html',
			'pdf'  => 'application/pdf',
			'txt'  => 'text/plain',
			'xml'  => 'application/xml',
		);

		return isset( $content_types[ $extension ] ) ? $content_types[ $extension ] : 'application/octet-stream';
	}
}
