<?php
/**
 * Tool Controller for Warranty Cart Admin REST API
 *
 * Handles tool-related REST API operations.
 *
 * @package WarrantyCart
 */

namespace WarrantyCart\REST\Controllers;

use WarrantyCart\REST\Controller;
use WarrantyCart\Core\Repository;
use WarrantyCart\REST\Csv;
use WC_Order;
use WP_Error;
use WP_REST_Request;
use WP_REST_Response;
use WP_REST_Server;

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

/**
 * Tools class
 */
class Tools extends Controller {

	/**
	 * Constructor
	 */
	public function __construct() {
		$this->rest_base = 'admin';
	}

	/**
	 * Register routes
	 */
	public function register_routes(): void {
		// Tools endpoints
		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/tools/import',
			array(
				array(
					'methods'             => WP_REST_Server::CREATABLE,
					'callback'            => array( $this, 'import_csv' ),
					'permission_callback' => array( $this, 'check_admin_permission' ),
					'args'                => $this->get_import_args(),
				),
			)
		);

		// Sample CSV download endpoint
		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/tools/sample-csv',
			array(
				array(
					'methods'             => WP_REST_Server::READABLE,
					'callback'            => array( $this, 'download_sample_csv' ),
					'permission_callback' => array( $this, 'check_admin_permission' ),
				),
			)
		);

		// Clear cache endpoint
		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/tools/clear-cache',
			array(
				array(
					'methods'             => WP_REST_Server::CREATABLE,
					'callback'            => array( $this, 'clear_cache' ),
					'permission_callback' => array( $this, 'check_admin_permission' ),
				),
			)
		);

		// System status endpoint
		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/tools/system-status',
			array(
				array(
					'methods'             => WP_REST_Server::READABLE,
					'callback'            => array( $this, 'get_system_status' ),
					'permission_callback' => array( $this, 'check_admin_permission' ),
				),
			)
		);

		// Database repair endpoint
		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/tools/database-repair',
			array(
				array(
					'methods'             => WP_REST_Server::CREATABLE,
					'callback'            => array( $this, 'repair_database' ),
					'permission_callback' => array( $this, 'check_admin_permission' ),
				),
			)
		);

		// Export data endpoint
		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/tools/export-data',
			array(
				array(
					'methods'             => WP_REST_Server::READABLE,
					'callback'            => array( $this, 'export_warranty_data' ),
					'permission_callback' => array( $this, 'check_admin_permission' ),
				),
			)
		);
	}

	/**
	 * Get import endpoint arguments.
	 *
	 * @return array
	 */
	private function get_import_args(): array {
		return array(
			'csv_file' => array(
				'required'          => true,
				'description'       => __( 'CSV file to import.', 'warranty-cart' ),
				'type'              => 'file',
				'validate_callback' => array( $this, 'validate_csv_file' ),
			),
		);
	}

	/**
	 * Import CSV file and create warranty products.
	 *
	 * @param WP_REST_Request $request REST request object.
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function import_csv( WP_REST_Request $request ) {
		$files = $request->get_file_params();

		if ( empty( $files['csv_file'] ) ) {
			return $this->error_response( __( 'No file uploaded.', 'warranty-cart' ), 400 );
		}

		$file = $files['csv_file'];

		// Validate file
		if ( UPLOAD_ERR_OK !== $file['error'] ) {
			return $this->error_response( __( 'File upload error.', 'warranty-cart' ), 400 );
		}

		// Check if product import functionality is available
		if ( ! isset( warranty_cart()->product_import ) ) {
			return $this->error_response( __( 'Product import functionality not available.', 'warranty-cart' ), 500 );
		}

		// Create temporary file using unified filesystem methods
		$file_contents = warranty_cart()->read_file( $file['tmp_name'] );
		if ( false === $file_contents ) {
			return $this->error_response( __( 'Failed to read uploaded file.', 'warranty-cart' ), 500 );
		}

		$temp_file = wp_tempnam();
		if ( ! warranty_cart()->write_file( $temp_file, $file_contents ) ) {
			return $this->error_response( __( 'Failed to process uploaded file.', 'warranty-cart' ), 500 );
		}

		// Process the CSV import
		try {
			/*
			 * The category is a setting now, not a literal.
			 *
			 * This passed 'warranty' regardless, so every imported product
			 * landed in a category the shop had not chosen and might not have —
			 * `wp_set_object_terms()` creates it, so importing quietly added a
			 * category to the catalogue as a side effect.
			 */
			$category = (string) get_option( 'warranty_cart_import_category', 'warranty' );
			$category = '' !== trim( $category ) ? $category : 'warranty';

			$results = warranty_cart()->product_import->import_report( $temp_file, $category );

			$rejected = $results['skipped'] + $results['failed'];

			/*
			 * What happened to every row, not only the ones that worked.
			 *
			 * `failed` was hardcoded to 0 and `errors` to an empty array, so a
			 * hundred-row file with forty bad rows reported sixty imported and
			 * nothing else — the shop had no way to learn the other forty were
			 * gone, let alone which.
			 */
			$formatted_results = array(
				'total'      => $results['imported'] + $rejected,
				'successful' => $results['imported'],
				'failed'     => $rejected,

				// Capped: a broken file can produce a line of explanation per
				// row, and a response holding ten thousand of them helps nobody.
				'errors'     => array_slice( $results['errors'], 0, 20 ),
				'message'    => $rejected > 0
					? sprintf(
						/* translators: 1: number imported, 2: number skipped. */
						__( 'Imported %1$d warranty products. %2$d rows were skipped.', 'warranty-cart' ),
						$results['imported'],
						$rejected
					)
					: sprintf(
						/* translators: %d: number of imported products */
						__( 'Successfully imported %d warranty products.', 'warranty-cart' ),
						$results['imported']
					),
			);
		} catch ( \Throwable $e ) {
			wp_delete_file( $temp_file );

			return $this->error_response( $e->getMessage(), 500 );
		}

		// Clean up temp file
		wp_delete_file( $temp_file );

		return $this->success_response( $formatted_results );
	}

	/**
	 * Validate CSV file before import.
	 *
	 * @param array $file File data from upload.
	 *
	 * @return bool True if file is valid, false otherwise.
	 */
	public function validate_csv_file( $file ): bool {
		if ( empty( $file['name'] ) ) {
			return false;
		}

		// Check file extension
		$extension = strtolower( pathinfo( $file['name'], PATHINFO_EXTENSION ) );
		if ( 'csv' !== $extension ) {
			return false;
		}

		/*
		 * The limit from the settings screen, not a constant.
		 *
		 * "Maximum file size" sat on the Settings page reading 5 MB while this
		 * check enforced a hardcoded 5 MB beside it — raising the setting did
		 * nothing, and the import rejected the file with no explanation of
		 * which limit it had hit.
		 */
		$max_mb = (int) get_option( 'warranty_max_file_size', 5 );
		$max_mb = max( 1, $max_mb );

		if ( $max_mb * MB_IN_BYTES < $file['size'] ) {
			return false;
		}

		return true;
	}

	/**
	 * The sample CSV an importer can fill in.
	 *
	 * @param WP_REST_Request $request REST request object.
	 *
	 * @return WP_REST_Response
	 */
	public function download_sample_csv( WP_REST_Request $request ): WP_REST_Response {
		/*
		 * Built by the same CSV writer as every export.
		 *
		 * This hand-assembled the file as a string and `echo`ed it after
		 * setting headers, then `exit`ed from inside the REST callback — so the
		 * sample a shop owner downloads to learn the import format was quoted
		 * by different rules than the exports it is meant to mirror, and had no
		 * byte-order mark, so a term typed in Excel came back mojibake.
		 */
		$header = array(
			'name',
			'description',
			'price',
			'warranty_type',
			'duration',
			'duration_unit',
			'coverage_details',
			'associated_warranty',
			'service_priority',
		);

		/*
		 * Rows that show the range of the format rather than one shape of it:
		 * both warranty types, all three duration units, a plan with an
		 * associated warranty and one without.
		 */
		$rows = array(
			array(
				'Extended Warranty - 2 Years',
				'Extended warranty coverage for 2 years',
				'49.99',
				'main_warranty',
				'2',
				'years',
				'Comprehensive coverage including parts and labour',
				'',
				'',
			),
			array(
				'Seasonal Cover - 6 Months',
				'Half a year of cover for equipment used in one season',
				'24.00',
				'main_warranty',
				'6',
				'months',
				'Mechanical and electrical failure',
				'',
				'',
			),
			array(
				'Starter Cover - 90 Days',
				'Three months of cover for early faults',
				'9.00',
				'main_warranty',
				'90',
				'days',
				'Manufacturing defects',
				'',
				'',
			),
			array(
				'Additional Support Service',
				'Priority technical support',
				'19.99',
				'additional_service',
				'1',
				'years',
				'24/7 technical support',
				'Extended Warranty - 2 Years',
				'recommended',
			),
		);

		return $this->file_response(
			'warranty-products-sample.csv',
			Csv::build( $header, $rows )
		);
	}

	/**
	 * Clear all plugin caches and transients.
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function clear_cache() {
		try {
			global $wpdb;

			// Clear plugin-specific transients
			$wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_warranty_%'" ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Bulk cleanup operation requires direct query
			$wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_timeout_warranty_%'" ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Bulk cleanup operation requires direct query

			// Clear specific transients
			delete_transient( 'warranty_sales_data' );
			delete_transient( 'warranty_dashboard_data' );
			delete_transient( 'warranty_reports_data' );
			delete_transient( 'warranty_product_count' );
			delete_transient( 'warranty_order_count' );

			// Clear WooCommerce product cache for warranty products
			if ( function_exists( 'wc_delete_product_transients' ) ) {
				$warranty_products = warranty_cart()->repository->get_warranty_product_ids();
				foreach ( $warranty_products as $product_id ) {
					wc_delete_product_transients( $product_id );
				}
			}

			// Clear object cache if available
			if ( function_exists( 'wp_cache_flush' ) ) {
				wp_cache_flush();
			}

			// Clear opcode cache if available
			if ( function_exists( 'opcache_reset' ) ) {
				opcache_reset();
			}

			return $this->success_response(
				array(
					'message'       => __( 'Cache cleared successfully.', 'warranty-cart' ),
					'cleared_items' => array(
						'transients'   => true,
						'object_cache' => function_exists( 'wp_cache_flush' ),
						'opcode_cache' => function_exists( 'opcache_reset' ),
					),
				)
			);
		} catch ( \Throwable $e ) {
			return $this->error_response( $e->getMessage(), 500 );
		}
	}

	/**
	 * Get system status information.
	 *
	 * @param WP_REST_Request $request REST request object.
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function get_system_status( WP_REST_Request $request ) {
		global $wpdb;

		$status = array(
			'php_version'         => PHP_VERSION,
			'wordpress_version'   => get_bloginfo( 'version' ),
			'woocommerce_version' => function_exists( 'WC' ) ? WC()->version : 'Not installed',
			'mysql_version'       => $wpdb->db_version(),
			'memory_limit'        => ini_get( 'memory_limit' ),
			'max_execution_time'  => ini_get( 'max_execution_time' ),
			'upload_max_filesize' => ini_get( 'upload_max_filesize' ),
			'post_max_size'       => ini_get( 'post_max_size' ),
			'wp_debug'            => defined( 'WP_DEBUG' ) && WP_DEBUG ? 'Enabled' : 'Disabled',
			'wp_cache'            => defined( 'WP_CACHE' ) && WP_CACHE ? 'Enabled' : 'Disabled',
			'opcache_enabled'     => function_exists( 'opcache_get_status' ) && opcache_get_status() ? 'Enabled' : 'Disabled',
			'warranty_products'   => count( warranty_cart()->repository->get_warranty_product_ids() ),
			'warranty_orders'     => warranty_cart()->repository->get_total_warranty_orders(),
		);

		// Check requirements
		$requirements = array(
			'php_version_ok'     => version_compare( PHP_VERSION, '7.4', '>=' ),
			'wordpress_ok'       => version_compare( get_bloginfo( 'version' ), '5.0', '>=' ),
			'woocommerce_ok'     => version_compare( WC()->version, '5.0', '>=' ),
			'memory_ok'          => wp_convert_hr_to_bytes( ini_get( 'memory_limit' ) ) >= 128 * 1024 * 1024, // 128MB
			'mysql_ok'           => function_exists( 'WC' ) && version_compare( $wpdb->db_version(), '5.6', '>=' ),
			'upload_size_ok'     => wp_convert_hr_to_bytes( ini_get( 'upload_max_filesize' ) ) >= 5 * 1024 * 1024, // 5MB
			'execution_time_ok'  => ini_get( 'max_execution_time' ) >= 30 || ini_get( 'max_execution_time' ) === 0,
			'https_enabled'      => is_ssl(),
			'permalinks_enabled' => get_option( 'permalink_structure' ) !== '',
		);

		return $this->success_response(
			array(
				'status'       => $status,
				'requirements' => $requirements,
			)
		);
	}

	/**
	 * Repair database tables.
	 *
	 * @param WP_REST_Request $request REST request object.
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function repair_database( WP_REST_Request $request ) {
		try {
			global $wpdb;

			// Get all tables
			$tables = $wpdb->get_col( 'SHOW TABLES' ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Administrative operation requires direct database access

			$repaired_tables = array();
			$errors          = array();

			foreach ( $tables as $table ) {
				// Only repair essential WooCommerce and WordPress core tables that might be related to warranties
				if ( strpos( $table, $wpdb->prefix . 'posts' ) === 0 ||
					strpos( $table, $wpdb->prefix . 'postmeta' ) === 0 ||
					strpos( $table, $wpdb->prefix . 'woocommerce_' ) === 0 ||
					strpos( $table, $wpdb->prefix . 'wc_' ) === 0 ) {
					// Check if table exists and is accessible before repairing
					$table_exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Administrative operation requires direct database access
					if ( $table_exists ) {
						$result = $wpdb->query( $wpdb->prepare( 'REPAIR TABLE %s', $table ) ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Database maintenance operation requires direct access
						if ( false !== $result ) {
							$repaired_tables[] = $table;
						} else {
							$errors[] = "Failed to repair table: {$table}";
						}
					}
				}
			}

			// Optimize tables
			$optimized_tables = array();
			foreach ( $repaired_tables as $table ) {
				$result = $wpdb->query( $wpdb->prepare( 'OPTIMIZE TABLE %s', $table ) ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Database maintenance operation requires direct access
				if ( false !== $result ) {
					$optimized_tables[] = $table;
				}
			}

			return $this->success_response(
				array(
					'repaired_tables'  => $repaired_tables,
					'optimized_tables' => $optimized_tables,
					'errors'           => $errors,
					'message'          => sprintf(
					/* translators: 1: number of repaired tables, 2: number of optimized tables */
						__( 'Repaired %1$d tables and optimized %2$d tables.', 'warranty-cart' ),
						count( $repaired_tables ),
						count( $optimized_tables )
					),
				)
			);
		} catch ( \Throwable $e ) {
			return $this->error_response( $e->getMessage(), 500 );
		}
	}

	/**
	 * Export warranty data to CSV format.
	 *
	 * @param WP_REST_Request $request REST request object.
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function export_warranty_data( WP_REST_Request $request ) {
		/*
		 * Every warranty order, from the repository.
		 *
		 * This asked WooCommerce for the single newest order in the shop and
		 * treated "it has no warranty on it" as "the shop has sold none",
		 * answering 404 whenever the most recent sale happened to be a plain
		 * one. It then loaded every order in the shop again to build the file.
		 */
		$orders = warranty_cart()->repository->get_formatted_warranty_orders(
			array(
				'limit'   => -1,
				'orderby' => 'date',
				'order'   => 'DESC',
			),
			false
		);

		if ( empty( $orders ) ) {
			return $this->error_response( __( 'No warranty orders found to export.', 'warranty-cart' ), 404 );
		}

		$header = array(
			__( 'Order ID', 'warranty-cart' ),
			__( 'Order Date', 'warranty-cart' ),
			__( 'Status', 'warranty-cart' ),
			__( 'Customer Name', 'warranty-cart' ),
			__( 'Email', 'warranty-cart' ),
			__( 'Warranty', 'warranty-cart' ),
			__( 'SKU', 'warranty-cart' ),
			__( 'Quantity', 'warranty-cart' ),
			__( 'Price', 'warranty-cart' ),
			__( 'Currency', 'warranty-cart' ),
			__( 'Warranty Type', 'warranty-cart' ),
			__( 'Duration', 'warranty-cart' ),
			__( 'Duration Unit', 'warranty-cart' ),
		);

		$rows = array();

		foreach ( $orders as $order ) {
			if ( ! $order instanceof WC_Order ) {
				continue;
			}

			$warranty_data = warranty_cart()->repository->get_order_warranty_items( $order );
			$created       = $order->get_date_created();

			foreach ( $warranty_data['items'] as $item ) {
				$product = wc_get_product( $item['product_id'] );

				if ( ! $product ) {
					continue;
				}

				$type = Repository::product_type( $product );

				$rows[] = array(
					$order->get_id(),
					$created ? $created->date( 'Y-m-d H:i:s' ) : '',
					\wc_get_order_status_name( $order->get_status() ),
					trim( $order->get_billing_first_name() . ' ' . $order->get_billing_last_name() ),
					$order->get_billing_email(),
					$item['product_name'],
					$product->get_sku(),
					$item['quantity'],
					\wc_format_decimal( (float) $item['total'], \wc_get_price_decimals() ),
					$order->get_currency(),
					'' !== $type ? $type : 'main_warranty',
					$product->get_meta( '_warranty_cart_duration', true ),
					$product->get_meta( '_warranty_cart_duration_unit', true ),
				);
			}
		}

		return $this->file_response(
			$this->export_filename( 'warranty-data' ),
			Csv::build( $header, $rows )
		);
	}
}
