<?php
/**
 * Connection Request DTO
 *
 * @category   WordPress_Plugin
 * @package    Common
 * @subpackage DTO
 * @author     miniOrange <info@miniorange.com>
 * @license    MIT/Expat
 * @link       https://plugins.miniorange.com/woocommerce-api-product-sync-with-woocommerce-rest-apis
 */

namespace MOWCPS\Common\DTO;

use WP_Error;
use MOWCPS\Common\Constants\Constants;
use MOWCPS\Common\Abstracts\Base_DTO;
use MOWCPS\Common\Utils\Utils;

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

/**
 * Class Connection_Request
 *
 * Handles connection request data transformation and validation
 */
class Connection_Request extends Base_DTO {

	/**
	 * Connection name
	 *
	 * @var string
	 */
	private $name;

	/**
	 * Connection type
	 *
	 * @var string
	 */
	private $type;

	/**
	 * Is active flag
	 *
	 * @var bool
	 */
	private $is_active;

	/**
	 * Is primary flag
	 *
	 * @var bool
	 */
	private $is_primary;

	/**
	 * Priority value
	 *
	 * @var int
	 */
	private $priority;

	/**
	 * Resource type
	 *
	 * @var string
	 */
	private $resource_type;

	/**
	 * Configuration data
	 *
	 * @var array|null
	 */
	private $configuration;

	/**
	 * Original request parameters
	 *
	 * @var array
	 */
	private $original_params;

	/**
	 * Valid connection types
	 *
	 * @var array
	 */
	private static $valid_types = array(
		Constants::CONNECTION_TYPE_API,
		Constants::CONNECTION_TYPE_FTP,
		Constants::CONNECTION_TYPE_SFTP,
		Constants::CONNECTION_TYPE_FILE,
	);

	/**
	 * Valid resource types
	 *
	 * @var array
	 */
	private static $valid_resource_types = array(
		Constants::CONNECTION_RESOURCE_TYPE_PRODUCT,
		Constants::CONNECTION_RESOURCE_TYPE_ORDER,
	);

	/**
	 * List of valid columns that can be selected in API responses.
	 *
	 * @var array
	 */
	private static $valid_columns = array(
		'uuid',
		'name',
		'type',
		'is_active',
		'is_primary',
		'priority',
		'resource_type',
		'configuration',
	);

	/**
	 * Valid file types for FILE connections.
	 *
	 * @var array
	 */
	private static $valid_file_types = array(
		Constants::CSV_FILE,
		Constants::TSV_FILE,
		Constants::TXT_FILE,
		Constants::XLSX_FILE,
		Constants::TEXT_FILE,
	);

	/**
	 * Validate and sanitize column selection for API responses.
	 *
	 * Ensures that only valid columns are requested and that required columns
	 * (uuid and name) are always included.
	 *
	 * @param string $columns_param Comma-separated list of column names to validate.
	 * @return array|WP_Error Array of validated column names or WP_Error if validation fails.
	 */
	public static function validate_parameters( $params ) {
		if ( ! is_string( $params['columns'] ) ) {
			return new WP_Error(
				Constants::ERROR_INVALID_DATA_TYPE,
				'Columns parameter must be a comma-separated string.',
				array( 'status' => Constants::HTTP_BAD_REQUEST )
			);
		}

		// Sanitize and split columns.
		$sanitized_param = sanitize_text_field( wp_unslash( $params['columns'] ) );
		$raw_columns     = explode( ',', $sanitized_param );
		$columns         = array_map( 'sanitize_key', $raw_columns );

		// Validate each column.
		$invalid_columns = array_diff( $columns, self::$valid_columns );
		if ( ! empty( $invalid_columns ) ) {
			return new WP_Error(
				Constants::ERROR_INVALID_FIELD_VALUE,
				sprintf(
					'Invalid columns: %s. Valid columns are: %s.',
					implode( ', ', $invalid_columns ),
					implode( ', ', self::$valid_columns )
				),
				array( 'status' => Constants::HTTP_BAD_REQUEST )
			);
		}

		if ( isset( $params['resource_type'] ) ) {
			$params['resource_type'] = sanitize_text_field( wp_unslash( $params['resource_type'] ) );
			if ( ! in_array( $params['resource_type'], self::$valid_resource_types, true ) ) {
				return new WP_Error(
					Constants::ERROR_INVALID_RESOURCE_TYPE,
					Constants::MSG_INVALID_RESOURCE_TYPE,
					array( 'status' => Constants::HTTP_BAD_REQUEST )
				);
			}
		}

		// Always include required columns.
		$required_columns  = array( 'uuid', 'name' );
		$params['columns'] = array_unique( array_merge( $required_columns, $columns ) );

		return $params;
	}

	/**
	 * Create instance from request parameters
	 *
	 * @param array $params Request parameters.
	 * @return self|WP_Error Instance or error if validation fails
	 */
	public static function from_request_parameters( $params ) {
		$instance = new self();

		// Transform data.
		$instance->name          = sanitize_text_field( wp_unslash( $params['name'] ?? '' ) );
		$instance->type          = sanitize_text_field( wp_unslash( $params['type'] ?? Constants::CONNECTION_TYPE_API ) );
		$instance->is_active     = isset( $params['is_active'] ) ? (bool) sanitize_text_field( wp_unslash( $params['is_active'] ) ) : false;
		$instance->is_primary    = isset( $params['is_primary'] ) ? (bool) sanitize_text_field( wp_unslash( $params['is_primary'] ) ) : false;
		$instance->priority      = isset( $params['priority'] ) ? intval( sanitize_text_field( wp_unslash( $params['priority'] ) ) ) : 0;
		$instance->resource_type = sanitize_text_field( wp_unslash( $params['resource_type'] ?? Constants::CONNECTION_RESOURCE_TYPE_PRODUCT ) );
		$instance->configuration = isset( $params['configuration'] ) ? Utils::sanitize_json_field( wp_unslash( $params['configuration'] ) ) : array();

		return $instance->validate();
	}

	/**
	 * Create instance from update parameters (for partial updates)
	 *
	 * @param array $params Request parameters.
	 * @return self|WP_Error Instance or error if validation fails
	 */
	public static function from_update_parameters( $params ) {
		$instance                  = new self();
		$instance->original_params = array();

		// Transform data - only set values if they were provided.
		if ( isset( $params['name'] ) ) {
			$instance->original_params['name'] = $params['name'];
			$instance->name                    = sanitize_text_field( wp_unslash( $params['name'] ) );
		}
		if ( isset( $params['type'] ) ) {
			$instance->type = sanitize_text_field( wp_unslash( $params['type'] ) );
		}
		if ( array_key_exists( 'is_active', $params ) ) {
			$instance->original_params['is_active'] = $params['is_active'];
			$instance->is_active                    = (bool) sanitize_text_field( wp_unslash( $params['is_active'] ) );
		}
		if ( isset( $params['is_primary'] ) ) {
			$instance->original_params['is_primary'] = $params['is_primary'];
			$instance->is_primary                    = (bool) sanitize_text_field( wp_unslash( $params['is_primary'] ) );
		}
		if ( isset( $params['priority'] ) ) {
			$instance->priority = intval( sanitize_text_field( wp_unslash( $params['priority'] ) ) );
		}
		if ( isset( $params['resource_type'] ) ) {
			$instance->original_params['resource_type'] = $params['resource_type'];
			$instance->resource_type                    = sanitize_text_field( wp_unslash( $params['resource_type'] ) );
		}
		if ( isset( $params['configuration'] ) ) {
			$config = Utils::sanitize_json_field( wp_unslash( $params['configuration'] ) );

			// Validate configuration structure.
			if ( ! is_array( $config ) ) {
				return new WP_Error(
					Constants::ERROR_INVALID_CONFIGURATION,
					'Configuration must be an object',
					array( 'status' => Constants::HTTP_BAD_REQUEST )
				);
			}

			$instance->configuration                    = $config;
			$instance->original_params['configuration'] = $config;
		}
		return $instance->validate_update();
	}

	/**
	 * Validate the request data
	 *
	 * @return self|WP_Error Instance if valid, WP_Error if invalid
	 */
	protected function validate() {
		// Validate name.
		if ( empty( $this->name ) ) {
			return new WP_Error(
				Constants::ERROR_MISSING_REQUIRED_FIELD,
				sprintf( Constants::MSG_MISSING_REQUIRED_FIELD, 'name' ),
				array( 'status' => Constants::HTTP_BAD_REQUEST )
			);
		}

		// Validate type.
		if ( ! in_array( $this->type, self::$valid_types, true ) ) {
			return new WP_Error(
				Constants::ERROR_INVALID_CONNECTION_TYPE,
				Constants::MSG_INVALID_CONNECTION_TYPE,
				array( 'status' => Constants::HTTP_BAD_REQUEST )
			);
		}

		// Validate resource_type.
		if ( ! in_array( $this->resource_type, self::$valid_resource_types, true ) ) {
			return new WP_Error(
				Constants::ERROR_INVALID_RESOURCE_TYPE,
				Constants::MSG_INVALID_RESOURCE_TYPE,
				array( 'status' => Constants::HTTP_BAD_REQUEST )
			);
		}

		// Validate priority.
		if ( $this->priority <= 0 ) {
			return new WP_Error(
				Constants::ERROR_INVALID_PRIORITY,
				Constants::MSG_INVALID_PRIORITY,
				array( 'status' => Constants::HTTP_BAD_REQUEST )
			);
		}

		// Validate configuration.
		if ( null !== $this->configuration && ! is_array( $this->configuration ) ) {
			return new WP_Error(
				Constants::ERROR_INVALID_CONFIGURATION,
				Constants::MSG_INVALID_CONFIGURATION,
				array( 'status' => Constants::HTTP_BAD_REQUEST )
			);
		}

		return $this;
	}

	/**
	 * Validate update request data - only validates fields that are present
	 *
	 * @return self|WP_Error Instance if valid, WP_Error if invalid
	 */
	protected function validate_update() {
		// Only validate name if provided.
		if ( isset( $this->original_params['name'] ) && empty( $this->name ) ) {
			return new WP_Error(
				Constants::ERROR_MISSING_REQUIRED_FIELD,
				sprintf( Constants::MSG_MISSING_REQUIRED_FIELD, 'name' ),
				array( 'status' => Constants::HTTP_BAD_REQUEST )
			);
		}

		// Type cannot be updated after creation.
		if ( isset( $this->original_params['type'] ) ) {
			return new WP_Error(
				Constants::ERROR_INVALID_REQUEST,
				'Connection type cannot be updated after creation.',
				array( 'status' => Constants::HTTP_BAD_REQUEST )
			);
		}

		// Only validate resource_type if provided.
		if ( isset( $this->original_params['resource_type'] ) && ! in_array( $this->resource_type, self::$valid_resource_types, true ) ) {
			return new WP_Error(
				Constants::ERROR_INVALID_RESOURCE_TYPE,
				Constants::MSG_INVALID_RESOURCE_TYPE,
				array( 'status' => Constants::HTTP_BAD_REQUEST )
			);
		}

		// Only validate priority if provided.
		if ( isset( $this->original_params['priority'] ) && $this->priority <= 0 ) {
			return new WP_Error(
				Constants::ERROR_INVALID_PRIORITY,
				Constants::MSG_INVALID_PRIORITY,
				array( 'status' => Constants::HTTP_BAD_REQUEST )
			);
		}

		// Only validate configuration if provided.
		if ( isset( $this->original_params['configuration'] ) && ! is_array( $this->configuration ) ) {
			return new WP_Error(
				Constants::ERROR_INVALID_CONFIGURATION,
				Constants::MSG_INVALID_CONFIGURATION,
				array( 'status' => Constants::HTTP_BAD_REQUEST )
			);
		}

		return $this;
	}

	/**
	 * Convert to array for database
	 *
	 * @return array Data ready for database
	 */
	public function to_array() {
		$data = array();

		// For update requests, only include fields that were provided.
		if ( isset( $this->original_params ) ) {
			if ( isset( $this->original_params['name'] ) ) {
				$data['name'] = $this->name;
			}
			// Type cannot be updated after creation.
			if ( array_key_exists( 'is_active', $this->original_params ) ) {
				$data['is_active'] = $this->is_active ? 1 : 0;
			}
			if ( isset( $this->original_params['is_primary'] ) ) {
				$data['is_primary'] = $this->is_primary ? 1 : 0;
			}
			if ( isset( $this->original_params['priority'] ) ) {
				$data['priority'] = $this->priority;
			}
			if ( isset( $this->original_params['resource_type'] ) ) {
				$data['resource_type'] = $this->resource_type;
			}
			if ( isset( $this->configuration ) ) {
				$data['configuration'] = $this->configuration;
			}
		} else {
			// For create requests, include all fields.
			$data = array(
				'name'          => $this->name,
				'type'          => $this->type,
				'is_active'     => $this->is_active ? 1 : 0,
				'is_primary'    => $this->is_primary ? 1 : 0,
				'priority'      => $this->priority,
				'resource_type' => $this->resource_type,
				'configuration' => $this->configuration,
			);
		}

		return $data;
	}

	/**
	 * Validate FILE connection configuration
	 *
	 * Validates file_name and file_type for FILE connections.
	 * Used when saving a new file or reading from existing file.
	 *
	 * @param array $configuration Connection configuration.
	 * @param bool  $is_manual_upload Whether this is a manual file upload (true) or reading from existing file (false).
	 * @return true|WP_Error True if valid, WP_Error if invalid
	 */
	public static function validate_file_connection_config( $configuration, $is_manual_upload = false ) {
		// Validate file_name is not empty.
		if ( empty( $configuration['file_name'] ) ) {
			return new WP_Error(
				'file_name_not_found',
				'File name is not configured in connection settings.',
				array( 'status' => Constants::HTTP_BAD_REQUEST )
			);
		}

		// Validate file_type is not empty.
		$file_type = strtolower( $configuration['file_type'] ?? '' );
		if ( empty( $file_type ) ) {
			return new WP_Error(
				'file_type_not_found',
				'File type is not configured in connection settings.',
				array( 'status' => Constants::HTTP_BAD_REQUEST )
			);
		}

		// Validate file_type is in allowed file types.
		if ( ! in_array( $file_type, self::$valid_file_types, true ) ) {
			return new WP_Error(
				Constants::ERROR_INVALID_FIELD_VALUE,
				sprintf( Constants::MSG_INVALID_FIELD_VALUE, 'file_type' ) . ' Only CSV, TSV, TXT, and XLSX file types are supported.',
				array(
					'status'             => Constants::HTTP_BAD_REQUEST,
					'provided_file_type' => $configuration['file_type'],
					'allowed_file_types' => self::$valid_file_types,
				)
			);
		}

		return true;
	}

	/**
	 * Validate chunked upload request data
	 *
	 * Validates chunk_number, total_chunks, chunk_data, file_name, and file_size
	 * for chunked file upload requests.
	 *
	 * @param array $request_body Request body containing chunk data.
	 * @return true|WP_Error True if valid, WP_Error if invalid
	 */
	public static function validate_chunked_upload_data( $request_body ) {
		// Validate chunk_number.
		$chunk_number = intval( $request_body['chunk_number'] ?? 0 );
		if ( $chunk_number <= 0 ) {
			return new WP_Error(
				Constants::ERROR_INVALID_CHUNK_DATA,
				'Invalid chunk number. Chunk number must be a positive integer.',
				array( 'status' => Constants::HTTP_BAD_REQUEST )
			);
		}

		// Validate total_chunks.
		$total_chunks = intval( $request_body['total_chunks'] ?? 0 );
		if ( $total_chunks <= 0 ) {
			return new WP_Error(
				Constants::ERROR_INVALID_CHUNK_DATA,
				'Invalid total chunks. Total chunks must be a positive integer.',
				array( 'status' => Constants::HTTP_BAD_REQUEST )
			);
		}

		// Validate chunk_number is within expected range.
		if ( $chunk_number > $total_chunks ) {
			return new WP_Error(
				Constants::ERROR_INVALID_CHUNK_NUMBER,
				sprintf( 'Chunk number %d exceeds total chunks %d.', $chunk_number, $total_chunks ),
				array( 'status' => Constants::HTTP_BAD_REQUEST )
			);
		}

		// Validate chunk_data is not empty.
		$chunk_data = $request_body['chunk_data'] ?? '';
		if ( empty( $chunk_data ) ) {
			return new WP_Error(
				Constants::ERROR_INVALID_CHUNK_DATA,
				'Chunk data is required.',
				array( 'status' => Constants::HTTP_BAD_REQUEST )
			);
		}

		// Validate file_name is not empty.
		$file_name = sanitize_file_name( $request_body['file_name'] ?? '' );
		if ( empty( $file_name ) ) {
			return new WP_Error(
				Constants::ERROR_INVALID_CHUNK_DATA,
				'File name is required.',
				array( 'status' => Constants::HTTP_BAD_REQUEST )
			);
		}

		// Validate file_size is a positive integer.
		$file_size = intval( $request_body['file_size'] ?? 0 );
		if ( $file_size <= 0 ) {
			return new WP_Error(
				Constants::ERROR_INVALID_CHUNK_DATA,
				'File size must be a positive integer.',
				array( 'status' => Constants::HTTP_BAD_REQUEST )
			);
		}

		return true;
	}
}
