<?php

/**
 * Export Supplier Service
 *
 * @category   WordPress_Plugin
 * @package    Common
 * @subpackage Services\Export_Supplier
 * @author     miniOrange <info@miniorange.com>
 * @license    MIT/Expat
 * @link       https://plugins.miniorange.com/woocommerce-api-product-sync-with-woocommerce-rest-apis
 */
namespace MOWCPS\Common\Services\Export_Supplier;

use MOWCPS\Common\Hooks\Sns_Activewear;
use MOWCPS\Common\Models\Authorization_Model;
use MOWCPS\Common\Models\Connection_Model;
use MOWCPS\Common\Models\Attribute_Mapping_Model;
use MOWCPS\Common\Models\Setting_Model;
use MOWCPS\Common\Utils\Model_Utils;
use MOWCPS\Common\Utils\Configuration_Loader;
use MOWCPS\Common\Constants\Constants;
use MOWCPS\Common\Models\Supplier_Model;
use MOWCPS\Premium\Models\Order_Sync_Model;
use MOWCPS\Common\Utils\Response_Builder;
use MOWCPS\Common\Handlers\File_Save_Handler\File_Save_Handler;
use MOWCPS\Common\Factories\Supplier_App_Factory;
use MOWCPS\Common\Constants\Plugin_Filters;

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

/**
 * Class Export_Supplier
 *
 * Handles export supplier data
 */
class Export_Supplier {

	/**
	 * File directory path
	 *
	 * @var string
	 */
	private $file_dir;

	/**
	 * Plugin directory path
	 *
	 * @var string
	 */
	private $plugin_dir;

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

	/**
	 * Constructor
	 */
	public function __construct() {
		$this->plugin_dir       = wp_normalize_path( MOWCPS_PLUGIN_DIR );
		$this->file_dir         = wp_normalize_path( $this->plugin_dir . 'resources/supplier-data/export-config' );
		$this->export_file_name = 'export-supplier-config-' . wp_date( 'H-i-Y-m-d' ) . '.json';
	}

	/**
	 * Export supplier
	 *
	 * @param array $data Data from request.
	 * @return WP_REST_Response Response object with export data or error.
	 */
	public function export_supplier( $data ) {
		$supplier_config = $this->get_suppliers_data( $data );

		if ( is_wp_error( $supplier_config ) ) {
			return Response_Builder::from_wp_error( $supplier_config );
		} else {
			$exported_data = $supplier_config['exported_data'];
		}

		$file_save_handler = new File_Save_Handler();
		$response_data     = $file_save_handler->export_data_to_file( $exported_data, $this->file_dir, $this->export_file_name );

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

		if ( true === $response_data ) {
			$response_data = $file_save_handler->download_file_from_browser( $this->file_dir, $this->export_file_name );

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

			return $response_data;
		}

		return Response_Builder::error(
			'unexpected_error',
			'Unexpected error during export',
			array(
				'status_code' => 500,
			)
		);
	}

	/**
	 * Build complete supplier JSON structure
	 *
	 * @param array $supplier Supplier data from database.
	 * @return array|WP_Error Supplier data in export format or error.
	 */
	public function build_supplier_json( $supplier ): array {
		try {
			// Base supplier data.
			$supplier_data = array(
				'name'                                => $supplier['name'],
				'app_type'                            => $supplier['app_type'],
				'sync_type'                           => strtolower( $supplier['sync_type'] ),
				'operation_type'                      => Constants::OPERATION_TYPE_IMPORT_EXPORT,
				'is_active'                           => (bool) $supplier['is_active'],
				'debug_mode'                          => (bool) $supplier['debug_mode'],
				'unique_product_identifier'           => $supplier['unique_product_identifier'] ?? Constants::DEFAULT_UNIQUE_PRODUCT_IDENTIFIER,
			);

			// Get authorization data.
			$authorization = $this->get_authorization_data( $supplier['id'], $supplier_data['sync_type'] );
			if ( ! is_wp_error( $authorization ) && null !== $authorization ) {
				$supplier_data['authorization'] = $authorization;
			}

			// Get connections data.
			$connections = $this->get_connections_data( $supplier['uuid'] );
			if ( ! is_wp_error( $connections ) ) {
				$supplier_data['connections'] = $connections;
			}

			/**
			 * Hook for altering supplier data before creating the supplier.
			 *
			 * @param array $data Supplier data.
			 * @return array Altered supplier data.
			 */
			if ( has_filter( Plugin_Filters::FILTER_ALTER_SUPPLIER_DATA ) ) {
				$returned_data = apply_filters( Plugin_Filters::FILTER_ALTER_SUPPLIER_DATA,
					array(
						'data' => $supplier,
					)
				);

				$supplier['app_type'] = $returned_data['app_type'];
			}

			// For default suppliers, extract details from suppliers table to display on edit supplier.
			if ( isset( $supplier['app_type'] ) && Constants::SUPPLIER_APP_TYPE_CUSTOM !== $supplier['app_type'] ) {
				$supplier_data['details'] = $supplier['details'];
			}

			// Get attribute mapping data.
			$attribute_mapping = $this->get_attribute_mapping_data( $supplier['id'] );
			if ( ! is_wp_error( $attribute_mapping ) ) {
				$supplier_data['attribute_mapping'] = $attribute_mapping;
			}

			// Get operations data.
			$operations = $this->get_operations_data( $supplier['id'] );
			if ( ! is_wp_error( $operations ) ) {
				$supplier_data['operations'] = $operations;
			}

			// Get order configs if any order connections exist.
			$order_configs = $this->get_order_configs( $supplier['id'], $connections );
			if ( ! empty( $order_configs ) ) {
				$supplier_data[ Constants::ORDER_SYNC_CONFIG ] = $order_configs;
			}

			return $supplier_data;

		} catch ( \Exception $e ) {
			return new WP_Error( 'build_error', 'Failed to build supplier data: ' . $e->getMessage() );
		}
	}

	/**
	 * Get authorization data for supplier
	 *
	 * @param int $supplier_id Supplier ID.
	 * @return array Authorization data.
	 */
	private function get_authorization_data( $supplier_id, $app_type ) {
		$auth_data = Authorization_Model::get_by_supplier_id( $supplier_id );

		if ( ! $auth_data ) {
			// Specific condition related to FTP supplier as we do not save authorization for FTP.
			if ( Constants::FTP_CONNECTION === $app_type ) {
				return null;
			}
			return array(
				'auth_type'     => 'no-auth',
				'auth_settings' => array(),
			);
		}

		$auth_settings = array();
		if ( ! empty( $auth_data['auth_settings'] ) ) {
			if ( is_string( $auth_data['auth_settings'] ) ) {
				$auth_settings = json_decode( $auth_data['auth_settings'], true ) ?: array();
			} elseif ( is_array( $auth_data['auth_settings'] ) ) {
				$auth_settings = $auth_data['auth_settings'];
			}
		}

		return array(
			'auth_type'     => $auth_data['auth_type'],
			'auth_settings' => $auth_settings,
		);
	}

	/**
	 * Get connections data for supplier
	 *
	 * @param string $supplier_uuid Supplier UUID.
	 * @return array|WP_Error Connections data.
	 */
	private function get_connections_data( $supplier_uuid ) {
		$connections = Connection_Model::get_by_supplier_uuid( $supplier_uuid );

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

		$formatted_connections = array();

		foreach ( $connections as $connection ) {
			$configuration = array();
			if ( ! empty( $connection['configuration'] ) ) {
				if ( is_string( $connection['configuration'] ) ) {
					$configuration = json_decode( $connection['configuration'], true ) ?: array();
				} elseif ( is_array( $connection['configuration'] ) ) {
					$configuration = $connection['configuration'];
				}
			}

			$formatted_connections[] = array(
				'name'          => $connection['name'],
				'type'          => strtolower( $connection['type'] ),
				'is_active'     => (bool) $connection['is_active'],
				'is_primary'    => (bool) $connection['is_primary'],
				'resource_type' => $connection['resource_type'],
				'priority'      => (int) $connection['priority'],
				'configuration' => $configuration,
			);
		}

		usort(
			$formatted_connections,
			function ( $a, $b ) {
				return $a['priority'] - $b['priority'];
			}
		);

		return $formatted_connections;
	}

	/**
	 * Get attribute mapping data for supplier
	 *
	 * @param int $supplier_id Supplier ID.
	 * @return array|WP_Error Attribute mapping data.
	 */
	private function get_attribute_mapping_data( $supplier_id ) {
		$mappings = Attribute_Mapping_Model::get_all( array( 'supplier_id' => $supplier_id ) );

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

		$formatted_mappings = array();

		foreach ( $mappings as $mapping ) {
			$advanced_configuration = null;
			if ( ! empty( $mapping['advanced_configuration'] ) ) {
				if ( is_string( $mapping['advanced_configuration'] ) ) {
					$advanced_configuration = json_decode( $mapping['advanced_configuration'], true );
				} elseif ( is_array( $mapping['advanced_configuration'] ) ) {
					$advanced_configuration = $mapping['advanced_configuration'];
				}

				if ( isset( $advanced_configuration['fetch_from_other_api']['connection'] ) ) {
					$connection_id   = $advanced_configuration['fetch_from_other_api']['connection'];
					$connection_name = '';
					if ( ! empty( $connection_id ) && $connection_id !== '' ) {
						$connection_name = Connection_Model::get_connection_name_by_id( (int) $connection_id ) ?: '';
					}
					$advanced_configuration['fetch_from_other_api']['connection'] = $connection_name;
				}
			}

			$connection_name = Connection_Model::get_connection_name_by_id( $mapping['connection_id'] );

			$mapping_data = array(
				'connection'             => $connection_name,
				'product_type'           => $mapping['product_type'],
				'type'                   => $mapping['type'],
				'wc_attribute'           => $mapping['wc_attribute'],
				'supplier_attribute'     => $mapping['supplier_attribute'],
				'advanced_configuration' => $advanced_configuration,
			);

			$formatted_mappings[] = $mapping_data;
		}

		return $formatted_mappings;
	}

	/**
	 * Get operations data for supplier
	 *
	 * @param int $supplier_id Supplier ID.
	 * @return array Operations data.
	 */
	private function get_operations_data( $supplier_id ) {
		$operations = array();

		$batch_connections = $this->get_batch_connections( $supplier_id );

		$operation_types = array( 'product-operations', 'schedule', 'batch', 'file-sync', 'outbound', 'page', 'filter' );

		foreach ( $operation_types as $type ) {
			$settings = Setting_Model::get_all(
				array(
					'supplier_id' => $supplier_id,
					'type'        => $type,
				)
			);

			if ( ! is_wp_error( $settings ) && ! empty( $settings ) ) {
				foreach ( $settings as $setting ) {
					$operation_data = array(
						'operation'      => $setting['operation'],
						'operation_type' => $setting['operation_type'],
						'is_active'      => (bool) $setting['is_active'],
					);

					if ( $type === 'schedule' && isset( $setting['schedule_name'] ) ) {
						$operation_data['schedule_data'] = array(
							'name'       => $setting['schedule_name'],
							'start_time' => $setting['start_time'],
							'interval'   => (int) $setting['interval'],
							'frequency'  => $setting['frequency'],
							'timezone'   => $setting['timezone'],
						);

						$schedule_connections = $this->get_connections_for_operation( $setting['id'] );
						if ( ! empty( $schedule_connections ) ) {
							$operation_data['connections'] = $schedule_connections;
						}
					} elseif ( $type === 'batch' && isset( $setting['configuration'] ) ) {
						if ( is_string( $setting['configuration'] ) ) {
							$operation_data['configuration'] = json_decode( $setting['configuration'], true ) ?: array();
						} elseif ( is_array( $setting['configuration'] ) ) {
							$operation_data['configuration'] = $setting['configuration'];
						}
					} elseif ( $type === 'product-operations' && isset( $setting['configuration'] ) ) {
						if ( is_string( $setting['configuration'] ) ) {
							$operation_data['configuration'] = json_decode( $setting['configuration'], true ) ?: array();
						} elseif ( is_array( $setting['configuration'] ) ) {
							$operation_data['configuration'] = $setting['configuration'];
						}
					} elseif ( $type === 'file-sync' && isset( $setting['configuration'] ) ) {
						if ( is_string( $setting['configuration'] ) ) {
							$operation_data['configuration'] = json_decode( $setting['configuration'], true ) ?: array();
						} elseif ( is_array( $setting['configuration'] ) ) {
							$operation_data['configuration'] = $setting['configuration'];
						}

						$file_sync_connections = $this->get_connections_for_operation( $setting['id'] );
						if ( ! empty( $file_sync_connections ) ) {
							$operation_data['connections'] = $file_sync_connections;

							$has_batch_connection = false;
							foreach ( $file_sync_connections as $connection_name ) {
								if ( in_array( $connection_name, $batch_connections ) ) {
									$has_batch_connection = true;
									break;
								}
							}

							if ( $has_batch_connection ) {
								$operation_data['configuration']['file_in_batch'] = true;
							}
						}
					} elseif ( $type === 'outbound' && isset( $setting['configuration'] ) ) {
						if ( is_string( $setting['configuration'] ) ) {
							$operation_data['configuration'] = json_decode( $setting['configuration'], true ) ?: array();
						} elseif ( is_array( $setting['configuration'] ) ) {
							$operation_data['configuration'] = $setting['configuration'];
						}

						if ( isset( $operation_data['configuration']['allow_connections_for_outbound'] ) ) {
							$operation_data['configuration']['allow_connections_for_outbound'] = $this->convert_supplier_uuids_to_ids( $operation_data['configuration']['allow_connections_for_outbound'] );
						}

						$outbound_connections = $this->get_connections_for_operation( $setting['id'] );
						if ( ! empty( $outbound_connections ) ) {
							$operation_data['connections'] = $outbound_connections;
						}
					} elseif ( $type === 'page' && isset( $setting['configuration'] ) ) {
						if ( is_string( $setting['configuration'] ) ) {
							$operation_data['configuration'] = json_decode( $setting['configuration'], true ) ?: array();
						} elseif ( is_array( $setting['configuration'] ) ) {
							$operation_data['configuration'] = $setting['configuration'];
						}

						$page_connections = $this->get_connections_for_operation( $setting['id'] );
						if ( ! empty( $page_connections ) ) {
							$operation_data['connections'] = $page_connections;
						}
					} elseif ( $type === 'filter' ) {
						$filters_data = array();
						if ( isset( $setting['filters'] ) ) {
							if ( is_string( $setting['filters'] ) ) {
								$filters_data = json_decode( $setting['filters'], true ) ?: array();
							} elseif ( is_array( $setting['filters'] ) ) {
								$filters_data = $setting['filters'];
							}
						}

						$operation_data['filter_data'] = array();
						if ( isset( $setting['filter_name'] ) ) {
							$operation_data['filter_data']['name'] = $setting['filter_name'];
						}
						if ( ! empty( $filters_data ) ) {
							$operation_data['filter_data']['filters'] = $filters_data;
						}

						$filter_connections = $this->get_connections_for_operation( $setting['id'] );
						if ( ! empty( $filter_connections ) ) {
							$operation_data['connections'] = $filter_connections;
						}
					}

					if ( isset( $setting['connections'] ) && ! empty( $setting['connections'] ) && $type !== 'file-sync' && $type !== 'schedule' && $type !== 'outbound' && $type !== 'page' && $type !== 'filter' ) {
						$connection_names = array();
						foreach ( $setting['connections'] as $connection ) {
							if ( isset( $connection['name'] ) ) {
								$connection_names[] = $connection['name'];
							}
						}
						$operation_data['connections'] = $connection_names;
					}

					$operations[] = $operation_data;
				}
			}
		}

		return $operations;
	}

	/**
	 * Get connections for a specific operation
	 *
	 * @param int $operation_id Operation ID.
	 * @return array Connection names.
	 */
	private function get_connections_for_operation( $operation_id ) {
		$connections_table           = Model_Utils::get_table_name( Model_Utils::TABLE_CONNECTIONS );
		$operation_connections_table = Model_Utils::get_table_name( Model_Utils::TABLE_SYNC_OPERATION_CONNECTIONS );

		$connections = Connection_Model::get_connection_name_from_operation_id( $operation_connections_table, $connections_table, $operation_id );

		if ( ! $connections ) {
			return array();
		}

		$connection_names = array();
		foreach ( $connections as $connection ) {
			if ( isset( $connection['name'] ) ) {
				$connection_names[] = $connection['name'];
			}
		}

		return $connection_names;
	}

	/**
	 * Get connections that have batch operations enabled.
	 *
	 * @param int $supplier_id Supplier ID.
	 * @return array Batch connection names.
	 */
	private function get_batch_connections( $supplier_id ) {
		$batch_connections = array();

		$batch_settings = Setting_Model::get_all(
			array(
				'supplier_id' => $supplier_id,
				'type'        => 'batch',
			)
		);

		if ( ! is_wp_error( $batch_settings ) && ! empty( $batch_settings ) ) {
			foreach ( $batch_settings as $batch_setting ) {
				$connections       = $this->get_connections_for_operation( $batch_setting['id'] );
				$batch_connections = array_merge( $batch_connections, $connections );
			}
		}

		return array_unique( $batch_connections );
	}

	/**
	 * Convert supplier UUIDs to IDs in an array
	 *
	 * @param array $supplier_uuids Array of supplier UUIDs.
	 * @return array Array of supplier IDs.
	 */
	private function convert_supplier_uuids_to_ids( $supplier_uuids ) {
		if ( empty( $supplier_uuids ) || ! is_array( $supplier_uuids ) ) {
			return $supplier_uuids;
		}

		$converted_suppliers = array();

		foreach ( $supplier_uuids as $supplier_uuid ) {
			if ( $supplier_uuid === 'allow_all_website_products' ) {
				continue;
			} else {
				$supplier_id = $this->get_supplier_id_by_uuid( $supplier_uuid );
				if ( $supplier_id ) {
					$converted_suppliers[] = $supplier_id;
				} else {
					$converted_suppliers[] = $supplier_uuid;
				}
			}
		}

		return $converted_suppliers;
	}

	/**
	 * Get supplier ID by UUID
	 *
	 * @param string $supplier_uuid Supplier UUID.
	 * @return int|null Supplier ID or null if not found.
	 */
	private function get_supplier_id_by_uuid( $supplier_uuid ) {
		global $wpdb;

		$supplier_id = Supplier_Model::get_by_uuid( $supplier_uuid );

		return $supplier_id ? (int) $supplier_id : null;
	}

	/**
	 * Get order configs for supplier if order connections exist
	 *
	 * @param int   $supplier_id Supplier ID.
	 * @param array $connections Connections data.
	 * @return array Array of order configs.
	 */
	private function get_order_configs( $supplier_id, $connections ) {
		global $wpdb;

		$order_configs = array();

		// Check if any connection has resource_type = 'order'.
		$has_order_connection = false;
		foreach ( $connections as $connection ) {
			if ( isset( $connection['resource_type'] ) && strtolower( $connection['resource_type'] ) === 'order' ) {
				$has_order_connection = true;
				break;
			}
		}

		if ( ! $has_order_connection ) {
			return $order_configs;
		}

		// Get all order connections for this supplier with their IDs, UUIDs, and names.
		$connections_table = Model_Utils::get_table_name( Model_Utils::TABLE_CONNECTIONS );
		$order_connections = Order_Sync_Model::get_connection_by_ids( $connections_table, $supplier_id, Constants::CONNECTION_RESOURCE_TYPE_ORDER );

		if ( empty( $order_connections ) ) {
			return $order_configs;
		}

		// Get order syncs for these connections.
		$connection_ids = array_map(
			function ( $conn ) {
				return (int) $conn['id'];
			},
			$order_connections
		);

		$order_syncs = Order_Sync_Model::get_order_config( $connection_ids );

		if ( empty( $order_syncs ) ) {
			return $order_configs;
		}

		// Create a map of connection_id to connection_name.
		$connection_map = array();
		foreach ( $order_connections as $conn ) {
			$connection_map[ (int) $conn['id'] ] = $conn['name'];
		}

		// Format order configs.
		foreach ( $order_syncs as $order_sync ) {
			$connection_id   = (int) $order_sync['connection_id'];
			$connection_name = $connection_map[ $connection_id ] ?? '';

			if ( empty( $connection_name ) ) {
				continue;
			}

			// Decode configuration JSON.
			$configuration = json_decode( $order_sync['configuration'], true );
			if ( ! is_array( $configuration ) ) {
				$configuration = array();
			}

			// Decode conditional_sync_config JSON with proper null/empty handling.
			$conditional_sync_config = null;
			if ( ! empty( $order_sync['conditional_sync_config'] ) ) {
				$decoded_config = json_decode( $order_sync['conditional_sync_config'], true );
				if ( is_array( $decoded_config ) ) {
					$conditional_sync_config = $decoded_config;
				}
			}

			$order_config = array(
				'name'                        => $order_sync['name'],
				'connection_id'               => $connection_name,
				'is_active'                   => (bool) $order_sync['is_active'],
				'configuration'               => $configuration,
				'is_conditional_sync_enabled' => (bool) $order_sync['is_conditional_sync_enabled'],
			);

			// Only include conditional_sync_config if it's not null (when conditional sync is enabled and config exists).
			if ( null !== $conditional_sync_config ) {
				$order_config['conditional_sync_config'] = $conditional_sync_config;
			}

			$order_configs[] = $order_config;
		}

		return $order_configs;
	}

	/**
	 * Get suppliers data
	 *
	 * @param array $data Data from request.
	 * @return array Array of suppliers data.
	 */
	public function get_suppliers_data( $data ) {
		$supplier_ids = isset( $data['supplier_ids'] ) ? $data['supplier_ids'] : array();

		if ( empty( $supplier_ids ) || ! is_array( $supplier_ids ) ) {
			return Response_Builder::error(
				'invalid_supplier_ids',
				'supplier_ids must be a non-empty array of supplier UUIDs.',
				400
			);
		}

		$results       = array();
		$errors        = array();
		$exported_data = array();

		foreach ( $supplier_ids as $supplier_uuid ) {
			$supplier           = Supplier_Model::get_by_uuid( $supplier_uuid );
			$supplier_app_class = Supplier_App_Factory::create_supplier_app( $supplier['app_type'] );

			if ( ! $supplier ) {
				$errors[] = array(
					'supplier_id' => $supplier_uuid,
					'success'     => false,
					'message'     => 'Supplier not found.',
				);
				continue;
			}

			$supplier_data = $this->build_supplier_json( $supplier );

			if ( ! is_wp_error( $supplier_data ) ) {
				$exported_data[] = $supplier_data;
				$results[]       = array(
					'supplier_id' => $supplier_uuid,
					'success'     => true,
					'message'     => 'Export successful',
				);
			} else {
				$errors[] = array(
					'supplier_id' => $supplier_uuid,
					'success'     => false,
					'message'     => sprintf( 'Failed to export supplier: %s', $supplier_data->get_error_message() ),
				);
			}
		}

		if ( empty( $results ) && ! empty( $errors ) ) {
			return Response_Builder::error(
				'export_failed',
				'All suppliers failed to export.',
				400
			);
		}

		return array(
			'exported_data' => $exported_data,
			'results'       => $results,
			'errors'        => $errors,
		);
	}
}
