<?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\Reservation\ChangeStatusRequest;
use BookiFlex\Core\Api\Request\Reservation\GetReservationRequest;
use BookiFlex\Core\Api\Request\Reservation\ResolvePenaltyRequest;
use BookiFlex\Core\Api\Transformer\ReservationGroupTransformer;
use BookiFlex\Core\Contract\RoleCapabilitiesInterface;
use BookiFlex\Core\Service\Reservation\ReservationDetails;
use BookiFlex\Core\Service\Reservation\ReservationService;
use BookiFlex\Core\Service\Reservation\ReservationStatusManager;
use Throwable;
use WP_Error;
use WP_REST_Request;
use WP_REST_Response;

defined( 'ABSPATH' ) || exit;

class ReservationController extends AbstractController
{
    public const API_ENDPOINT_NAME = 'reservation';

    private ReservationGroupTransformer $reservationGroupTransformer;

    public function __construct(
        private readonly ReservationDetails $reservationDetails,
        private readonly ReservationStatusManager $statusManager,
        private readonly ReservationService $reservationService,
        string $namespace = 'bflex/v1'
    ) {
        parent::__construct($namespace);
        $this->reservationGroupTransformer = new ReservationGroupTransformer();
    }

    /**
     * Register API routes
     */
    public function registerRoutes(): void
    {
        register_rest_route($this->namespace, '/' . self::API_ENDPOINT_NAME . '/(?P<id>\d+)', [
            [
                'methods' => 'GET',
                'callback' => [$this, 'getReservation'],
                'permission_callback' => [$this, 'hasAccess'],
                'args' => GetReservationRequest::getValidationRules(),
            ],
        ]);

        register_rest_route($this->namespace, '/' . self::API_ENDPOINT_NAME . '/penalty/resolve', [
            [
                'methods' => 'POST',
                'callback' => [$this, 'resolvePenalty'],
                'permission_callback' => [$this, 'hasAccess'],
                'args' => ResolvePenaltyRequest::getValidationRules(),
            ],
        ]);

        register_rest_route($this->namespace, '/' . self::API_ENDPOINT_NAME . '/status/update', [
            [
                'methods' => 'POST',
                'callback' => [$this, 'changeStatus'],
                'permission_callback' => [$this, 'hasAccess'],
                'args' => ChangeStatusRequest::getValidationRules(),
            ],
        ]);
    }

    /**
     * GET /reservation/{id}
     * Return reservation details
     */
    public function getReservation(WP_REST_Request $request): WP_REST_Response|WP_Error
    {
        try {
            $getRequest = GetReservationRequest::fromWpRestRequest($request);

            $reservations = $this->reservationDetails->getReservationGroup($getRequest->getId());

            if (!$reservations) {
                return $this->error(
                    'reservation_not_found',
                    __('Reservation not found.', 'bookiflex'),
                    404
                );
            }

            return $this->success([
                'status' => 'success',
                'result' => $this->reservationGroupTransformer->transform(
                    $reservations,
                    $this->reservationDetails->getRatePlanNames(),
                    $this->reservationDetails->getAccommodationTypes(),
                    $this->reservationDetails->getPaymentAllocations($reservations),
                )
            ]);
        } catch (Throwable $e) {
            return $this->handleError(
                $e,
                'reservation_load_failed',
                __('Failed to load reservation.', 'bookiflex')
            );
        }
    }

    /**
     * POST /reservation/{id}/penalty/resolve
     * Resolve penalty for reservation
     */
    public function resolvePenalty(WP_REST_Request $request): WP_REST_Response|WP_Error
    {
        try {
            $penaltyRequest = ResolvePenaltyRequest::fromWpRestRequest($request);

            $this->reservationService->resolvePenalty(
                $penaltyRequest->getId(),
                $penaltyRequest->getComment()
            );

            return $this->success([
                'status' => 'success',
                'result' => 'ok'
            ]);
        } catch (Throwable $e) {
            return $this->handleError(
                $e,
                'reservation_penalty_resolving_failed',
                __('Failed to resolve penalty of reservation.', 'bookiflex')
            );
        }
    }

    /**
     * PATCH /reservation/status
     * Changes the status of a reservation
     */
    public function changeStatus(WP_REST_Request $request): WP_REST_Response|WP_Error
    {
        try {
            $statusRequest = ChangeStatusRequest::fromWpRestRequest($request);

            $this->statusManager->changeStatus(
                $statusRequest->getId(),
                $statusRequest->getStatus(),
                $statusRequest->getReason()
            );

            return $this->success([
                'status' => 'success',
                'result' => 'ok'
            ]);
        } catch (Throwable $e) {
            return $this->handleError(
                $e,
                'reservation_change_status_failed',
                __('Failed to change reservation status.', 'bookiflex')
            );
        }
    }

    /**
     * Check if current user has access to the endpoint
     */
    public function hasAccess(): bool|WP_Error
    {
        if (!current_user_can(RoleCapabilitiesInterface::MANAGE_RESERVATIONS)) {
            return $this->error(
                'bookiflex_forbidden',
                __('You are not allowed to manage reservations.', 'bookiflex'),
                401
            );
        }

        return true;
    }
}
