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

declare(strict_types=1);

namespace BookiFlex\Core\Api\Request\Offer;

use BookiFlex\Core\Api\Request\AbstractRequest;
use BookiFlex\Core\Model\DateUnit\RestrictionRulesConfigurator;
use BookiFlex\Core\Provider\LocalTimeZone;
use BookiFlex\Core\Service\OfferFinder\DTO\OfferRequest;
use DateTimeImmutable;
use WP_REST_Request;

defined( 'ABSPATH' ) || exit;

class SearchOffersRequest extends AbstractRequest
{
    private const DEFAULT_ADULTS = 2;

    private readonly ?DateTimeImmutable $checkInDate;
    private readonly ?DateTimeImmutable $checkOutDate;

    public function __construct(
        DateTimeImmutable|string|null $checkInDate,
        DateTimeImmutable|string|null $checkOutDate,
        private readonly ?int $adults,
        private readonly array $children
    ) {
        $this->checkInDate = match (true) {
            $checkInDate instanceof DateTimeImmutable => $checkInDate,
            is_string($checkInDate)                  => DateTimeImmutable::createFromFormat('Y-m-d', $checkInDate, LocalTimeZone::get()),
            default                                  => null,
        };

        $this->checkOutDate = match (true) {
            $checkOutDate instanceof DateTimeImmutable => $checkOutDate,
            is_string($checkOutDate)                  => DateTimeImmutable::createFromFormat('Y-m-d', $checkOutDate, LocalTimeZone::get()),
            default                                   => null,
        };
    }

    /**
     * Returns true only when both checkInDate and checkOutDate were explicitly provided.
     */
    public function hasExplicitDates(): bool
    {
        return $this->checkInDate !== null && $this->checkOutDate !== null;
    }

    /**
     * Create request from WP REST Request
     */
    public static function fromWpRestRequest(WP_REST_Request $request): self
    {
        $params = $request->get_params();

        $checkIn  = isset($params['checkInDate']) ? $params['checkInDate'] : null;
        $checkOut = isset($params['checkOutDate']) ? $params['checkOutDate'] : null;

        // Treat partial date input (only one of the two) as "no dates provided"
        if ($checkIn === null || $checkOut === null) {
            $checkIn  = null;
            $checkOut = null;
        }

        return new self(
            checkInDate: $checkIn,
            checkOutDate: $checkOut,
            adults: isset($params['adults']) ? (int) $params['adults'] : self::DEFAULT_ADULTS,
            children: self::parseChildrenAges($params['childrenAges'] ?? null)
        );
    }

    /**
     * Parse the childrenAges param into an array of integer ages.
     *
     * Accepts a comma-separated list ("5,8"). Tolerates an already-array value
     * and treats null/empty input as "no children".
     *
     * @param mixed $value
     * @return int[]
     */
    private static function parseChildrenAges($value): array
    {
        if (is_array($value)) {
            return array_map('intval', $value);
        }

        if (!is_string($value) || $value === '') {
            return [];
        }

        return array_map('intval', array_filter(explode(',', $value), static fn($age) => $age !== ''));
    }

    /**
     * Convert to domain DTO
     */
    public function toDomainRequest(DateTimeImmutable $checkInDate, DateTimeImmutable $checkOutDate): OfferRequest
    {
        $restrictionConfig = new RestrictionRulesConfigurator(
            false,
            false,
            false,
            false,
            false,
            false,
            false,
            false,
            false
        );

        return new OfferRequest([
            'checkInDate'  => $checkInDate->format('Y-m-d'),
            'checkOutDate' => $checkOutDate->format('Y-m-d'),
            'adults'       => $this->adults,
            'children'     => $this->children,
        ], $restrictionConfig);
    }

    /**
     * Get validation rules for WP REST API
     */
    public static function getValidationRules(): array
    {
        return [
            'checkInDate' => [
                'required' => false,
                'validate_callback' => function ($param) {
                    return strtotime($param) !== false;
                },
                'sanitize_callback' => function ($value) {
                    return (new DateTimeImmutable($value))->format('Y-m-d');
                },
            ],
            'checkOutDate' => array_merge(
                self::maxNightsRule('checkInDate', 90, required: false),
                ['sanitize_callback' => fn($value) => (new DateTimeImmutable($value))->format('Y-m-d')]
            ),
            'adults' => [
                'required' => false,
                'type' => 'integer',
                'minimum' => 1,
                'maximum' => 20,
                'sanitize_callback' => fn($value) => $value !== null ? (int) $value : null,
                'validate_callback' => 'rest_validate_request_arg',
            ],
            'childrenAges' => [
                'required' => false,
                'type' => 'string',
                'default' => '',
                'sanitize_callback' => fn($value) => is_string($value) ? preg_replace('/[^0-9,]/', '', $value) : '',
            ],
        ];
    }

    public function getCheckInDate(): ?DateTimeImmutable
    {
        return $this->checkInDate;
    }

    public function getCheckOutDate(): ?DateTimeImmutable
    {
        return $this->checkOutDate;
    }

    public function getAdults(): ?int
    {
        return $this->adults;
    }

    public function getChildren(): array
    {
        return $this->children;
    }
}
