<?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\Pricing\CalculatePriceRequest;
use BookiFlex\Core\Api\Transformer\StayCostResultTransformer;
use BookiFlex\Core\Service\OfferFinder\OfferFinder;
use Throwable;
use WP_Error;
use WP_REST_Request;
use WP_REST_Response;

defined( 'ABSPATH' ) || exit;

class PricingController extends AbstractController
{
    public const API_ENDPOINT_NAME = 'pricing';

    public function __construct(
        private readonly OfferFinder $stayFinder,
        string $namespace = 'bflex/v1'
    ) {
        parent::__construct($namespace);
    }

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

    /**
     * POST /pricing/calculate
     * Calculates the price based on provided parameters
     */
    public function calculatePrice(WP_REST_Request $request): WP_REST_Response|WP_Error
    {
        try {
            $calculateRequest = CalculatePriceRequest::fromWpRestRequest($request);

            $result = $this->stayFinder->find($calculateRequest->toDomainRequest());

            // OfferFinder also returns unavailable placeholders (no rate plan / no
            // stay cost) for accommodation types that pass capacity but have no
            // priced offer. This endpoint only cares about an actual priced result.
            $result = array_values(array_filter(
                $result,
                fn ($item) => $item->getRatePlan() !== null
                    && $item->getStayCost() !== null
                    && !$item->getStayCost()->hasError()
            ));

            if (empty($result)) {
                $adults   = $calculateRequest->getAdults();
                $children = count($calculateRequest->getChildren());

                $adultsText = sprintf(
                    /* translators: %d: number of adults */
                    _n('%d adult', '%d adults', $adults, 'bookiflex'),
                    $adults
                );
                $childrenText = sprintf(
                    /* translators: %d: number of children */
                    _n('%d child', '%d children', $children, 'bookiflex'),
                    $children
                );

                $message = sprintf(
                    /* translators: %1$s: adults phrase (e.g. "2 adults"), %2$s: children phrase (e.g. "1 child") */
                    __('Booking is not available for %1$s and %2$s.', 'bookiflex'),
                    $adultsText,
                    $childrenText
                );

                return $this->success([
                    'status' => 'success',
                    'result' => [
                        'prices'  => null,
                        'reason'  => 'occupancy_not_available',
                        'message' => $message,
                    ],
                ]);
            }

            return $this->success([
                'status' => 'success',
                'result' => [
                    'prices' => StayCostResultTransformer::transform($result[0])
                ]
            ]);
        } catch (Throwable $e) {
            return $this->handleError(
                $e,
                'pricing_calculate_price_failed',
                __('Failed to calculate the prices.', 'bookiflex')
            );
        }
    }
}
