<?php
/**
 * Mercados personalizados para códigos de etiqueta EAN-13.
 *
 * @package Logistica
 */

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

/**
 * Opciones de mercado persistidas (wp_options) + catálogo base + registros.
 */
class Logistica_Label_Etiqueta_Mercados {

	const OPTION_KEY        = 'logistica_etiqueta_mercados';
	const HIDDEN_OPTION_KEY = 'logistica_etiqueta_mercados_ocultos';

	/**
	 * @return array<string, array{label:string}>
	 */
	public static function get_hidden() {
		$hidden = get_option( self::HIDDEN_OPTION_KEY, array() );

		if ( ! is_array( $hidden ) ) {
			return array();
		}

		$clean = array();
		foreach ( $hidden as $key => $entry ) {
			$key = self::normalize_key( $key );
			if ( '' === $key ) {
				continue;
			}

			if ( true === $entry || 1 === $entry || '1' === $entry ) {
				$clean[ $key ] = array( 'label' => '' );
				continue;
			}

			if ( ! is_array( $entry ) ) {
				continue;
			}

			$clean[ $key ] = array(
				'label' => trim( sanitize_text_field( (string) ( $entry['label'] ?? '' ) ) ),
			);
		}

		return $clean;
	}

	/**
	 * @param string $key Clave del mercado.
	 * @return bool
	 */
	public static function is_hidden( $key ) {
		$key = self::normalize_key( $key );

		return '' !== $key && isset( self::get_hidden()[ $key ] );
	}

	/**
	 * @param string $key   Clave del mercado.
	 * @param string $label Etiqueta visible al ocultar.
	 * @return void
	 */
	private static function hide_key( $key, $label = '' ) {
		$key = self::normalize_key( $key );
		if ( '' === $key ) {
			return;
		}

		$hidden         = self::get_hidden();
		$hidden[ $key ] = array(
			'label' => trim( sanitize_text_field( (string) $label ) ),
		);
		update_option( self::HIDDEN_OPTION_KEY, $hidden, false );
	}

	/**
	 * @param string $key Clave del mercado.
	 * @return void
	 */
	private static function unhide_key( $key ) {
		$key = self::normalize_key( $key );
		if ( '' === $key ) {
			return;
		}

		$hidden = self::get_hidden();
		if ( ! isset( $hidden[ $key ] ) ) {
			return;
		}

		unset( $hidden[ $key ] );
		update_option( self::HIDDEN_OPTION_KEY, $hidden, false );
	}

	/**
	 * @return array<string, array{label:string,market_digit:string}>
	 */
	public static function get_built_in() {
		return array(
			'NAC' => array(
				'label'       => __( 'Nacional', 'logistica' ),
				'market_digit' => '1',
			),
			'EXP' => array(
				'label'       => __( 'Exportación', 'logistica' ),
				'market_digit' => '2',
			),
		);
	}

	/**
	 * @return array<string, array{label:string,market_digit:string}>
	 */
	public static function get_saved() {
		$saved = get_option( self::OPTION_KEY, array() );

		if ( ! is_array( $saved ) ) {
			return array();
		}

		$clean = array();
		foreach ( $saved as $key => $entry ) {
			if ( ! is_array( $entry ) ) {
				continue;
			}

			$key = self::normalize_key( $key );
			if ( '' === $key ) {
				continue;
			}

			$label       = trim( sanitize_text_field( (string) ( $entry['label'] ?? '' ) ) );
			$market_digit = self::normalize_market_digit( $entry['market_digit'] ?? '' );

			if ( '' === $label || '' === $market_digit ) {
				continue;
			}

			$clean[ $key ] = array(
				'label'        => $label,
				'market_digit' => $market_digit,
			);
		}

		return $clean;
	}

	/**
	 * @return array<string, string> clave => etiqueta visible.
	 */
	public static function get_option_labels() {
		global $wpdb;

		$options = array();
		$built_in = self::get_built_in();
		$saved    = self::get_saved();

		foreach ( $built_in as $key => $entry ) {
			if ( self::is_hidden( $key ) ) {
				continue;
			}
			$options[ $key ] = isset( $saved[ $key ]['label'] ) ? (string) $saved[ $key ]['label'] : (string) $entry['label'];
		}

		foreach ( $saved as $key => $entry ) {
			if ( isset( $built_in[ $key ] ) || self::is_hidden( $key ) ) {
				continue;
			}
			$options[ $key ] = (string) $entry['label'];
		}

		$table = Logistica_Database::table( 'etiqueta_productos' );
		if ( $table ) {
			$rows = $wpdb->get_col( "SELECT DISTINCT mercado FROM {$table} WHERE mercado <> '' ORDER BY mercado ASC" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
			if ( is_array( $rows ) ) {
				foreach ( $rows as $row ) {
					$key = self::normalize_key( $row );
					if ( '' === $key || self::is_hidden( $key ) || isset( $options[ $key ] ) ) {
						continue;
					}
					$options[ $key ] = ucwords( strtolower( str_replace( '_', ' ', $key ) ) );
				}
			}
		}

		uasort(
			$options,
			static function ( $a, $b ) {
				return strnatcasecmp( (string) $a, (string) $b );
			}
		);

		return $options;
	}

	/**
	 * @return list<array{key:string,label:string,market_digit:string,built_in:bool,can_delete:bool}>
	 */
	public static function get_admin_entries() {
		$entries = array();
		$seen    = array();
		$saved   = self::get_saved();
		$built_in = self::get_built_in();

		foreach ( $built_in as $key => $entry ) {
			if ( self::is_hidden( $key ) ) {
				continue;
			}

			$entries[] = array(
				'key'          => $key,
				'label'        => isset( $saved[ $key ]['label'] ) ? (string) $saved[ $key ]['label'] : (string) $entry['label'],
				'market_digit' => (string) $entry['market_digit'],
				'built_in'     => true,
				'can_delete'   => true,
			);
			$seen[ $key ] = true;
		}

		foreach ( $saved as $key => $entry ) {
			if ( isset( $seen[ $key ] ) || self::is_hidden( $key ) ) {
				continue;
			}

			$entries[] = array(
				'key'          => $key,
				'label'        => (string) $entry['label'],
				'market_digit' => (string) $entry['market_digit'],
				'built_in'     => false,
				'can_delete'   => true,
			);
			$seen[ $key ] = true;
		}

		foreach ( self::get_option_labels() as $key => $label ) {
			if ( isset( $seen[ $key ] ) || self::is_hidden( $key ) ) {
				continue;
			}

			$digit = self::market_digit_for( $key );
			$entries[] = array(
				'key'          => $key,
				'label'        => (string) $label,
				'market_digit' => (string) ( $digit ?? '' ),
				'built_in'     => false,
				'can_delete'   => true,
			);
			$seen[ $key ] = true;
		}

		usort(
			$entries,
			static function ( $a, $b ) {
				return strnatcasecmp( (string) $a['label'], (string) $b['label'] );
			}
		);

		return $entries;
	}

	/**
	 * @param mixed $value Clave o etiqueta.
	 * @return string
	 */
	public static function normalize_key( $value ) {
		$text = remove_accents( strtoupper( trim( (string) $value ) ) );
		$text = preg_replace( '/[^A-Z0-9]+/', '', $text );

		return trim( (string) $text );
	}

	/**
	 * @param mixed $value Dígito EAN de mercado (1-9).
	 * @return string
	 */
	public static function normalize_market_digit( $value ) {
		$digits = preg_replace( '/\D+/', '', (string) $value );

		if ( 1 !== strlen( $digits ) || '0' === $digits ) {
			return '';
		}

		return $digits;
	}

	/**
	 * @return array<string, true>
	 */
	public static function used_market_digits() {
		$used = array();

		foreach ( self::get_built_in() as $key => $entry ) {
			if ( self::is_hidden( $key ) ) {
				continue;
			}
			$used[ (string) $entry['market_digit'] ] = true;
		}

		foreach ( self::get_saved() as $entry ) {
			if ( ! empty( $entry['market_digit'] ) ) {
				$used[ (string) $entry['market_digit'] ] = true;
			}
		}

		return $used;
	}

	/**
	 * @return string|null
	 */
	public static function next_market_digit() {
		$used = self::used_market_digits();

		for ( $digit = 3; $digit <= 9; $digit++ ) {
			$code = (string) $digit;
			if ( empty( $used[ $code ] ) ) {
				return $code;
			}
		}

		return null;
	}

	/**
	 * @param string $key Clave del mercado.
	 * @return int|null
	 */
	public static function market_digit_for( $key ) {
		$key = self::normalize_key( $key );
		if ( '' === $key ) {
			return null;
		}

		$built_in = self::get_built_in();
		if ( isset( $built_in[ $key ] ) ) {
			return (int) $built_in[ $key ]['market_digit'];
		}

		$saved = self::get_saved();
		if ( isset( $saved[ $key ]['market_digit'] ) ) {
			return (int) $saved[ $key ]['market_digit'];
		}

		return null;
	}

	/**
	 * @param mixed $digit Dígito EAN.
	 * @return string
	 */
	public static function market_key_from_digit( $digit ) {
		$digit = self::normalize_market_digit( $digit );
		if ( '' === $digit ) {
			return '';
		}

		foreach ( self::get_built_in() as $key => $entry ) {
			if ( (string) $entry['market_digit'] === $digit ) {
				return (string) $key;
			}
		}

		foreach ( self::get_saved() as $key => $entry ) {
			if ( (string) $entry['market_digit'] === $digit ) {
				return (string) $key;
			}
		}

		return '';
	}

	/**
	 * @param mixed $value Clave, etiqueta o dígito.
	 * @return string
	 */
	public static function normalize_market( $value ) {
		$key = self::normalize_key( $value );
		if ( '' !== $key && isset( self::get_option_labels()[ $key ] ) ) {
			return $key;
		}

		$text = remove_accents( strtolower( trim( (string) $value ) ) );
		if ( '' === $text || 'estandar' === $text || 'estándar' === $text ) {
			return 'NAC';
		}

		foreach ( self::get_option_labels() as $option_key => $label ) {
			if ( 0 === strcasecmp( (string) $label, $text ) ) {
				return (string) $option_key;
			}
		}

		if ( false !== strpos( $text, 'export' ) || false !== strpos( $text, 'bio' ) ) {
			return 'EXP';
		}

		if ( false !== strpos( $text, 'nacional' ) || 'nac' === $text ) {
			return 'NAC';
		}

		$from_digit = self::market_key_from_digit( $text );
		if ( '' !== $from_digit ) {
			return $from_digit;
		}

		return '';
	}

	/**
	 * @param string $key Clave del mercado.
	 * @return bool
	 */
	public static function is_valid_key( $key ) {
		$key = self::normalize_key( $key );

		return '' !== $key && isset( self::get_option_labels()[ $key ] ) && null !== self::market_digit_for( $key );
	}

	/**
	 * @param mixed  $key          Clave del mercado (solo nuevas).
	 * @param mixed  $label        Etiqueta visible.
	 * @param mixed  $market_digit Dígito EAN (opcional).
	 * @return array{key:string,label:string,market_digit:string}|\WP_Error
	 */
	public static function add( $key, $label, $market_digit = '' ) {
		$key   = self::normalize_key( $key );
		$label = Logistica_Label_Etiqueta_Tipos::normalize_display_label( $label );

		if ( '' === $key || '' === $label ) {
			return new WP_Error( 'empty_mercado', __( 'Indica la clave y la etiqueta del mercado.', 'logistica' ) );
		}

		$built_in = self::get_built_in();
		if ( isset( $built_in[ $key ] ) ) {
			if ( ! self::is_hidden( $key ) ) {
				return new WP_Error( 'reserved_mercado', __( 'Esa clave de mercado ya está reservada.', 'logistica' ) );
			}

			self::unhide_key( $key );

			return self::update( $key, $label, $built_in[ $key ]['market_digit'] );
		}

		if ( isset( self::get_option_labels()[ $key ] ) ) {
			return new WP_Error( 'duplicate_mercado', __( 'Ese mercado ya existe.', 'logistica' ) );
		}

		self::unhide_key( $key );

		$market_digit = self::normalize_market_digit( $market_digit );
		if ( '' === $market_digit ) {
			$market_digit = self::next_market_digit();
		}

		if ( null === $market_digit || '' === $market_digit ) {
			return new WP_Error( 'no_market_digit', __( 'No hay dígitos EAN de mercado disponibles.', 'logistica' ) );
		}

		$used = self::used_market_digits();
		if ( ! empty( $used[ $market_digit ] ) ) {
			return new WP_Error( 'duplicate_market_digit', __( 'Ese dígito EAN de mercado ya está en uso.', 'logistica' ) );
		}

		$saved         = self::get_saved();
		$saved[ $key ] = array(
			'label'        => $label,
			'market_digit' => $market_digit,
		);
		update_option( self::OPTION_KEY, $saved, false );

		return array(
			'key'          => $key,
			'label'        => $label,
			'market_digit' => $market_digit,
		);
	}

	/**
	 * @param string $key          Clave del mercado.
	 * @param mixed  $label        Etiqueta visible.
	 * @param mixed  $market_digit Dígito EAN (solo personalizados).
	 * @return array{key:string,label:string,market_digit:string}|\WP_Error
	 */
	public static function update( $key, $label, $market_digit = null ) {
		$key   = self::normalize_key( $key );
		$label = Logistica_Label_Etiqueta_Tipos::normalize_display_label( $label );

		if ( '' === $key || '' === $label ) {
			return new WP_Error( 'invalid_mercado', __( 'Indica la etiqueta del mercado.', 'logistica' ) );
		}

		$built_in = self::get_built_in();
		$saved    = self::get_saved();
		$is_built_in = isset( $built_in[ $key ] );

		if ( $is_built_in ) {
			$market_digit = (string) $built_in[ $key ]['market_digit'];
		} else {
			if ( ! isset( $saved[ $key ] ) ) {
				$result = self::add( $key, $label, $market_digit );
				return $result;
			}

			$market_digit = (string) $saved[ $key ]['market_digit'];
		}

		$saved[ $key ] = array(
			'label'        => $label,
			'market_digit' => (string) $market_digit,
		);
		update_option( self::OPTION_KEY, $saved, false );

		self::unhide_key( $key );

		return array(
			'key'          => $key,
			'label'        => $label,
			'market_digit' => (string) $market_digit,
		);
	}

	/**
	 * @param string $key Clave del mercado personalizado.
	 * @return true|\WP_Error
	 */
	public static function remove( $key ) {
		$key = self::normalize_key( $key );

		if ( '' === $key ) {
			return new WP_Error( 'invalid_mercado', __( 'Indica el mercado.', 'logistica' ) );
		}

		if ( self::is_hidden( $key ) ) {
			return new WP_Error( 'already_hidden', __( 'El mercado ya está oculto.', 'logistica' ) );
		}

		$label_snapshot = (string) ( self::get_option_labels()[ $key ] ?? '' );
		$saved          = self::get_saved();
		$saved_entry    = isset( $saved[ $key ] ) ? $saved[ $key ] : null;

		if ( isset( $saved[ $key ] ) ) {
			unset( $saved[ $key ] );
			update_option( self::OPTION_KEY, $saved, false );
		}

		self::hide_key( $key, $label_snapshot );

		Logistica_Label_Option_Lifecycle::record_remove(
			'mercado',
			$key,
			$label_snapshot ?: $key,
			array(
				'key'    => $key,
				'label'  => $label_snapshot,
				'hidden' => array( 'label' => $label_snapshot ),
				'saved'  => $saved_entry,
			)
		);

		return true;
	}

	/**
	 * @param array<string, mixed> $payload Snapshot papelera.
	 * @return true|\WP_Error
	 */
	public static function apply_restore_from_payload( $payload ) {
		$key = self::normalize_key( (string) ( $payload['key'] ?? '' ) );
		if ( '' === $key ) {
			return new WP_Error( 'invalid_mercado', __( 'No hay clave de mercado para restaurar.', 'logistica' ) );
		}

		if ( ! empty( $payload['saved'] ) && is_array( $payload['saved'] ) ) {
			$saved = self::get_saved();
			$entry = $payload['saved'];
			$label = trim( sanitize_text_field( (string) ( $entry['label'] ?? '' ) ) );
			$digit = self::normalize_market_digit( $entry['market_digit'] ?? '' );
			if ( '' !== $label && '' !== $digit ) {
				$saved[ $key ] = array(
					'label'        => $label,
					'market_digit' => $digit,
				);
				update_option( self::OPTION_KEY, $saved, false );
			}
		}

		self::unhide_key( $key );

		return true;
	}

	/**
	 * @param string $key Clave del mercado oculto.
	 * @return true|\WP_Error
	 */
	public static function restore( $key ) {
		$key = self::normalize_key( $key );

		if ( '' === $key || ! self::is_hidden( $key ) ) {
			return new WP_Error( 'not_hidden', __( 'El mercado no está en la lista de ocultos.', 'logistica' ) );
		}

		self::unhide_key( $key );

		Logistica_Label_Option_Lifecycle::record_restore( 'mercado', $key, (string) ( self::get_option_labels()[ $key ] ?? $key ) );

		return true;
	}

	/**
	 * @return list<array{kind:string,key:string,label:string,meta:string,usage:int}>
	 */
	public static function get_hidden_admin_entries() {
		$entries  = array();
		$built_in = self::get_built_in();
		$hidden   = self::get_hidden();

		foreach ( $hidden as $key => $meta ) {
			$label = trim( (string) ( $meta['label'] ?? '' ) );
			if ( '' === $label ) {
				$label = isset( $built_in[ $key ] ) ? (string) $built_in[ $key ]['label'] : (string) $key;
			}

			$digit = self::market_digit_for( $key );

			$entries[] = array(
				'kind'  => 'mercado',
				'key'   => (string) $key,
				'label' => $label,
				'meta'  => null === $digit ? '' : (string) $digit,
				'usage' => Logistica_Label_Etiqueta_Option_Usage::count_mercado( $key ),
			);
		}

		usort(
			$entries,
			static function ( $a, $b ) {
				return strnatcasecmp( (string) $a['label'], (string) $b['label'] );
			}
		);

		return $entries;
	}

	/**
	 * Filas para hoja de referencia Excel.
	 *
	 * @return array<int, array{0:string,1:string}>
	 */
	public static function excel_market_lookup_rows() {
		$rows = array();
		$seen = array();

		foreach ( self::get_option_labels() as $key => $label ) {
			$digit = self::market_digit_for( $key );
			if ( null === $digit ) {
				continue;
			}

			foreach ( array( (string) $label, (string) $key, (string) $digit ) as $lookup ) {
				$lookup = trim( $lookup );
				if ( '' === $lookup ) {
					continue;
				}

				$seen_key = strtolower( $lookup );
				if ( isset( $seen[ $seen_key ] ) ) {
					continue;
				}

				$seen[ $seen_key ] = true;
				$rows[]            = array( $lookup, (string) $key );
			}
		}

		usort(
			$rows,
			static function ( $a, $b ) {
				return strnatcasecmp( (string) $a[0], (string) $b[0] );
			}
		);

		return $rows;
	}
}
