<?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\Controller;

use BookiFlex\Core\Api\Request\Offer\SearchOffersRequest;
use BookiFlex\Core\Api\Transformer\Offer\OffersTransformer;
use BookiFlex\Core\Service\ConfigurationService;
use BookiFlex\Core\Service\OfferFinder\OfferFinder;
use DateTimeImmutable;
use Throwable;
use WP_Error;
use WP_REST_Request;
use WP_REST_Response;

defined( 'ABSPATH' ) || exit;

class OfferController extends AbstractController
{
    public const API_ENDPOINT_NAME = 'offers';

    public function __construct(
        private readonly OfferFinder $stayFinderService,
        private readonly ConfigurationService $config,
        private readonly OffersTransformer $transformer,
        string $namespace = 'bflex/v1'
    ) {
        parent::__construct($namespace);
    }

    /**
     * Register API routes
     */
    public function registerRoutes(): void
    {
        // Public endpoint: used by frontend booking widgets to fetch available offers (no authentication required)
        register_rest_route($this->namespace, '/' . self::API_ENDPOINT_NAME, [
            [
                'methods' => 'GET',
                'callback' => [$this, 'searchOffers'],
                'permission_callback' => '__return_true',
                'args' => SearchOffersRequest::getValidationRules(),
            ],
        ]);
    }

    /**
     * GET /offers
     * Searches for available offers. When checkInDate/checkOutDate are omitted,
     * returns an empty result.
     */
    public function searchOffers(WP_REST_Request $request): WP_REST_Response|WP_Error
    {
        return $this->withLocale($this->config->getWidgetLanguage(), function() use ($request) {
            try {
                $searchRequest = SearchOffersRequest::fromWpRestRequest($request);

                if (!$searchRequest->hasExplicitDates()) {
                    return $this->emptyOffersResponse();
                }

                $checkInDate  = $searchRequest->getCheckInDate();
                $checkOutDate = $searchRequest->getCheckOutDate();

                return $this->buildOffersResponse($searchRequest, $checkInDate, $checkOutDate);
            } catch (Throwable $e) {
                return $this->handleError(
                    $e,
                    'availability_search_unit_failed',
                    __('Failed to search available units.', 'bookiflex')
                );
            }
        });
    }

    /**
     * Build the full offers response for a resolved period.
     */
    private function buildOffersResponse(
        SearchOffersRequest $searchRequest,
        DateTimeImmutable $checkInDate,
        DateTimeImmutable $checkOutDate
    ): WP_REST_Response {
        $result = $this->stayFinderService->find($searchRequest->toDomainRequest($checkInDate, $checkOutDate));

        $checkInDateTime = $this->getCheckInDateTime($checkInDate);

        return $this->success([
            'status' => 'success',
            'result' => [
                'searchResults' => $this->transformer->transform($result, $checkInDateTime),
                'searchParams'  => [
                    'checkInDate'  => $checkInDate->format('Y-m-d'),
                    'checkOutDate' => $checkOutDate->format('Y-m-d'),
                    'adults'       => $searchRequest->getAdults(),
                    'children'     => $searchRequest->getChildren(),
                ],
                'meta' => [],
            ],
        ]);
    }

    /**
     * Return an empty offers response when no period could be determined.
     */
    private function emptyOffersResponse(): WP_REST_Response
    {
        return $this->success([
            'status' => 'success',
            'result' => [
                'searchResults' => [],
                'searchParams'  => null,
                'meta'          => [],
            ],
        ]);
    }

    /**
     * Get check-in DateTime with configured time
     */
    private function getCheckInDateTime(DateTimeImmutable $checkInDate): DateTimeImmutable
    {
        [$hours, $min] = explode(':', $this->config->getCheckInTime());
        return $checkInDate->setTime((int) $hours, (int) $min);
    }
}
