<?php
/**
 * GGM Commerce Variation Type CSV Importer
 *
 * Handles CSV import functionality for variation types
 *
 * @package GGM_Commerce
 * @since 1.0.0
 */

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

/**
 * Class GGM_Commerce_Variation_Type_CSV_Importer
 *
 * Imports variation types from CSV format with batch processing
 */
class GGM_Commerce_Variation_Type_CSV_Importer {

	/**
	 * Session key for storing import progress
	 *
	 * @var string
	 */
	private $session_key;

	/**
	 * Import progress data
	 *
	 * @var array
	 */
	private $progress;

	/**
	 * Lookup map for variation type slugs
	 *
	 * @var array
	 */
	private $variation_type_lookup;

	/**
	 * Valid input types
	 *
	 * @var array
	 */
	private $valid_input_types = array( 'select', 'button', 'color', 'image' );

	/**
	 * Constructor
	 */
	public function __construct() {
		$this->session_key = 'ggmc_variation_type_import_' . get_current_user_id();
	}

	/**
	 * Initialize import from CSV file
	 *
	 * @param string $file_path Path to uploaded CSV file.
	 * @param array  $options   Import options.
	 * @return array|WP_Error Initialization result or error.
	 */
	public function init_import( $file_path, $options = array() ) {
		$defaults = array(
			'duplicate_handling' => 'skip', // skip, update, create.
			'batch_size'         => 50,
		);

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

		// Validate file exists.
		if ( ! file_exists( $file_path ) ) {
			return new WP_Error(
				'file_not_found',
				__( 'Import file not found', 'ggm-commerce' )
			);
		}

		// Parse CSV.
		$parsed = $this->parse_csv( $file_path );
		if ( is_wp_error( $parsed ) ) {
			return $parsed;
		}

		// Build lookup maps.
		$this->build_lookup_maps();

		// Initialize progress.
		$this->progress = array(
			'file_path'          => $file_path,
			'options'            => $options,
			'total_rows'         => count( $parsed['rows'] ),
			'processed_rows'     => 0,
			'current_row'        => 0,
			'created'            => 0,
			'updated'            => 0,
			'skipped'            => 0,
			'errors'             => array(),
			'warnings'           => array(),
			'rows'               => $parsed['rows'],
			'headers'            => $parsed['headers'],
			'variation_type_lookup' => $this->variation_type_lookup,
			'status'             => 'processing',
		);

		// Save to transient.
		set_transient( $this->session_key, $this->progress, HOUR_IN_SECONDS );

		return array(
			'success'    => true,
			'total_rows' => $this->progress['total_rows'],
			'message'    => sprintf(
				/* translators: %d: number of rows */
				__( 'Import initialized. Found %d variation types to import.', 'ggm-commerce' ),
				$this->progress['total_rows']
			),
		);
	}

	/**
	 * Process a batch of rows
	 *
	 * @return array|WP_Error Batch result or error.
	 */
	public function process_batch() {
		// Restore session.
		$this->progress = get_transient( $this->session_key );

		if ( false === $this->progress ) {
			return new WP_Error(
				'no_session',
				__( 'No active import session found', 'ggm-commerce' )
			);
		}

		// Restore lookup maps.
		$this->variation_type_lookup = $this->progress['variation_type_lookup'];

		$options    = $this->progress['options'];
		$batch_size = $options['batch_size'];
		$rows       = $this->progress['rows'];
		$start_row  = $this->progress['current_row'];
		$end_row    = min( $start_row + $batch_size, count( $rows ) );

		// Process batch.
		for ( $i = $start_row; $i < $end_row; $i++ ) {
			$row    = $rows[ $i ];
			$result = $this->import_variation_type( $row, $options );

			$this->progress['processed_rows']++;
			$this->progress['current_row'] = $i + 1;

			if ( is_wp_error( $result ) ) {
				$this->progress['errors'][] = array(
					'row'     => $i + 2, // +2 for header row and 0-index.
					'message' => $result->get_error_message(),
					'data'    => $row,
				);
			} elseif ( 'created' === $result ) {
				$this->progress['created']++;
			} elseif ( 'updated' === $result ) {
				$this->progress['updated']++;
			} elseif ( 'skipped' === $result ) {
				$this->progress['skipped']++;
			}
		}

		// Check if complete.
		if ( $this->progress['current_row'] >= count( $rows ) ) {
			$this->progress['status'] = 'complete';

			// Clean up file.
			if ( file_exists( $this->progress['file_path'] ) ) {
				wp_delete_file( $this->progress['file_path'] );
			}

			// Delete transient.
			delete_transient( $this->session_key );
		} else {
			// Update lookup maps.
			$this->progress['variation_type_lookup'] = $this->variation_type_lookup;

			// Save progress.
			set_transient( $this->session_key, $this->progress, HOUR_IN_SECONDS );
		}

		return array(
			'success'        => true,
			'status'         => $this->progress['status'],
			'total_rows'     => $this->progress['total_rows'],
			'processed_rows' => $this->progress['processed_rows'],
			'created'        => $this->progress['created'],
			'updated'        => $this->progress['updated'],
			'skipped'        => $this->progress['skipped'],
			'errors'         => $this->progress['errors'],
			'warnings'       => $this->progress['warnings'],
			'progress'       => $this->progress['total_rows'] > 0
				? round( ( $this->progress['processed_rows'] / $this->progress['total_rows'] ) * 100 )
				: 100,
		);
	}

	/**
	 * Import a single variation type
	 *
	 * @param array $row_data Row data from CSV.
	 * @param array $options  Import options.
	 * @return string|WP_Error Result status or error.
	 */
	private function import_variation_type( $row_data, $options ) {
		global $wpdb;

		// Validate required fields.
		$name = trim( $row_data['name'] ?? '' );
		if ( empty( $name ) ) {
			return new WP_Error( 'missing_name', __( 'Missing required field: name', 'ggm-commerce' ) );
		}

		// Generate or use provided slug.
		$slug = ! empty( $row_data['slug'] ) ? sanitize_title( $row_data['slug'] ) : sanitize_title( $name );

		// Validate and normalize input_type.
		$input_type = strtolower( trim( $row_data['input_type'] ?? 'select' ) );
		if ( ! in_array( $input_type, $this->valid_input_types, true ) ) {
			$this->progress['warnings'][] = array(
				'message' => sprintf(
					/* translators: 1: input type, 2: variation type name */
					__( 'Invalid input_type "%1$s" for variation type "%2$s", using "select"', 'ggm-commerce' ),
					$input_type,
					$name
				),
			);
			$input_type = 'select';
		}

		// Parse options JSON.
		$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: variation type name */
						__( 'Invalid JSON in options for variation type "%s", using empty options', 'ggm-commerce' ),
						$name
					),
				);
			}
		}

		// Validate options structure based on input_type.
		$options_array = $this->validate_options_structure( $options_array, $input_type, $name );

		// Check for existing variation type.
		$existing_id = $this->variation_type_lookup[ $slug ] ?? null;

		if ( $existing_id ) {
			// Handle duplicate.
			if ( 'skip' === $options['duplicate_handling'] ) {
				return 'skipped';
			} elseif ( 'update' === $options['duplicate_handling'] ) {
				// Update existing.
				$update_data = array(
					'name'          => $name,
					'input_type'    => $input_type,
					'options'       => wp_json_encode( $options_array, JSON_UNESCAPED_UNICODE ),
					'display_order' => absint( $row_data['display_order'] ?? 0 ),
					'status'        => $this->validate_status( $row_data['status'] ?? 'active' ),
				);

				// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
				$result = $wpdb->update(
					$wpdb->prefix . 'ggmc_variation_types',
					$update_data,
					array( 'id' => $existing_id ),
					array( '%s', '%s', '%s', '%d', '%s' ),
					array( '%d' )
				);

				if ( false === $result ) {
					return new WP_Error( 'db_error', __( 'Database error updating variation type', 'ggm-commerce' ) );
				}

				return 'updated';
			}
			// 'create' mode - generate unique slug.
			$slug = $this->generate_unique_slug( $slug );
		}

		// Insert new variation type.
		$insert_data = array(
			'name'          => $name,
			'slug'          => $slug,
			'input_type'    => $input_type,
			'options'       => wp_json_encode( $options_array, JSON_UNESCAPED_UNICODE ),
			'display_order' => absint( $row_data['display_order'] ?? 0 ),
			'status'        => $this->validate_status( $row_data['status'] ?? 'active' ),
		);

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

		if ( false === $result ) {
			return new WP_Error( 'db_error', __( 'Database error inserting variation type', 'ggm-commerce' ) );
		}

		// Update lookup map.
		$this->variation_type_lookup[ $slug ] = $wpdb->insert_id;

		return 'created';
	}

	/**
	 * Validate options structure based on input type
	 *
	 * @param array  $options    Options array.
	 * @param string $input_type Input type.
	 * @param string $name       Variation type name for warnings.
	 * @return array Validated options.
	 */
	private function validate_options_structure( $options, $input_type, $name ) {
		if ( empty( $options ) || ! is_array( $options ) ) {
			return array();
		}

		$validated = array();

		foreach ( $options as $option ) {
			if ( ! is_array( $option ) ) {
				continue;
			}

			// All options need at least a value.
			if ( ! isset( $option['value'] ) ) {
				continue;
			}

			$valid_option = array(
				'value' => sanitize_text_field( $option['value'] ),
				'label' => sanitize_text_field( $option['label'] ?? $option['value'] ),
			);

			// Add type-specific properties.
			switch ( $input_type ) {
				case 'color':
					if ( ! empty( $option['color'] ) ) {
						// Validate hex color.
						if ( preg_match( '/^#[a-fA-F0-9]{6}$/', $option['color'] ) ) {
							$valid_option['color'] = $option['color'];
						} else {
							$this->progress['warnings'][] = array(
								'message' => sprintf(
									/* translators: 1: color value, 2: variation type name */
									__( 'Invalid color hex "%1$s" for variation type "%2$s"', 'ggm-commerce' ),
									$option['color'],
									$name
								),
							);
						}
					}
					break;

				case 'image':
					if ( ! empty( $option['image_id'] ) ) {
						$valid_option['image_id'] = absint( $option['image_id'] );
					}
					if ( ! empty( $option['image_url'] ) ) {
						$valid_option['image_url'] = esc_url_raw( $option['image_url'] );
					}
					break;
			}

			// Optional price modifier.
			if ( isset( $option['price_modifier'] ) ) {
				$valid_option['price_modifier'] = floatval( $option['price_modifier'] );
			}

			$validated[] = $valid_option;
		}

		return $validated;
	}

	/**
	 * Parse CSV file
	 *
	 * @param string $file_path Path to CSV file.
	 * @return array|WP_Error Parsed data or error.
	 */
	private function parse_csv( $file_path ) {
		$handle = fopen( $file_path, 'r' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen

		if ( false === $handle ) {
			return new WP_Error(
				'file_open_error',
				__( 'Could not open import 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 ( false === $headers || empty( $headers ) ) {
			fclose( $handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose
			return new WP_Error(
				'invalid_csv',
				__( 'Could not read CSV headers', 'ggm-commerce' )
			);
		}

		// Normalize headers.
		$headers = array_map( 'trim', $headers );
		$headers = array_map( 'strtolower', $headers );

		// Validate required headers.
		if ( ! in_array( 'name', $headers, true ) ) {
			fclose( $handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose
			return new WP_Error(
				'missing_headers',
				__( 'CSV is missing required header: name', 'ggm-commerce' )
			);
		}

		// Read rows.
		$rows = array();
		while ( ( $row = fgetcsv( $handle ) ) !== false ) { // phpcs:ignore WordPress.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition
			if ( empty( array_filter( $row ) ) ) {
				continue; // Skip empty rows.
			}

			// Map to associative array.
			$row_data = array();
			foreach ( $headers as $index => $header ) {
				$row_data[ $header ] = isset( $row[ $index ] ) ? $row[ $index ] : '';
			}
			$rows[] = $row_data;
		}

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

		if ( empty( $rows ) ) {
			return new WP_Error(
				'empty_csv',
				__( 'CSV file contains no data rows', 'ggm-commerce' )
			);
		}

		return array(
			'headers' => $headers,
			'rows'    => $rows,
		);
	}

	/**
	 * Build lookup maps for existing data
	 */
	private function build_lookup_maps() {
		global $wpdb;

		// Build variation type slug => id map.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$variation_types = $wpdb->get_results(
			"SELECT id, slug FROM {$wpdb->prefix}ggmc_variation_types",
			ARRAY_A
		);

		$this->variation_type_lookup = array();
		foreach ( $variation_types as $type ) {
			$this->variation_type_lookup[ $type['slug'] ] = (int) $type['id'];
		}
	}

	/**
	 * Generate unique slug
	 *
	 * @param string $slug Base slug.
	 * @return string Unique slug.
	 */
	private function generate_unique_slug( $slug ) {
		$original_slug = $slug;
		$counter       = 1;

		while ( isset( $this->variation_type_lookup[ $slug ] ) ) {
			$slug = $original_slug . '-' . $counter;
			$counter++;
		}

		return $slug;
	}

	/**
	 * Validate status value
	 *
	 * @param string $status Status value.
	 * @return string Validated status.
	 */
	private function validate_status( $status ) {
		$valid_statuses = array( 'active', 'inactive' );
		$status         = strtolower( trim( $status ) );

		return in_array( $status, $valid_statuses, true ) ? $status : 'active';
	}

	/**
	 * Cancel active import
	 *
	 * @return bool Whether cancellation was successful.
	 */
	public function cancel_import() {
		$progress = get_transient( $this->session_key );

		if ( $progress && ! empty( $progress['file_path'] ) && file_exists( $progress['file_path'] ) ) {
			wp_delete_file( $progress['file_path'] );
		}

		return delete_transient( $this->session_key );
	}

	/**
	 * Get current import status
	 *
	 * @return array|false Progress data or false if no active import.
	 */
	public function get_status() {
		$progress = get_transient( $this->session_key );

		if ( false === $progress ) {
			return false;
		}

		return array(
			'status'         => $progress['status'],
			'total_rows'     => $progress['total_rows'],
			'processed_rows' => $progress['processed_rows'],
			'created'        => $progress['created'],
			'updated'        => $progress['updated'],
			'skipped'        => $progress['skipped'],
			'errors'         => $progress['errors'],
			'warnings'       => $progress['warnings'],
			'progress'       => $progress['total_rows'] > 0
				? round( ( $progress['processed_rows'] / $progress['total_rows'] ) * 100 )
				: 100,
		);
	}
}
