<?php
/**
 * Class Shipment_Template_Manager.
 *
 * Manages shipment templates CRUD operations and AJAX handlers.
 *
 * @package Inspire_Labs\Alsendo_Connect
 */

namespace Inspire_Labs\Alsendo_Connect;

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

/**
 * Shipment_Template_Manager
 */
class Shipment_Template_Manager {

	/**
	 * Option key for the default package template slug.
	 */
	const DEFAULT_TEMPLATE_OPTION = 'alsendo_connect_settings_general_select_package_template';

	/**
	 * Instance
	 *
	 * @var self|null
	 */
	private static $instance = null;

	/**
	 * Option name for storing templates.
	 *
	 * @var string
	 */
	private $option_name;

	/**
	 * Templates data.
	 *
	 * @var array
	 */
	private $data;

	/**
	 * Construct
	 */
	private function __construct() {
		$this->option_name = Plugin::APP_PREFIX . '_shipment_templates';
		$this->data        = get_option( $this->option_name, array() );
		ksort( $this->data );
	}

	/**
	 * Get instance
	 *
	 * @return self
	 */
	public static function get_instance(): self {
		if ( null === self::$instance ) {
			self::$instance = new self();
		}

		return self::$instance;
	}

	/**
	 * Initialize AJAX hooks.
	 *
	 * @return void
	 */
	public function init() {
		add_action( 'wp_ajax_alsendo_connect_shipment_template', array( $this, 'ajax_template' ) );
		add_action( 'wp_ajax_alsendo_connect_shipment_template_delete', array( $this, 'ajax_template_delete' ) );
		add_action( 'wp_ajax_alsendo_connect_shipment_template_set_default', array( $this, 'ajax_set_default' ) );
	}

	/**
	 * Get the current default template slug.
	 *
	 * @return string
	 */
	public static function get_default_template_slug(): string {
		return (string) get_option( self::DEFAULT_TEMPLATE_OPTION, '' );
	}

	/**
	 * Get parcel types for specific API type.
	 *
	 * @param string $api_type Current API type (apaczka, ecolet, zaslat).
	 *
	 * @return array
	 */
	public static function get_parcel_types_for_api( string $api_type ): array {
		if ( 'apaczka' === $api_type ) {
			return array(
				'box'             => __( 'Box', 'alsendo-connect' ),
				'europalette'     => __( 'Europallet', 'alsendo-connect' ),
				'palette_60x80'   => __( 'Pallet 60x80', 'alsendo-connect' ),
				'palette_120x100' => __( 'Pallet 120x100', 'alsendo-connect' ),
				'palette_120x120' => __( 'Pallet 120x120', 'alsendo-connect' ),
			);
		}

		if ( 'ecolet' === $api_type ) {
			return array(
				'package'         => __( 'Package', 'alsendo-connect' ),
				'envelope'        => __( 'Envelope', 'alsendo-connect' ),
				'europalette'     => __( 'Euro palette 120x80', 'alsendo-connect' ),
				'palette_60x80'   => __( 'Half palette 80x60', 'alsendo-connect' ),
				'palette_120x100' => __( 'Industrial palette 120x100', 'alsendo-connect' ),
				'palette_120x120' => __( 'Industrial palette 120x120', 'alsendo-connect' ),
			);
		}

		if ( 'zaslat' === $api_type ) {
			return array(
				'P2P' => __( 'P2P', 'alsendo-connect' ),
				'P2H' => __( 'P2H', 'alsendo-connect' ),
				'H2H' => __( 'H2H', 'alsendo-connect' ),
				'H2P' => __( 'H2P', 'alsendo-connect' ),
			);
		}

		return array(
			'package' => __( 'Package', 'alsendo-connect' ),
		);
	}

	/**
	 * Get shipping method options for specific API type.
	 *
	 * @param string $api_type Current API type.
	 *
	 * @return array
	 */
	public static function get_shipping_methods_for_api( string $api_type ): array {
		if ( 'apaczka' === $api_type ) {
			return array(
				'POINT'   => __( 'Shipment directly at the point', 'alsendo-connect' ),
				'COURIER' => __( 'Courier pickup request', 'alsendo-connect' ),
				'SELF'    => __( 'Pickup self', 'alsendo-connect' ),
			);
		}

		if ( 'ecolet' === $api_type ) {
			return array(
				'COURIER' => __( 'Courier pickup request', 'alsendo-connect' ),
				'SELF_'   => __( 'Pickup self', 'alsendo-connect' ),
			);
		}

		if ( 'zaslat' === $api_type ) {
			return array(
				'ONDEMAND'   => __( 'ONDEMAND', 'alsendo-connect' ),
				'OCCASIONAL' => __( 'OCCASIONAL', 'alsendo-connect' ),
			);
		}

		return array();
	}

	/**
	 * Get package contents options.
	 *
	 * @return array
	 */
	public static function get_package_contents_options(): array {
		return array(
			'order_id'     => __( 'Order ID', 'alsendo-connect' ),
			'product_id'   => __( 'Product ID(s)', 'alsendo-connect' ),
			'product_name' => __( 'Product Name(s)', 'alsendo-connect' ),
			'custom_text'  => __( 'I shall write my text', 'alsendo-connect' ),
		);
	}

	/**
	 * Get all templates data.
	 *
	 * @return array
	 */
	public function get_data(): array {
		return $this->data;
	}

	/**
	 * Get value of template by ID.
	 *
	 * @param string $template_id Template ID.
	 *
	 * @return array
	 */
	public function get_value( string $template_id ): array {
		if ( isset( $this->data[ $template_id ] ) ) {
			return $this->data[ $template_id ];
		}

		return array();
	}

	/**
	 * Get templates list with optional nonce for delete actions.
	 * Merges legacy templates (from old storage) that haven't been migrated yet.
	 *
	 * @param bool $add_nonce Whether to add delete nonce to each template.
	 *
	 * @return array
	 */
	public function get_templates( bool $add_nonce = false ): array {
		$output    = array();
		$new_slugs = array();

		foreach ( $this->data as $key => $value ) {
			$data       = $this->set_data( $value );
			$data['id'] = $key;

			if ( true === $add_nonce ) {
				$data['_nonce'] = wp_create_nonce( 'alsendo_connect_ajax_shipment_template_delete_' . $key );
			}

			if ( ! empty( $data['template_slug'] ) ) {
				$new_slugs[] = $data['template_slug'];
			}

			$output[] = $data;
		}

		$legacy = $this->get_legacy_templates_raw();
		if ( ! empty( $legacy ) ) {
			$next_id = $this->get_next_legacy_display_id();
			foreach ( $legacy as $slug => $old_tpl ) {
				if ( in_array( $slug, $new_slugs, true ) ) {
					continue;
				}

				$converted                 = $this->convert_legacy_template( $old_tpl, $slug );
				$converted['id']           = $next_id;
				$converted['_legacy_slug'] = $slug;

				if ( true === $add_nonce ) {
					$converted['_nonce'] = wp_create_nonce( 'alsendo_connect_ajax_shipment_template_delete_' . $next_id );
				}

				$output[] = $converted;
				++$next_id;
			}
		}

		return $output;
	}

	/**
	 * Save template data.
	 *
	 * @param array $data Template data.
	 *
	 * @return void
	 */
	public function save( array $data ) {
		if ( empty( $data['id'] ) ) {
			$data['id'] = (int) $this->array_key_last( $this->data ) + 1;
		}

		if ( ! empty( $data['name'] ) ) {
			$data['template_slug'] = sanitize_title( $data['name'] );
		}

		$this->data[ $data['id'] ] = $this->set_data( $data );

		update_option( $this->option_name, $this->data );
	}

	/**
	 * Delete template by ID.
	 *
	 * @param string $id Template ID.
	 *
	 * @return void
	 */
	public function delete( string $id ) {
		if ( ! empty( $id ) ) {
			unset( $this->data[ $id ] );

			update_option( $this->option_name, $this->data );
		}
	}

	/**
	 * Process AJAX template save/update request.
	 *
	 * @return void
	 */
	public function ajax_template() {
		if ( ! empty( $_POST['template'] ) ) { // phpcs:ignore
			$data = wp_unslash( $_POST['template'] ); // phpcs:ignore

			if ( ! empty( $data['_nonce'] ) && wp_verify_nonce( sanitize_text_field( $data['_nonce'] ), 'alsendo_connect_ajax_shipment_template' ) ) {
				$data['id']                  = ! empty( $data['id'] ) ? absint( $data['id'] ) : 0;
				$data['name']                = ! empty( $data['name'] ) ? sanitize_text_field( $data['name'] ) : '';
				$data['parcel_type']         = ! empty( $data['parcel_type'] ) ? sanitize_text_field( $data['parcel_type'] ) : '';
				$data['is_nstd']             = ! empty( $data['is_nstd'] ) ? sanitize_text_field( $data['is_nstd'] ) : 'no';
				$data['package_length']      = ! empty( $data['package_length'] ) ? absint( $data['package_length'] ) : '';
				$data['package_width']       = ! empty( $data['package_width'] ) ? absint( $data['package_width'] ) : '';
				$data['package_height']      = ! empty( $data['package_height'] ) ? absint( $data['package_height'] ) : '';
				$data['package_weight']      = ! empty( $data['package_weight'] ) ? floatval( $data['package_weight'] ) : '';
				$data['package_contents']    = ! empty( $data['package_contents'] ) ? sanitize_text_field( $data['package_contents'] ) : '';
				$data['custom_text_content'] = ! empty( $data['custom_text_content'] ) ? sanitize_text_field( $data['custom_text_content'] ) : '';
				$data['declared_content']    = ! empty( $data['declared_content'] ) ? sanitize_text_field( $data['declared_content'] ) : '';
				$data['shipping_method']     = ! empty( $data['shipping_method'] ) ? $this->normalize_shipping_method( sanitize_text_field( $data['shipping_method'] ) ) : '';
				$data['pickup_hour_from']    = ! empty( $data['pickup_hour_from'] ) ? sanitize_text_field( $data['pickup_hour_from'] ) : '';
				$data['pickup_hour_to']      = ! empty( $data['pickup_hour_to'] ) ? sanitize_text_field( $data['pickup_hour_to'] ) : '';
				$raw_additional_parcels      = $data['additional_parcels'] ?? null;
				$data['additional_parcels']  = array();
				$set_as_default              = ! empty( $data['set_as_default'] ) && '1' === $data['set_as_default'];

				if ( ! empty( $raw_additional_parcels ) && is_array( $raw_additional_parcels ) ) {
					$additional = array();
					foreach ( $raw_additional_parcels as $idx => $parcel ) {
						if ( ! is_array( $parcel ) ) {
							continue;
						}
						$additional[] = array(
							'parcel_type'    => ! empty( $parcel['parcel_type'] ) ? sanitize_text_field( $parcel['parcel_type'] ) : '',
							'is_nstd'        => ! empty( $parcel['is_nstd'] ) ? sanitize_text_field( $parcel['is_nstd'] ) : 'no',
							'package_length' => ! empty( $parcel['package_length'] ) ? absint( $parcel['package_length'] ) : '',
							'package_width'  => ! empty( $parcel['package_width'] ) ? absint( $parcel['package_width'] ) : '',
							'package_height' => ! empty( $parcel['package_height'] ) ? absint( $parcel['package_height'] ) : '',
							'package_weight' => ! empty( $parcel['package_weight'] ) ? floatval( $parcel['package_weight'] ) : '',
						);
					}
					$data['additional_parcels'] = array_slice( $additional, 0, 9 );
				}

				// Validate.
				if ( 0 > $data['id'] ) {
					$data['id'] = 0;
				}
				if ( empty( $data['name'] ) ) {
					wp_send_json(
						array(
							array(
								'type'    => 'error',
								'message' => __( 'Field "Name" is required.', 'alsendo-connect' ),
							),
						),
						\WP_Http::UNPROCESSABLE_ENTITY
					);

					return;
				}

				if ( $this->name_exists( $data['name'], $data['id'] ) ) {
					wp_send_json(
						array(
							array(
								'type'    => 'error',
								'message' => __( 'A template with this name already exists. Please choose a different name.', 'alsendo-connect' ),
							),
						),
						\WP_Http::UNPROCESSABLE_ENTITY
					);

					return;
				}

				$legacy_slug = ! empty( $data['_legacy_slug'] ) ? sanitize_text_field( $data['_legacy_slug'] ) : '';

				if ( ! empty( $legacy_slug ) ) {
					$data['id'] = 0;
				}

				$this->save( $data );

				if ( ! empty( $legacy_slug ) ) {
					$this->remove_legacy_template( $legacy_slug );
				}

				if ( $set_as_default ) {
					update_option( self::DEFAULT_TEMPLATE_OPTION, sanitize_title( $data['name'] ) );
				}

				wp_send_json( array(), \WP_Http::OK );

				return;
			}
		}

		wp_send_json( '', \WP_Http::BAD_REQUEST );
	}

	/**
	 * Process AJAX template delete request.
	 *
	 * @return void
	 */
	public function ajax_template_delete() {
		if ( ! empty( $_POST['template_delete'] ) ) { // phpcs:ignore
			$data = wp_unslash( $_POST['template_delete'] ); // phpcs:ignore
			$id   = ! empty( $data['id'] ) ? absint( $data['id'] ) : 0;

			if ( ! empty( $data['_nonce'] ) && wp_verify_nonce( sanitize_text_field( $data['_nonce'] ), 'alsendo_connect_ajax_shipment_template_delete_' . $id ) ) {
				$legacy_slug = ! empty( $data['_legacy_slug'] ) ? sanitize_text_field( $data['_legacy_slug'] ) : '';

				$this->delete( $id );

				if ( ! empty( $legacy_slug ) ) {
					$this->remove_legacy_template( $legacy_slug );
				}

				wp_send_json( array(), \WP_Http::NO_CONTENT );

				return;
			}
		}

		wp_send_json( '', \WP_Http::BAD_REQUEST );
	}

	/**
	 * Process AJAX set default template request.
	 *
	 * @return void
	 */
	public function ajax_set_default() {
		if ( ! empty( $_POST['_nonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_nonce'] ) ), 'alsendo_connect_ajax_set_default_template' ) ) {
			$template_slug = ! empty( $_POST['template_slug'] ) ? sanitize_text_field( wp_unslash( $_POST['template_slug'] ) ) : '';

			if ( ! empty( $template_slug ) ) {
				update_option( self::DEFAULT_TEMPLATE_OPTION, $template_slug );
				wp_send_json( array( 'slug' => $template_slug ), \WP_Http::OK );

				return;
			}
		}

		wp_send_json( '', \WP_Http::BAD_REQUEST );
	}

	/**
	 * Get legacy option name used by Gateway_Settings_Templates_Helper.
	 *
	 * @return string
	 */
	private function get_legacy_option_name(): string {
		return Plugin::APP_PREFIX . 'gateway_opts_templates';
	}

	/**
	 * Read raw legacy templates from old storage.
	 *
	 * @return array
	 */
	private function get_legacy_templates_raw(): array {
		$templates = get_option( $this->get_legacy_option_name(), array() );

		return is_array( $templates ) ? $templates : array();
	}

	/**
	 * Convert a single legacy template (old format) to new format.
	 *
	 * @param array  $old_tpl Legacy template data with 'name' and 'options'.
	 * @param string $slug    Legacy template slug (array key).
	 *
	 * @return array
	 */
	private function convert_legacy_template( array $old_tpl, string $slug ): array {
		$prefix  = 'alsendo_connect_settings_general_';
		$options = isset( $old_tpl['options'] ) ? $old_tpl['options'] : array();

		return array(
			'name'                => $old_tpl['name'] ?? '',
			'template_slug'       => $slug,
			'parcel_type'         => $options[ $prefix . 'parcel_type' ] ?? null,
			'is_nstd'             => $options[ $prefix . 'is_nstd' ] ?? 'no',
			'package_length'      => $options[ $prefix . 'package_length' ] ?? null,
			'package_width'       => $options[ $prefix . 'package_width' ] ?? null,
			'package_height'      => $options[ $prefix . 'package_height' ] ?? null,
			'package_weight'      => $options[ $prefix . 'package_weight' ] ?? null,
			'package_contents'    => $options[ $prefix . 'package_contents' ] ?? null,
			'custom_text_content' => $options[ $prefix . 'custom_text_content' ] ?? null,
			'declared_content'    => $options[ $prefix . 'declared_content' ] ?? null,
			'shipping_method'     => $this->normalize_shipping_method( $options[ $prefix . 'shipping_method' ] ?? '' ),
			'pickup_hour_from'    => $options[ $prefix . 'pickup_hour_from' ] ?? null,
			'pickup_hour_to'      => $options[ $prefix . 'pickup_hour_to' ] ?? null,
		);
	}

	/**
	 * Generate starting ID for legacy templates display (avoids collision with real IDs).
	 *
	 * @return int
	 */
	private function get_next_legacy_display_id(): int {
		$max = 0;
		foreach ( $this->data as $key => $value ) {
			if ( (int) $key > $max ) {
				$max = (int) $key;
			}
		}

		return max( $max + 1, 90000 );
	}

	/**
	 * Remove a single template from legacy storage by slug.
	 *
	 * @param string $slug Template slug.
	 *
	 * @return void
	 */
	private function remove_legacy_template( string $slug ): void {
		$option_name = $this->get_legacy_option_name();
		$templates   = get_option( $option_name, array() );

		if ( is_array( $templates ) && isset( $templates[ $slug ] ) ) {
			unset( $templates[ $slug ] );
			update_option( $option_name, $templates );
		}
	}

	/**
	 * Check if a template with the given name already exists.
	 *
	 * @param string $name        Template name to check.
	 * @param int    $exclude_id  Template ID to exclude from the check (for edits).
	 *
	 * @return bool
	 */
	private function name_exists( string $name, int $exclude_id = 0 ): bool {
		$name_lower = mb_strtolower( trim( $name ) );
		foreach ( $this->data as $key => $value ) {
			if ( (int) $key === $exclude_id ) {
				continue;
			}
			$existing_name = isset( $value['name'] ) ? mb_strtolower( trim( $value['name'] ) ) : '';
			if ( $existing_name === $name_lower ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Gets the last key of an array.
	 *
	 * @param mixed $array_data An array.
	 *
	 * @return mixed
	 */
	private function array_key_last( $array_data ) {
		if ( ! is_array( $array_data ) || empty( $array_data ) ) {
			return null;
		}

		return array_keys( $array_data )[ count( $array_data ) - 1 ];
	}

	/**
	 * Normalize template data structure.
	 *
	 * @param array $value Raw values.
	 *
	 * @return array
	 */
	private function set_data( array $value ): array {
		$data = array(
			'name'                => null,
			'template_slug'       => null,
			'parcel_type'         => null,
			'is_nstd'             => 'no',
			'package_length'      => null,
			'package_width'       => null,
			'package_height'      => null,
			'package_weight'      => null,
			'package_contents'    => null,
			'custom_text_content' => null,
			'declared_content'    => null,
			'additional_parcels'  => array(),
			'shipping_method'     => null,
			'pickup_hour_from'    => null,
			'pickup_hour_to'      => null,
		);
		foreach ( $data as $key => $v ) {
			if ( isset( $value[ $key ] ) ) {
				$data[ $key ] = $value[ $key ];
			}
		}

		$data['shipping_method'] = $this->normalize_shipping_method( $data['shipping_method'] ?? '' );

		return $data;
	}

	/**
	 * Normalize shipping method values for backward compatibility.
	 *
	 * @param string $shipping_method Shipping method code.
	 *
	 * @return string
	 */
	private function normalize_shipping_method( string $shipping_method ): string {
		if ( 0 === strpos( $shipping_method, 'SELF_' ) ) {
			return 'SELF_';
		}

		return $shipping_method;
	}
}
