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

namespace MOWCPS\Common\Models;

use MOWCPS\Common\Interfaces\Model_Interface;
use MOWCPS\Common\Constants\Constants;
use WP_Error;
use MOWCPS\Common\Utils\Model_Utils;

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

/**
 * Class Attribute_Mapping_Model
 *
 * Handles attribute mapping data operations
 */
class Attribute_Mapping_Model implements Model_Interface {

	/**
	 * Database table name
	 *
	 * @var string
	 */
	private static $table_name = Model_Utils::TABLE_ATTRIBUTE_MAPPING;

	/**
	 * Valid product types
	 *
	 * @var array
	 */
	private static $valid_product_types = array(
		Constants::ATTRIBUTE_MAPPING_PRODUCT_TYPE_PARENT,
		Constants::ATTRIBUTE_MAPPING_PRODUCT_TYPE_VARIANT,
	);

	/**
	 * Valid mapping types
	 *
	 * @var array
	 */
	private static $valid_types = array(
		Constants::ATTRIBUTE_MAPPING_TYPE_CORE,
		Constants::ATTRIBUTE_MAPPING_TYPE_ATTRIBUTE,
		Constants::ATTRIBUTE_MAPPING_TYPE_TAXONOMY,
		Constants::ATTRIBUTE_MAPPING_TYPE_CUSTOM,
		Constants::ATTRIBUTE_MAPPING_TYPE_INTEGRATION,
	);

	/**
	 * Check if an attribute mapping already exists.
	 *
	 * @param array  $data   The mapping data to check.
	 * @param string $exclude_uuid Optional UUID to exclude from check (for updates).
	 * @return true|WP_Error True if no duplicate exists, WP_Error if duplicate found or error occurs.
	 */
	private static function check_duplicate_mapping( $data, $exclude_uuid = null ) {
		global $wpdb;

		$table_name       = Model_Utils::get_table_name( self::$table_name );
		$where_conditions = array(
			'supplier_id = %d',
			'product_type = %s',
			'type = %s',
			'wc_attribute = %s',
		);
		$prepare_values   = array(
			$data['supplier_id'],
			$data['product_type'],
			$data['type'],
			$data['wc_attribute'],
		);

		if ( $exclude_uuid ) {
			$where_conditions[] = 'uuid != %s';
			$prepare_values[]   = $exclude_uuid;
		}

		$table_name = esc_sql( $table_name );
		$query      = $wpdb->prepare(
			"SELECT COUNT(*) FROM `{$table_name}` WHERE " . implode( ' AND ', $where_conditions ), // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- dynamic WHERE conditions are validated earlier
			...$prepare_values
		);

		$count = $wpdb->get_var( $query ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared -- Custom query.

		if ( null === $count ) {
			return new WP_Error(
				Constants::ERROR_DB_ERROR,
				sprintf( Constants::MSG_DB_ERROR, $wpdb->last_error ),
				array( 'status' => Constants::HTTP_INTERNAL_SERVER_ERROR )
			);
		}

		if ( $count > 0 ) {
			return new WP_Error(
				Constants::ERROR_DUPLICATE_ENTRY,
				sprintf( 'Duplicate attribute mapping: The WooCommerce attribute \'%s\' is already mapped for this supplier.', $data['wc_attribute'] ),
				array( 'status' => Constants::HTTP_CONFLICT )
			);
		}

		return true;
	}

	/**
	 * Get all attribute mappings
	 *
	 * @param array $args Query parameters.
	 * {
	 *     @type int|string $id            Filter by ID.
	 *     @type int|string $supplier_id   Filter by supplier ID.
	 *     @type int|string $connection_id Filter by connection ID.
	 *     @type string     $product_type  Filter by product type.
	 *     @type string     $type          Filter by mapping type.
	 *     @type string|array $ids         Filter by comma-separated IDs or array of IDs.
	 * }
	 * @return array List of attribute mappings
	 */
	public static function get_all( $args = array() ) {
		global $wpdb;

		$table_name = Model_Utils::get_table_name( self::$table_name );

		$defaults = array(
			'supplier_id'   => '',
			'connection_id' => '',
			'product_type'  => '',
			'type'          => '',
			'ids'           => '',
		);

		$args = wp_parse_args( $args, $defaults );

		$where_conditions = array();
		$prepare_values   = array();

		// Build WHERE clause.
		if ( ! empty( $args['supplier_id'] ) ) {
			$where_conditions[] = 'supplier_id = %d';
			$prepare_values[]   = $args['supplier_id'];
		}

		if ( ! empty( $args['connection_id'] ) ) {
			$where_conditions[] = 'connection_id = %d';
			$prepare_values[]   = $args['connection_id'];
		}

		if ( ! empty( $args['product_type'] ) && in_array( $args['product_type'], self::$valid_product_types, true ) ) {
			$where_conditions[] = 'product_type = %s';
			$prepare_values[]   = $args['product_type'];
		}

		if ( ! empty( $args['type'] ) && in_array( $args['type'], self::$valid_types, true ) ) {
			$where_conditions[] = 'type = %s';
			$prepare_values[]   = $args['type'];
		}

		// Filter by comma-separated attribute mapping IDs.
		if ( ! empty( $args['ids'] ) ) {
			$ids = $args['ids'];

			// Handle both comma-separated string and array formats.
			if ( is_string( $ids ) ) {
				$ids = explode( ',', $ids );
			}

			// Clean and validate IDs.
			$ids = array_map( 'intval', $ids );
			$ids = array_filter(
				$ids,
				function ( $id ) {
					return $id > 0;
				}
			);

			if ( ! empty( $ids ) ) {
				$placeholders       = implode( ',', array_fill( 0, count( $ids ), '%d' ) );
				$where_conditions[] = "id IN ({$placeholders})";
				$prepare_values     = array_merge( $prepare_values, $ids );
			}
		}

		// Build the query.
		$query = "SELECT * FROM `{$table_name}`";
		if ( ! empty( $where_conditions ) ) {
			$query .= ' WHERE ' . implode( ' AND ', $where_conditions );
		}

		if ( ! empty( $prepare_values ) ) {
			$results = $wpdb->get_results( $wpdb->prepare( $query, ...$prepare_values ), ARRAY_A ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared -- Custom query.
		} else {
			$results = $wpdb->get_results( $wpdb->prepare( $query ), ARRAY_A ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared -- Custom query.
		}

		// Decode JSON fields.
		$results = Model_Utils::decode_json_fields( $results, array( 'advanced_configuration' ) );

		return $results ? $results : array();
	}

	/**
	 * Get attribute mapping by UUID
	 *
	 * @param string $uuid The mapping UUID.
	 * @return array|null Mapping data or null if not found
	 */
	public static function get_by_uuid( $uuid ) {
		global $wpdb;

		$table_name = Model_Utils::get_table_name( self::$table_name );

		$mapping = $wpdb->get_row(
			$wpdb->prepare(
				"SELECT * FROM `{$table_name}` WHERE uuid = %s",
				$uuid
			),
			ARRAY_A
		);

		// Decode JSON fields.
		$mapping = Model_Utils::decode_json_fields( $mapping, array( 'advanced_configuration' ) );

		return $mapping;
	}

	/**
	 * Create new attribute mapping
	 *
	 * @param array $data The mapping data.
	 * @return array|WP_Error Created mapping data or error
	 */
	public static function create( $data ) {
		global $wpdb;

		$table_name = Model_Utils::get_table_name( self::$table_name );

		// Prepare advanced_configuration as JSON.
		$advanced_configuration = null;
		if ( isset( $data['advanced_configuration'] ) && is_array( $data['advanced_configuration'] ) ) {
			$advanced_configuration = wp_json_encode( $data['advanced_configuration'] );
		}

		$insert_data = array(
			'uuid'                   => wp_generate_uuid4(),
			'supplier_id'            => intval( $data['supplier_id'] ),
			'connection_id'          => intval( $data['connection_id'] ),
			'product_type'           => $data['product_type'],
			'type'                   => $data['type'],
			'wc_attribute'           => $data['wc_attribute'],
			'supplier_attribute'     => $data['supplier_attribute'],
			'advanced_configuration' => $advanced_configuration,
		);

		// Check for duplicate mapping.
		$duplicate_check = self::check_duplicate_mapping( $insert_data );
		if ( is_wp_error( $duplicate_check ) ) {
			return $duplicate_check;
		}

		$result = $wpdb->insert( //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom query.
			$table_name,
			$insert_data,
			array( '%s', '%d', '%d', '%s', '%s', '%s', '%s', '%s' )
		);

		if ( false === $result ) {
			return new WP_Error(
				Constants::ERROR_DB_ERROR,
				sprintf( Constants::MSG_DB_ERROR, $wpdb->last_error ),
				array( 'status' => Constants::HTTP_INTERNAL_SERVER_ERROR )
			);
		}

		return self::get_by_uuid( $insert_data['uuid'] );
	}

	/**
	 * Update attribute mapping
	 *
	 * @param string $uuid The mapping UUID.
	 * @param array  $data The mapping data.
	 * @return array|WP_Error Updated mapping data or error
	 */
	public static function update( $uuid, $data ) {
		global $wpdb;

		$table_name = Model_Utils::get_table_name( self::$table_name );

		$existing = self::get_by_uuid( $uuid );
		if ( ! $existing ) {
			return new WP_Error(
				Constants::ERROR_RECORD_NOT_FOUND,
				Constants::MSG_RECORD_NOT_FOUND,
				array( 'status' => Constants::HTTP_NOT_FOUND )
			);
		}

		$update_data = array();
		$formats     = array();

		if ( isset( $data['connection_id'] ) ) {
			$update_data['connection_id'] = intval( $data['connection_id'] );
			$formats[]                    = '%d';
		}

		if ( isset( $data['product_type'] ) ) {
			$update_data['product_type'] = $data['product_type'];
			$formats[]                   = '%s';
		}

		if ( isset( $data['type'] ) ) {
			$update_data['type'] = $data['type'];
			$formats[]           = '%s';
		}

		if ( isset( $data['wc_attribute'] ) ) {
			$update_data['wc_attribute'] = $data['wc_attribute'];
			$formats[]                   = '%s';
		}

		if ( isset( $data['supplier_attribute'] ) ) {
			$update_data['supplier_attribute'] = $data['supplier_attribute'];
			$formats[]                         = '%s';
		}

		// Update advanced configuration if provided.
		if ( isset( $data['advanced_configuration'] ) ) {
			if ( is_array( $data['advanced_configuration'] ) ) {
				$update_data['advanced_configuration'] = wp_json_encode( $data['advanced_configuration'] );
			} else {
				$update_data['advanced_configuration'] = null;
			}
			$formats[] = '%s';
		}

		if ( empty( $update_data ) ) {
			return new WP_Error(
				Constants::ERROR_MISSING_REQUIRED_FIELD,
				Constants::MSG_MISSING_REQUIRED_FIELD,
				array( 'status' => Constants::HTTP_BAD_REQUEST )
			);
		}

		// Check for duplicate mapping if relevant fields are being updated.
		if ( isset( $update_data['supplier_id'] ) || isset( $update_data['product_type'] ) ||
			isset( $update_data['type'] ) || isset( $update_data['wc_attribute'] ) ) {
			$check_data = array(
				'supplier_id'  => $update_data['supplier_id'] ?? $existing['supplier_id'],
				'product_type' => $update_data['product_type'] ?? $existing['product_type'],
				'type'         => $update_data['type'] ?? $existing['type'],
				'wc_attribute' => $update_data['wc_attribute'] ?? $existing['wc_attribute'],
			);

			$duplicate_check = self::check_duplicate_mapping( $check_data, $uuid );
			if ( is_wp_error( $duplicate_check ) ) {
				return $duplicate_check;
			}
		}

		$result = $wpdb->update( //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom query.
			$table_name,
			$update_data,
			array( 'uuid' => $uuid ),
			$formats,
			array( '%s' )
		);

		if ( false === $result ) {
			return new WP_Error(
				Constants::ERROR_DB_ERROR,
				sprintf( Constants::MSG_DB_ERROR, $wpdb->last_error ),
				array( 'status' => Constants::HTTP_INTERNAL_SERVER_ERROR )
			);
		}

		return self::get_by_uuid( $uuid );
	}

	/**
	 * Delete attribute mapping
	 *
	 * @param string $uuid The mapping UUID.
	 * @return bool|WP_Error True on success, WP_Error on failure
	 */
	public static function delete( $uuid ) {
		global $wpdb;

		$table_name = Model_Utils::get_table_name( self::$table_name );

		$existing = self::get_by_uuid( $uuid );
		if ( ! $existing ) {
			return new WP_Error(
				Constants::ERROR_RECORD_NOT_FOUND,
				Constants::MSG_RECORD_NOT_FOUND,
				array( 'status' => Constants::HTTP_NOT_FOUND )
			);
		}

		$result = $wpdb->delete( //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom query.
			$table_name,
			array( 'uuid' => $uuid ),
			array( '%s' )
		);

		if ( false === $result ) {
			return new WP_Error(
				Constants::ERROR_DB_ERROR,
				sprintf( Constants::MSG_DB_ERROR, $wpdb->last_error ),
				array( 'status' => Constants::HTTP_INTERNAL_SERVER_ERROR )
			);
		}

		return true;
	}
}
