<?php
/**
 * BookiFlex Onboarding Extension
 *
 * @package    BookiFlex
 * @subpackage Onboarding
 * @author     BookiFlex
 * @copyright  Copyright (c) 2025 BookiFlex
 * @license    GPL-2.0-or-later
 *
 * @link       https://bookiflex.com
 */

declare(strict_types=1);

namespace BookiFlex\Extensions\Onboarding\Api\Request;

use BookiFlex\Core\Api\Request\AbstractRequest;
use BookiFlex\Extensions\Onboarding\ValueObject\OnboardingConfiguration;
use WP_Error;
use WP_REST_Request;

defined( 'ABSPATH' ) || exit;

/**
 * Onboarding Complete Request
 *
 * Validates and transforms onboarding completion request data.
 * Validates structure per FR-013 requirements using WordPress REST API validation.
 *
 * @package BookiFlex\Extensions\Onboarding\Api\Request
 * @since   1.0.0
 */
class OnboardingCompleteRequest extends AbstractRequest
{
    /**
     * OnboardingCompleteRequest constructor
     *
     * @param string                                $mode                 Onboarding mode
     * @param string|null                           $currency             Currency code (ISO 4217)
     * @param array<int, array<string, mixed>>|null $accommodationTypes   Accommodation configurations
     * @param array<int, int>|null                  $paymentTypeIds       Payment method IDs
     * @param int|null                              $cancellationPolicyId Cancellation policy ID
     * @param string|null                           $companyName          Company legal/trading name
     * @param string|null                           $companyAddress       Company address
     * @param string|null                           $companyPhone         Company phone
     * @param string|null                           $companyEmail         Company email
     */
    public function __construct(
        private readonly string $mode,
        private readonly ?string $currency = null,
        private readonly ?array $accommodationTypes = null,
        private readonly ?array $paymentTypeIds = null,
        private readonly ?int $cancellationPolicyId = null,
        private readonly ?string $companyName = null,
        private readonly ?string $companyAddress = null,
        private readonly ?string $companyPhone = null,
        private readonly ?string $companyEmail = null,
    ) {
    }

    /**
     * Create request from WP REST Request
     *
     * @param  WP_REST_Request $request WordPress REST request
     * @return static
     */
    public static function fromWpRestRequest(WP_REST_Request $request): self
    {
        $params = $request->get_json_params() ?? [];

        return new self(
            mode: self::sanitizeString($params['mode']),
            currency: isset($params['currency']) ? self::sanitizeString($params['currency']) : null,
            accommodationTypes: $params['accommodationTypes'] ?? null,
            paymentTypeIds: isset($params['paymentTypeIds']) ? array_map('intval', $params['paymentTypeIds']) : null,
            cancellationPolicyId: isset($params['cancellationPolicyId']) ? self::sanitizeInt($params['cancellationPolicyId']) : null,
            companyName: isset($params['companyName']) ? self::sanitizeString($params['companyName']) : null,
            companyAddress: isset($params['companyAddress']) ? self::sanitizeString($params['companyAddress']) : null,
            companyPhone: isset($params['companyPhone']) ? self::sanitizeString($params['companyPhone']) : null,
            companyEmail: isset($params['companyEmail']) ? sanitize_email($params['companyEmail']) : null,
        );
    }

    /**
     * Get validation rules for WP REST API
     *
     * @return array<string, mixed> Validation rules
     */
    public static function getValidationRules(): array
    {
        return [
            'mode' => [
                'required' => true,
                'type' => 'string',
                'enum' => ['quick_setup', 'test_data', 'manual'],
                'sanitize_callback' => [static::class, 'sanitizeString'],
            ],
            'currency' => [
                'required' => false,
                'type' => 'string',
                'validate_callback' => [static::class, 'validateCurrency'],
                'sanitize_callback' => [static::class, 'sanitizeString'],
            ],
            'accommodationTypes' => [
                'required' => false,
                'type' => 'array',
                'validate_callback' => [static::class, 'validateAccommodationTypes'],
            ],
            'paymentTypeIds' => [
                'required' => false,
                'type' => 'array',
                'validate_callback' => [static::class, 'validatePaymentTypeIds'],
            ],
            'cancellationPolicyId' => [
                'required' => false,
                'type' => 'integer',
                'validate_callback' => [static::class, 'validateCancellationPolicyId'],
                'sanitize_callback' => [static::class, 'sanitizeInt'],
            ],
            'companyName' => [
                'required' => false,
                'type' => 'string',
                'validate_callback' => [static::class, 'validateCompanyName'],
                'sanitize_callback' => [static::class, 'sanitizeString'],
            ],
            'companyAddress' => [
                'required' => false,
                'type' => 'string',
                'validate_callback' => [static::class, 'validateCompanyAddress'],
                'sanitize_callback' => [static::class, 'sanitizeString'],
            ],
            'companyPhone' => [
                'required' => false,
                'type' => 'string',
                'sanitize_callback' => [static::class, 'sanitizeString'],
            ],
            'companyEmail' => [
                'required' => false,
                'type' => 'string',
                'validate_callback' => [static::class, 'validateCompanyEmail'],
                'sanitize_callback' => 'sanitize_email',
            ],
        ];
    }

    /**
     * Validate currency for quick_setup mode
     *
     * @param  mixed           $value   Currency value
     * @param  WP_REST_Request $request Request object
     * @return bool|WP_Error True if valid, WP_Error otherwise
     */
    public static function validateCurrency($value, WP_REST_Request $request)
    {
        $params = $request->get_json_params() ?? [];
        $mode = $params['mode'] ?? '';

        // Only required for quick_setup
        if ($mode === 'quick_setup') {
            if (empty($value)) {
                return new WP_Error(
                    'rest_invalid_param',
                    'currency is required for quick setup'
                );
            }

            if (!is_string($value) || strlen($value) !== 3) {
                return new WP_Error(
                    'rest_invalid_param',
                    'currency must be a valid 3-letter ISO 4217 code'
                );
            }
        }

        return true;
    }

    /**
     * Validate accommodation types array for quick_setup mode
     *
     * @param  mixed           $value   Accommodation types array
     * @param  WP_REST_Request $request Request object
     * @return bool|WP_Error True if valid, WP_Error otherwise
     */
    public static function validateAccommodationTypes($value, WP_REST_Request $request)
    {
        $params = $request->get_json_params() ?? [];
        $mode = $params['mode'] ?? '';

        // Only required for quick_setup
        if ($mode === 'quick_setup') {
            if (!is_array($value) || empty($value)) {
                return new WP_Error(
                    'rest_invalid_param',
                    'accommodation types array is required for quick setup'
                );
            }

            // Validate each accommodation structure
            foreach ($value as $index => $accommodation) {
                if (!is_array($accommodation)) {
                    return new WP_Error(
                        'rest_invalid_param',
                        sprintf(
                            /* translators: %d: array index */
							'accommodation at index %d must be an array'
							, $index)
                    );
                }

                $error = self::validateSingleAccommodation($accommodation, $index);
                if (is_wp_error($error)) {
                    return $error;
                }
            }
        }

        return true;
    }

    /**
     * Validate single accommodation structure
     *
     * @param  array<string, mixed> $accommodation Accommodation data
     * @param  int                  $index         Array index for error messages
     * @return bool|WP_Error True if valid, WP_Error otherwise
     */
    private static function validateSingleAccommodation(array $accommodation, int $index)
    {
        $required = ['name', 'mainPlaces', 'unitType', 'availableUnits', 'weeklyPricing'];

        foreach ($required as $field) {
            if (!isset($accommodation[$field])) {
                return new WP_Error(
                    'rest_invalid_param',
                    sprintf(
                        /* translators: %1$d: accommodation index, %2$s: field name */
                        'accommodation at index %1$d is missing required field: %2$s',
                        $index,
                        $field
                    )
                );
            }
        }

        // Validate mainPlaces
        if (!is_int($accommodation['mainPlaces']) || $accommodation['mainPlaces'] < 1) {
            return new WP_Error(
                'rest_invalid_param',
                sprintf(
                    /* translators: %d: accommodation index */
                    'accommodation at index %d: mainPlaces must be a positive integer',
                    $index
                )
            );
        }

        // Validate availableUnits
        if (!is_int($accommodation['availableUnits']) || $accommodation['availableUnits'] < 1) {
            return new WP_Error(
                'rest_invalid_param',
                sprintf(
                    /* translators: %d: accommodation index */
                    'accommodation at index %d: availableUnits must be a positive integer',
                    $index
                )
            );
        }

        // Validate weeklyPricing
        $error = self::validateWeeklyPricing($accommodation['weeklyPricing'], $index);
        if (is_wp_error($error)) {
            return $error;
        }

        return true;
    }

    /**
     * Validate weekly pricing structure
     *
     * @param  mixed $pricing Pricing data
     * @param  int   $index   Array index for error messages
     * @return bool|WP_Error True if valid, WP_Error otherwise
     */
    private static function validateWeeklyPricing($pricing, int $index)
    {
        if (!is_array($pricing)) {
            return new WP_Error(
                'rest_invalid_param',
                sprintf(
                    /* translators: %d: accommodation index */
                    'accommodation at index %d: weeklyPricing must be an array',
                    $index
                )
            );
        }

        $days = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'];
        foreach ($days as $day) {
            if (!isset($pricing[$day])) {
                return new WP_Error(
                    'rest_invalid_param',
                    sprintf(
                        /* translators: %1$d: accommodation index, %2$s: day of week */
                        'accommodation at index %1$d: weeklyPricing missing %2$s',
                        $index,
                        $day
                    )
                );
            }

            if (!is_int($pricing[$day]) || $pricing[$day] < 0) {
                return new WP_Error(
                    'rest_invalid_param',
                    sprintf(
                        /* translators: %1$d: accommodation index, %2$s: day of week */
                        'accommodation at index %1$d: weeklyPricing.%2$s must be a non-negative integer',
                        $index,
                        $day
                    )
                );
            }
        }

        return true;
    }

    /**
     * Validate payment method IDs for quick_setup mode
     *
     * @param  mixed           $value   Payment method IDs
     * @param  WP_REST_Request $request Request object
     * @return bool|WP_Error True if valid, WP_Error otherwise
     */
    public static function validatePaymentTypeIds($value, WP_REST_Request $request)
    {
        $params = $request->get_json_params() ?? [];
        $mode = $params['mode'] ?? '';

        // Only required for quick_setup
        if ($mode === 'quick_setup') {
            if (!is_array($value) || empty($value)) {
                return new WP_Error(
                    'rest_invalid_param',
                    'paymentTypeIds array is required for quick setup'
                );
            }

            foreach ($value as $id) {
                if (!is_int($id) || $id <= 0) {
                    return new WP_Error(
                        'rest_invalid_param',
                        'paymentTypeIds must contain positive integers'
                    );
                }
            }
        }

        return true;
    }

    /**
     * Validate cancellation policy ID for quick_setup mode
     *
     * @param  mixed           $value   Cancellation policy ID
     * @param  WP_REST_Request $request Request object
     * @return bool|WP_Error True if valid, WP_Error otherwise
     */
    public static function validateCancellationPolicyId($value, WP_REST_Request $request)
    {
        $params = $request->get_json_params() ?? [];
        $mode = $params['mode'] ?? '';

        // Only required for quick_setup
        if ($mode === 'quick_setup') {
            if (!isset($value)) {
                return new WP_Error(
                    'rest_invalid_param',
                    'cancellationPolicyId is required for quick setup'
                );
            }

            if (!is_int($value) || $value <= 0) {
                return new WP_Error(
                    'rest_invalid_param',
                    'cancellationPolicyId must be a positive integer'
                );
            }
        }

        return true;
    }

    /**
     * Validate company name for quick_setup mode
     *
     * @param  mixed           $value   Company name value
     * @param  WP_REST_Request $request Request object
     * @return bool|WP_Error True if valid, WP_Error otherwise
     */
    public static function validateCompanyName($value, WP_REST_Request $request)
    {
        $params = $request->get_json_params() ?? [];
        $mode = $params['mode'] ?? '';

        if ($mode === 'quick_setup' && empty($value)) {
            return new WP_Error(
                'rest_invalid_param',
                'companyName is required for quick setup'
            );
        }

        return true;
    }

    /**
     * Validate company address for quick_setup mode
     *
     * @param  mixed           $value   Company address value
     * @param  WP_REST_Request $request Request object
     * @return bool|WP_Error True if valid, WP_Error otherwise
     */
    public static function validateCompanyAddress($value, WP_REST_Request $request)
    {
        $params = $request->get_json_params() ?? [];
        $mode = $params['mode'] ?? '';

        if ($mode === 'quick_setup' && empty($value)) {
            return new WP_Error(
                'rest_invalid_param',
                'companyAddress is required for quick setup'
            );
        }

        return true;
    }

    /**
     * Validate company email for quick_setup mode
     *
     * @param  mixed           $value   Company email value
     * @param  WP_REST_Request $request Request object
     * @return bool|WP_Error True if valid, WP_Error otherwise
     */
    public static function validateCompanyEmail($value, WP_REST_Request $request)
    {
        $params = $request->get_json_params() ?? [];
        $mode = $params['mode'] ?? '';

        if ($mode === 'quick_setup') {
            if (empty($value)) {
                return new WP_Error(
                    'rest_invalid_param',
                    'companyEmail is required for quick setup'
                );
            }

            if (!is_email($value)) {
                return new WP_Error(
                    'rest_invalid_param',
                    'companyEmail must be a valid email address'
                );
            }
        }

        return true;
    }

    /**
     * Get mode
     *
     * @return string
     */
    public function getMode(): string
    {
        return $this->mode;
    }

    /**
     * Get currency
     *
     * @return string|null
     */
    public function getCurrency(): ?string
    {
        return $this->currency;
    }

    /**
     * Get accommodation types
     *
     * @return array<int, array<string, mixed>>|null
     */
    public function getAccommodationTypes(): ?array
    {
        return $this->accommodationTypes;
    }

    /**
     * Get payment method IDs
     *
     * @return array<int, int>|null
     */
    public function getPaymentTypeIds(): ?array
    {
        return $this->paymentTypeIds;
    }

    /**
     * Get cancellation policy ID
     *
     * @return int|null
     */
    public function getCancellationPolicyId(): ?int
    {
        return $this->cancellationPolicyId;
    }

    /**
     * Get company name
     *
     * @return string|null
     */
    public function getCompanyName(): ?string
    {
        return $this->companyName;
    }

    /**
     * Get company address
     *
     * @return string|null
     */
    public function getCompanyAddress(): ?string
    {
        return $this->companyAddress;
    }

    /**
     * Get company phone
     *
     * @return string|null
     */
    public function getCompanyPhone(): ?string
    {
        return $this->companyPhone;
    }

    /**
     * Get company email
     *
     * @return string|null
     */
    public function getCompanyEmail(): ?string
    {
        return $this->companyEmail;
    }

    /**
     * Transform validated request data to OnboardingConfiguration
     *
     * @return OnboardingConfiguration
     * @throws \InvalidArgumentException If transformation fails
     */
    public function toConfiguration(): OnboardingConfiguration
    {
        return OnboardingConfiguration::fromArray(
            [
            'mode' => $this->mode,
            'currency' => $this->currency,
            'accommodationTypes' => $this->accommodationTypes,
            'paymentTypeIds' => $this->paymentTypeIds,
            'cancellationPolicyId' => $this->cancellationPolicyId,
            'companyName' => $this->companyName,
            'companyAddress' => $this->companyAddress,
            'companyPhone' => $this->companyPhone,
            'companyEmail' => $this->companyEmail,
            ]
        );
    }
}
