<?php
/**
 * Notion Property Formatter
 *
 * Converts simplified user input into Notion's verbose property format.
 *
 * @package Sequensy\Integrations\Notion
 * @since 1.0.0
 */

declare(strict_types=1);

namespace Sequensy\Integrations\Notion\Helpers;

// If this file is called directly, abort.
if ( ! defined( 'WPINC' ) ) {
	die;
}

/**
 * Class PropertyFormatter
 *
 * Transforms simplified property values into Notion API property objects.
 *
 * Users provide simplified JSON like:
 *   {"Status": "In Progress", "Priority": 3, "Due Date": "2024-01-15"}
 *
 * This class converts each value to the appropriate Notion property format:
 *   - String → rich_text
 *   - Number → number
 *   - Boolean → checkbox
 *   - Date string (YYYY-MM-DD) → date
 *   - {"select": "value"} → select
 *   - {"multi_select": ["a", "b"]} → multi_select
 */
class PropertyFormatter {

	/**
	 * Format a simplified properties object into Notion property format.
	 *
	 * @param array $properties Key-value pairs of property names and simplified values.
	 * @return array Notion-formatted properties.
	 */
	public static function format( array $properties ): array {
		$formatted = array();

		foreach ( $properties as $name => $value ) {
			$formatted[ $name ] = self::format_value( $value );
		}

		return $formatted;
	}

	/**
	 * Format a title property value.
	 *
	 * @param string $text Title text.
	 * @return array Notion title property.
	 */
	public static function format_title( string $text ): array {
		return array(
			'title' => array(
				array(
					'text' => array( 'content' => $text ),
				),
			),
		);
	}

	/**
	 * Format a single property value based on its type.
	 *
	 * @param mixed $value The simplified value.
	 * @return array Notion property format.
	 */
	private static function format_value( mixed $value ): array {
		// Explicit type objects (select, multi_select).
		if ( is_array( $value ) ) {
			return self::format_explicit_type( $value );
		}

		if ( is_bool( $value ) ) {
			return array( 'checkbox' => $value );
		}

		if ( is_int( $value ) || is_float( $value ) ) {
			return array( 'number' => $value );
		}

		if ( is_string( $value ) ) {
			// Check for date pattern (YYYY-MM-DD).
			if ( preg_match( '/^\d{4}-\d{2}-\d{2}$/', $value ) ) {
				return array(
					'date' => array( 'start' => $value ),
				);
			}

			// Default to rich_text.
			return array(
				'rich_text' => array(
					array(
						'text' => array( 'content' => $value ),
					),
				),
			);
		}

		// Null or unsupported types — return empty rich_text.
		return array(
			'rich_text' => array(),
		);
	}

	/**
	 * Format an explicit type object (select, multi_select, etc.).
	 *
	 * @param array $value Array with a single key indicating the type.
	 * @return array Notion property format.
	 */
	private static function format_explicit_type( array $value ): array {
		if ( isset( $value['select'] ) ) {
			return array(
				'select' => array( 'name' => (string) $value['select'] ),
			);
		}

		if ( isset( $value['multi_select'] ) && is_array( $value['multi_select'] ) ) {
			return array(
				'multi_select' => array_map(
					fn( string $name ) => array( 'name' => $name ),
					$value['multi_select']
				),
			);
		}

		if ( isset( $value['date'] ) ) {
			return array(
				'date' => array( 'start' => (string) $value['date'] ),
			);
		}

		if ( isset( $value['url'] ) ) {
			return array(
				'url' => (string) $value['url'],
			);
		}

		if ( isset( $value['email'] ) ) {
			return array(
				'email' => (string) $value['email'],
			);
		}

		if ( isset( $value['phone_number'] ) ) {
			return array(
				'phone_number' => (string) $value['phone_number'],
			);
		}

		if ( isset( $value['number'] ) ) {
			return array(
				'number' => (float) $value['number'],
			);
		}

		if ( isset( $value['checkbox'] ) ) {
			return array(
				'checkbox' => (bool) $value['checkbox'],
			);
		}

		// Unknown explicit type — pass through as-is.
		return $value;
	}

	/**
	 * Format a single value using an explicit Notion property type.
	 *
	 * Used when the property type is known from the database schema, so we can
	 * convert a plain string (from key_value field) to the correct API format.
	 *
	 * @param string $value User-entered string value.
	 * @param string $type  Notion property type from database schema.
	 * @return array|null Notion property format, or null if value is empty and type doesn't support empty.
	 */
	public static function format_typed_value( string $value, string $type ): ?array {
		return match ( $type ) {
			'number'       => self::format_typed_number( $value ),
			'checkbox'     => array( 'checkbox' => in_array( strtolower( $value ), array( 'true', '1', 'yes' ), true ) ),
			'select'       => array( 'select' => array( 'name' => $value ) ),
			'status'       => array( 'status' => array( 'name' => $value ) ),
			'multi_select' => array(
				'multi_select' => array_map(
					fn( string $name ) => array( 'name' => trim( $name ) ),
					explode( ',', $value )
				),
			),
			'date'         => array( 'date' => array( 'start' => $value ) ),
			'url'          => array( 'url' => $value ),
			'email'        => array( 'email' => $value ),
			'phone_number' => array( 'phone_number' => $value ),
			default        => array(
				'rich_text' => array(
					array(
						'text' => array( 'content' => $value ),
					),
				),
			),
		};
	}

	/**
	 * Format a number value from string, returning null for invalid input.
	 *
	 * @param string $value User-entered string.
	 * @return array|null Notion number property or null.
	 */
	private static function format_typed_number( string $value ): ?array {
		if ( '' === $value || ! is_numeric( $value ) ) {
			return null;
		}

		return array( 'number' => (float) $value );
	}

	/**
	 * Format key_value field data into Notion properties using a type map.
	 *
	 * @param array $properties Key-value pairs from key_value field (Record<string, string>).
	 * @param array $type_map   Map of property name → Notion type from database schema.
	 * @return array Notion-formatted properties.
	 */
	public static function format_typed_properties( array $properties, array $type_map ): array {
		$formatted = array();

		foreach ( $properties as $name => $value ) {
			$type   = $type_map[ $name ] ?? 'rich_text';
			$result = self::format_typed_value( (string) $value, $type );

			if ( null !== $result ) {
				$formatted[ $name ] = $result;
			}
		}

		return $formatted;
	}

	/**
	 * Extract a simple value from a Notion property object.
	 *
	 * Converts verbose Notion property responses into simple PHP values.
	 *
	 * @param array $property Notion property object from API response.
	 * @return mixed Simplified value.
	 */
	public static function extract_value( array $property ): mixed {
		$type = $property['type'] ?? '';

		return match ( $type ) {
			'title'        => implode( '', array_map( fn( $t ) => $t['plain_text'] ?? '', $property['title'] ?? array() ) ),
			'rich_text'    => implode( '', array_map( fn( $t ) => $t['plain_text'] ?? '', $property['rich_text'] ?? array() ) ),
			'number'       => $property['number'] ?? null,
			'checkbox'     => $property['checkbox'] ?? false,
			'select'       => $property['select']['name'] ?? null,
			'multi_select' => array_map( fn( $s ) => $s['name'] ?? '', $property['multi_select'] ?? array() ),
			'date'         => $property['date']['start'] ?? null,
			'url'          => $property['url'] ?? null,
			'email'        => $property['email'] ?? null,
			'phone_number' => $property['phone_number'] ?? null,
			'formula'      => $property['formula'][ $property['formula']['type'] ?? '' ] ?? null,
			'relation'     => array_map( fn( $r ) => $r['id'] ?? '', $property['relation'] ?? array() ),
			'status'       => $property['status']['name'] ?? null,
			default        => null,
		};
	}
}
