<?php

namespace SugarCalendarBookings\Migration\Sources\Amelia\Mappers;

use SugarCalendarBookings\Migration\AbstractMapper;
use SugarCalendarBookings\Migration\ImageMigrationHandler;
use SugarCalendarBookings\Support\Helpers\Color;

/**
 * Service Mapper.
 *
 * Maps Amelia service data to SCB format.
 *
 * @since 1.3.0
 */
class ServiceMapper extends AbstractMapper {

	/**
	 * Image handler.
	 *
	 * @since 1.3.0
	 *
	 * @var ImageMigrationHandler
	 */
	private $image_handler;

	/**
	 * Constructor.
	 *
	 * @since 1.3.0
	 *
	 * @param ImageMigrationHandler $image_handler Image migration handler.
	 */
	public function __construct( ImageMigrationHandler $image_handler ) {

		$this->image_handler = $image_handler;
	}

	/**
	 * Map Amelia service to SCB format.
	 *
	 * @since 1.3.0
	 *
	 * @param object               $raw     Raw Amelia data.
	 * @param array<string, mixed> $context Context.
	 *
	 * @return array<string, mixed>
	 */
	public function map( object $raw, array $context = [] ): array {

		$sanitized = $this->extract_and_sanitize(
			$raw,
			[
				'name'         => [
					'source'  => 'name',
					'default' => 'Untitled Service',
					'rule'    => 'string',
				],
				'description'  => [
					'source'  => 'description',
					'default' => '',
					'rule'    => 'string',
				],
				'price'        => [
					'source'  => 'price',
					'default' => 0,
					'rule'    => 'numeric',
				],
				'color'        => [
					'source'  => 'color',
					'default' => '#5B7CB2',
					'rule'    => 'string',
				],
				'status'       => [
					'source'  => 'status',
					'default' => '',
					'rule'    => 'string',
				],
				'duration'     => [
					'source'  => 'duration',
					'default' => 3600,
					'rule'    => 'numeric',
				],
				'id'           => [
					'source'  => 'id',
					'default' => 0,
					'rule'    => 'numeric',
				],
				'picture_path' => [
					'source'  => 'pictureFullPath',
					'default' => '',
					'rule'    => 'string',
				],
			]
		);

		$mapped = [
			'name'        => $sanitized['name'],
			'description' => wp_kses_post( $sanitized['description'] ),
			'duration'    => $this->convert_seconds_to_minutes( (int) $sanitized['duration'] ),
			'price'       => (float) $sanitized['price'],
			'color'       => $this->validate_color( $sanitized['color'] ),
			'status'      => $this->map_entity_status( $sanitized['status'] ),
			'author_id'   => $context['author_id'] ?? 1,
			'created_at'  => current_time( 'mysql' ),
			'updated_at'  => current_time( 'mysql' ),
			// Store original Amelia ID for cross-entity lookup during appointment migration.
			'_amelia_id'  => (int) $sanitized['id'],
		];

		// Handle image.
		if ( ! empty( $sanitized['picture_path'] ) ) {
			$mapped['feature_image_id'] = $this->image_handler->migrate_image(
				$sanitized['picture_path'],
				$sanitized['name']
			);
		}

		// Fetch employee relationships (providers_to_services).
		$amelia_employee_ids = $this->get_service_employee_ids( (int) $sanitized['id'] );

		if ( ! empty( $amelia_employee_ids ) ) {
			$mapped['_amelia_employee_ids'] = $amelia_employee_ids;
		}

		// Fetch location relationships (through providers_to_locations).
		$amelia_location_ids = $this->get_service_location_ids( (int) $sanitized['id'] );

		if ( ! empty( $amelia_location_ids ) ) {
			$mapped['_amelia_location_ids'] = $amelia_location_ids;
		}

		return $mapped;
	}

	/**
	 * Get Amelia employee IDs linked to a service.
	 *
	 * @since 1.3.0
	 *
	 * @param int $service_id Amelia service ID.
	 *
	 * @return array<int>
	 */
	private function get_service_employee_ids( int $service_id ): array {

		global $wpdb;

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$results = $wpdb->get_col(
			$wpdb->prepare(
				"SELECT userId FROM {$wpdb->prefix}amelia_providers_to_services WHERE serviceId = %d",
				$service_id
			)
		);

		return array_map( 'intval', $results );
	}

	/**
	 * Get Amelia location IDs linked to a service (through providers).
	 *
	 * @since 1.3.0
	 *
	 * @param int $service_id Amelia service ID.
	 *
	 * @return array<int>
	 */
	private function get_service_location_ids( int $service_id ): array {

		global $wpdb;

		// Get locations through providers linked to this service.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$results = $wpdb->get_col(
			$wpdb->prepare(
				"SELECT DISTINCT pl.locationId
				FROM {$wpdb->prefix}amelia_providers_to_locations pl
				INNER JOIN {$wpdb->prefix}amelia_providers_to_services ps ON pl.userId = ps.userId
				WHERE ps.serviceId = %d",
				$service_id
			)
		);

		return array_map( 'intval', $results );
	}

	/**
	 * Convert seconds to minutes.
	 *
	 * @since 1.3.0
	 *
	 * @param int $seconds Seconds.
	 *
	 * @return int
	 */
	private function convert_seconds_to_minutes( int $seconds ): int {

		return (int) round( $seconds / 60 );
	}

	/**
	 * Map color to the closest palette color.
	 *
	 * Validates the input color and maps it to the most similar color
	 * from the Sugar Calendar palette using perceptual color distance.
	 *
	 * @since 1.3.0
	 *
	 * @param string $color Color value.
	 *
	 * @return string
	 */
	private function validate_color( string $color ): string {

		return Color::map_to_palette(
			$color,
			Color::SERVICE_PALETTE,
			Color::DEFAULT_SERVICE_COLOR
		);
	}
}
