<?php
/**
 * GGM Commerce Attribute CSV Importer
 *
 * Handles CSV import functionality for attribute groups and attributes
 *
 * @package GGM_Commerce
 * @since 1.0.0
 */

// Prevent direct access.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Class GGM_Commerce_Attribute_CSV_Importer
 *
 * Imports attribute groups and attributes from CSV format
 */
class GGM_Commerce_Attribute_CSV_Importer {

	/**
	 * Import session data transient key
	 *
	 * @var string
	 */
	private $session_key;

	/**
	 * Import type: 'groups' or 'attributes'
	 *
	 * @var string
	 */
	private $import_type;

	/**
	 * Current import progress
	 *
	 * @var array
	 */
	private $progress = array(
		'total'     => 0,
		'processed' => 0,
		'created'   => 0,
		'updated'   => 0,
		'skipped'   => 0,
		'errors'    => array(),
		'warnings'  => array(),
	);

	/**
	 * Lookup maps for relationship resolution
	 *
	 * @var array
	 */
	private $lookup_maps = array(
		'groups'     => array(), // name => id.
		'attributes' => array(), // name => id.
	);

	/**
	 * Constructor
	 *
	 * @param string $import_type Import type ('groups' or 'attributes').
	 */
	public function __construct( $import_type = 'attributes' ) {
		$this->import_type = $import_type;
		$this->session_key = 'ggmc_attr_' . $import_type . '_import_' . get_current_user_id();
	}

	/**
	 * Parse CSV file and return preview data
	 *
	 * @param string $file_path Path to CSV file.
	 * @return array|WP_Error Parsed data or error.
	 */
	public function parse_csv( $file_path ) {
		if ( ! file_exists( $file_path ) ) {
			return new WP_Error( 'file_not_found', __( 'CSV file not found', 'ggm-commerce' ) );
		}

		$handle = fopen( $file_path, 'r' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen
		if ( ! $handle ) {
			return new WP_Error( 'file_read_error', __( 'Could not read CSV file', 'ggm-commerce' ) );
		}

		// Skip BOM if present.
		$bom = fread( $handle, 3 ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fread
		if ( "\xEF\xBB\xBF" !== $bom ) {
			rewind( $handle );
		}

		// Read headers.
		$headers = fgetcsv( $handle );
		if ( empty( $headers ) ) {
			fclose( $handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose
			return new WP_Error( 'empty_file', __( 'CSV file is empty or has no headers', 'ggm-commerce' ) );
		}

		// Clean headers.
		$headers = array_map( 'trim', $headers );
		$headers = array_map(
			function ( $h ) {
				return preg_replace( '/^\xEF\xBB\xBF/', '', $h );
			},
			$headers
		);

		// Count rows and get sample.
		$row_count   = 0;
		$sample_rows = array();

		while ( ( $row = fgetcsv( $handle ) ) !== false ) { // phpcs:ignore WordPress.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition
			$row_count++;
			if ( count( $sample_rows ) < 5 ) {
				$row_data = array();
				foreach ( $headers as $i => $header ) {
					$row_data[ $header ] = isset( $row[ $i ] ) ? $row[ $i ] : '';
				}
				$sample_rows[] = $row_data;
			}
		}

		fclose( $handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose

		return array(
			'headers'     => $headers,
			'row_count'   => $row_count,
			'sample_rows' => $sample_rows,
			'file_path'   => $file_path,
		);
	}

	/**
	 * Initialize import session
	 *
	 * @param string $file_path Path to CSV file.
	 * @param array  $options Import options.
	 * @return array|WP_Error Initial state or error.
	 */
	public function init_import( $file_path, $options = array() ) {
		$defaults = array(
			'duplicate_handling' => 'update', // skip, update, create.
			'create_groups'      => true, // Auto-create missing groups.
			'batch_size'         => 50,
		);

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

		// Parse CSV to get headers and count.
		$parse_result = $this->parse_csv( $file_path );
		if ( is_wp_error( $parse_result ) ) {
			return $parse_result;
		}

		// Pre-load lookup maps for relationship resolution.
		$this->preload_lookup_maps();

		// Initialize progress.
		$this->progress = array(
			'total'        => $parse_result['row_count'],
			'processed'    => 0,
			'created'      => 0,
			'updated'      => 0,
			'skipped'      => 0,
			'errors'       => array(),
			'warnings'     => array(),
			'current_item' => '',
			'status'       => 'running',
			'file_path'    => $file_path,
			'headers'      => $parse_result['headers'],
			'options'      => $options,
			'current_row'  => 0,
			'lookup_maps'  => $this->lookup_maps,
		);

		$this->save_progress();

		return array(
			'total'      => $parse_result['row_count'],
			'batch_size' => $options['batch_size'],
			'status'     => 'initialized',
		);
	}

	/**
	 * Process the next batch of imports
	 *
	 * @return array|WP_Error Batch result with progress info.
	 */
	public function process_batch() {
		// Load current progress.
		$this->progress = $this->get_progress();

		if ( empty( $this->progress ) || 'running' !== $this->progress['status'] ) {
			return new WP_Error( 'no_session', __( 'No active import session', 'ggm-commerce' ) );
		}

		$file_path  = $this->progress['file_path'];
		$headers    = $this->progress['headers'];
		$options    = $this->progress['options'];
		$start_row  = $this->progress['current_row'];
		$batch_size = isset( $options['batch_size'] ) ? $options['batch_size'] : 50;

		// Restore lookup maps from session.
		$this->lookup_maps = $this->progress['lookup_maps'];

		// Open file.
		$handle = fopen( $file_path, 'r' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen
		if ( ! $handle ) {
			$this->progress['status'] = 'error';
			$this->save_progress();
			return new WP_Error( 'file_read_error', __( 'Could not read CSV file', 'ggm-commerce' ) );
		}

		// Skip BOM.
		$bom = fread( $handle, 3 ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fread
		if ( "\xEF\xBB\xBF" !== $bom ) {
			rewind( $handle );
		}

		// Skip header row.
		fgetcsv( $handle );

		// Skip to current position.
		for ( $i = 0; $i < $start_row; $i++ ) {
			fgetcsv( $handle );
		}

		// Process batch.
		$processed_in_batch = 0;
		while ( $processed_in_batch < $batch_size && ( $row = fgetcsv( $handle ) ) !== false ) { // phpcs:ignore WordPress.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition
			$row_data = array();
			foreach ( $headers as $idx => $header ) {
				$row_data[ $header ] = isset( $row[ $idx ] ) ? $row[ $idx ] : '';
			}

			$this->progress['current_item'] = $row_data['name'] ?? '';

			if ( 'groups' === $this->import_type ) {
				$result = $this->import_single_group( $row_data, $options );
			} else {
				$result = $this->import_single_attribute( $row_data, $options );
			}

			$this->handle_import_result( $result, $row_data['name'] ?? 'Unknown' );

			$this->progress['processed']++;
			$this->progress['current_row']++;
			$processed_in_batch++;
		}

		fclose( $handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose

		// Check if complete.
		if ( $this->progress['processed'] >= $this->progress['total'] ) {
			$this->progress['status']       = 'complete';
			$this->progress['current_item'] = '';
		}

		// Update lookup maps in progress for next batch.
		$this->progress['lookup_maps'] = $this->lookup_maps;
		$this->save_progress();

		return array(
			'total'           => $this->progress['total'],
			'processed'       => $this->progress['processed'],
			'created'         => $this->progress['created'],
			'updated'         => $this->progress['updated'],
			'skipped'         => $this->progress['skipped'],
			'errors'          => $this->progress['errors'],
			'warnings'        => $this->progress['warnings'],
			'current_item'    => $this->progress['current_item'],
			'status'          => $this->progress['status'],
			'batch_processed' => $processed_in_batch,
		);
	}

	/**
	 * Handle import result
	 *
	 * @param string|WP_Error $result Import result.
	 * @param string          $item_name Item name for error reporting.
	 */
	private function handle_import_result( $result, $item_name ) {
		if ( is_wp_error( $result ) ) {
			$this->progress['errors'][] = array(
				'row'     => $this->progress['processed'] + 1,
				'message' => $result->get_error_message(),
				'data'    => $item_name,
			);
		} elseif ( 'created' === $result ) {
			$this->progress['created']++;
		} elseif ( 'updated' === $result ) {
			$this->progress['updated']++;
		} else {
			$this->progress['skipped']++;
		}
	}

	/**
	 * Pre-load lookup maps for efficient relationship resolution
	 */
	private function preload_lookup_maps() {
		global $wpdb;

		// Load existing attribute groups.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$groups = $wpdb->get_results(
			"SELECT id, name FROM {$wpdb->prefix}ggmc_attribute_groups",
			ARRAY_A
		);
		foreach ( $groups as $group ) {
			$this->lookup_maps['groups'][ $group['name'] ] = $group['id'];
		}

		// Load existing attributes.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$attributes = $wpdb->get_results(
			"SELECT id, name FROM {$wpdb->prefix}ggmc_attributes",
			ARRAY_A
		);
		foreach ( $attributes as $attr ) {
			$this->lookup_maps['attributes'][ $attr['name'] ] = $attr['id'];
		}
	}

	/**
	 * Import single attribute group
	 *
	 * @param array $row_data Row data from CSV.
	 * @param array $options Import options.
	 * @return string|WP_Error Result: 'created', 'updated', 'skipped', or WP_Error.
	 */
	private function import_single_group( $row_data, $options ) {
		global $wpdb;

		// Validate required fields.
		if ( empty( $row_data['name'] ) ) {
			return new WP_Error( 'missing_name', __( 'Attribute group name is required', 'ggm-commerce' ) );
		}

		$name = sanitize_text_field( $row_data['name'] );

		// Check for existing by name.
		$existing_id = $this->lookup_maps['groups'][ $name ] ?? null;

		if ( $existing_id ) {
			// Handle duplicate.
			switch ( $options['duplicate_handling'] ) {
				case 'skip':
					return 'skipped';

				case 'update':
					return $this->update_group( $existing_id, $row_data );

				case 'create':
					// Create anyway - will have duplicate name.
					break;
			}
		}

		// Create new group.
		$insert_data = array(
			'name'          => $name,
			'description'   => sanitize_textarea_field( $row_data['description'] ?? '' ),
			'display_order' => intval( $row_data['display_order'] ?? 0 ),
			'status'        => sanitize_text_field( $row_data['status'] ?? 'active' ),
			'created_at'    => current_time( 'mysql' ),
			'updated_at'    => current_time( 'mysql' ),
		);

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
		$result = $wpdb->insert( "{$wpdb->prefix}ggmc_attribute_groups", $insert_data );

		if ( $result ) {
			$new_id = $wpdb->insert_id;
			// Update lookup map for subsequent items.
			$this->lookup_maps['groups'][ $name ] = $new_id;
			return 'created';
		}

		return new WP_Error( 'insert_failed', __( 'Failed to create attribute group', 'ggm-commerce' ) );
	}

	/**
	 * Update existing attribute group
	 *
	 * @param int   $group_id Existing group ID.
	 * @param array $row_data Row data from CSV.
	 * @return string|WP_Error Result.
	 */
	private function update_group( $group_id, $row_data ) {
		global $wpdb;

		$update_data = array(
			'name'          => sanitize_text_field( $row_data['name'] ),
			'description'   => sanitize_textarea_field( $row_data['description'] ?? '' ),
			'display_order' => intval( $row_data['display_order'] ?? 0 ),
			'status'        => sanitize_text_field( $row_data['status'] ?? 'active' ),
			'updated_at'    => current_time( 'mysql' ),
		);

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$result = $wpdb->update(
			"{$wpdb->prefix}ggmc_attribute_groups",
			$update_data,
			array( 'id' => $group_id )
		);

		if ( false !== $result ) {
			return 'updated';
		}

		return new WP_Error( 'update_failed', __( 'Failed to update attribute group', 'ggm-commerce' ) );
	}

	/**
	 * Import single attribute
	 *
	 * @param array $row_data Row data from CSV.
	 * @param array $options Import options.
	 * @return string|WP_Error Result: 'created', 'updated', 'skipped', or WP_Error.
	 */
	private function import_single_attribute( $row_data, $options ) {
		global $wpdb;

		// Validate required fields.
		if ( empty( $row_data['name'] ) ) {
			return new WP_Error( 'missing_name', __( 'Attribute name is required', 'ggm-commerce' ) );
		}

		$name = sanitize_text_field( $row_data['name'] );

		// Resolve group ID.
		$group_id = null;
		if ( ! empty( $row_data['group_name'] ) ) {
			$group_name = $row_data['group_name'];
			$group_id   = $this->lookup_maps['groups'][ $group_name ] ?? null;

			if ( ! $group_id && $options['create_groups'] ) {
				// Create missing group.
				$group_id = $this->create_attribute_group( $group_name );
			}

			if ( ! $group_id ) {
				$this->progress['warnings'][] = array(
					'message' => sprintf(
						/* translators: 1: group name, 2: attribute name */
						__( 'Attribute group "%1$s" not found for attribute "%2$s"', 'ggm-commerce' ),
						$group_name,
						$name
					),
				);
			}
		}

		// Parse JSON fields with validation.
		$options_array = array();
		if ( ! empty( $row_data['options'] ) ) {
			$decoded = json_decode( $row_data['options'], true );
			if ( JSON_ERROR_NONE === json_last_error() && is_array( $decoded ) ) {
				$options_array = $decoded;
			} else {
				$this->progress['warnings'][] = array(
					'message' => sprintf(
						/* translators: %s: attribute name */
						__( 'Invalid JSON in options for attribute "%s"', 'ggm-commerce' ),
						$name
					),
				);
			}
		}

		$icon_json = '';
		if ( ! empty( $row_data['icon'] ) ) {
			$decoded = json_decode( $row_data['icon'], true );
			if ( JSON_ERROR_NONE === json_last_error() ) {
				$icon_json = $row_data['icon']; // Keep as JSON string.
			} else {
				$this->progress['warnings'][] = array(
					'message' => sprintf(
						/* translators: %s: attribute name */
						__( 'Invalid JSON in icon for attribute "%s"', 'ggm-commerce' ),
						$name
					),
				);
			}
		}

		// Check for existing by name.
		$existing_id = $this->lookup_maps['attributes'][ $name ] ?? null;

		if ( $existing_id ) {
			// Handle duplicate.
			switch ( $options['duplicate_handling'] ) {
				case 'skip':
					return 'skipped';

				case 'update':
					return $this->update_attribute( $existing_id, $row_data, $group_id, $options_array, $icon_json );

				case 'create':
					// Create anyway - will have duplicate name.
					break;
			}
		}

		// Validate attribute_type.
		$valid_types    = array( 'text', 'number', 'select' );
		$attribute_type = sanitize_text_field( $row_data['attribute_type'] ?? 'text' );
		if ( ! in_array( $attribute_type, $valid_types, true ) ) {
			$attribute_type = 'text';
		}

		// Create new attribute.
		$insert_data = array(
			'name'           => $name,
			'group_id'       => $group_id,
			'attribute_type' => $attribute_type,
			'options'        => wp_json_encode( $options_array ),
			'unit'           => sanitize_text_field( $row_data['unit'] ?? '' ),
			'icon'           => $icon_json,
			'filterable'     => intval( $row_data['filterable'] ?? 0 ),
			'display_order'  => intval( $row_data['display_order'] ?? 0 ),
			'status'         => sanitize_text_field( $row_data['status'] ?? 'active' ),
			'created_at'     => current_time( 'mysql' ),
			'updated_at'     => current_time( 'mysql' ),
		);

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
		$result = $wpdb->insert( "{$wpdb->prefix}ggmc_attributes", $insert_data );

		if ( $result ) {
			$new_id = $wpdb->insert_id;
			// Update lookup map.
			$this->lookup_maps['attributes'][ $name ] = $new_id;
			return 'created';
		}

		return new WP_Error( 'insert_failed', __( 'Failed to create attribute', 'ggm-commerce' ) );
	}

	/**
	 * Update existing attribute
	 *
	 * @param int    $attribute_id Existing attribute ID.
	 * @param array  $row_data Row data from CSV.
	 * @param int    $group_id Resolved group ID.
	 * @param array  $options_array Parsed options array.
	 * @param string $icon_json Icon JSON string.
	 * @return string|WP_Error Result.
	 */
	private function update_attribute( $attribute_id, $row_data, $group_id, $options_array, $icon_json ) {
		global $wpdb;

		// Validate attribute_type.
		$valid_types    = array( 'text', 'number', 'select' );
		$attribute_type = sanitize_text_field( $row_data['attribute_type'] ?? 'text' );
		if ( ! in_array( $attribute_type, $valid_types, true ) ) {
			$attribute_type = 'text';
		}

		$update_data = array(
			'name'           => sanitize_text_field( $row_data['name'] ),
			'group_id'       => $group_id,
			'attribute_type' => $attribute_type,
			'options'        => wp_json_encode( $options_array ),
			'unit'           => sanitize_text_field( $row_data['unit'] ?? '' ),
			'icon'           => $icon_json,
			'filterable'     => intval( $row_data['filterable'] ?? 0 ),
			'display_order'  => intval( $row_data['display_order'] ?? 0 ),
			'status'         => sanitize_text_field( $row_data['status'] ?? 'active' ),
			'updated_at'     => current_time( 'mysql' ),
		);

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$result = $wpdb->update(
			"{$wpdb->prefix}ggmc_attributes",
			$update_data,
			array( 'id' => $attribute_id )
		);

		if ( false !== $result ) {
			return 'updated';
		}

		return new WP_Error( 'update_failed', __( 'Failed to update attribute', 'ggm-commerce' ) );
	}

	/**
	 * Create an attribute group (for auto-creation during attribute import)
	 *
	 * @param string $name Group name.
	 * @return int|null New group ID or null on failure.
	 */
	private function create_attribute_group( $name ) {
		global $wpdb;

		$insert_data = array(
			'name'          => sanitize_text_field( $name ),
			'description'   => '',
			'display_order' => 0,
			'status'        => 'active',
			'created_at'    => current_time( 'mysql' ),
			'updated_at'    => current_time( 'mysql' ),
		);

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
		$result = $wpdb->insert( "{$wpdb->prefix}ggmc_attribute_groups", $insert_data );

		if ( $result ) {
			$new_id = $wpdb->insert_id;
			$this->lookup_maps['groups'][ $name ] = $new_id;
			return $new_id;
		}

		return null;
	}

	/**
	 * Save import progress to transient
	 */
	private function save_progress() {
		set_transient( $this->session_key, $this->progress, HOUR_IN_SECONDS );
	}

	/**
	 * Get current import progress
	 *
	 * @return array Progress data.
	 */
	public function get_progress() {
		$progress = get_transient( $this->session_key );
		return $progress ? $progress : $this->progress;
	}

	/**
	 * Clear import session
	 */
	public function clear_session() {
		delete_transient( $this->session_key );
	}

	/**
	 * Get upload directory for imports
	 *
	 * @return string Upload directory path.
	 */
	public function get_upload_directory() {
		$upload_dir = wp_upload_dir();
		$import_dir = $upload_dir['basedir'] . '/ggm-commerce-imports/';

		if ( ! file_exists( $import_dir ) ) {
			wp_mkdir_p( $import_dir );
			file_put_contents( $import_dir . 'index.php', '<?php // Silence is golden' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
			file_put_contents( $import_dir . '.htaccess', "deny from all\n" ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
		}

		return $import_dir;
	}
}
