<?php

namespace Bookaro\Controller\Rest;


defined( 'ABSPATH' ) || exit;

use Bookaro\Controller\AbstractController;
use WP_REST_Request;
use WP_Error;

abstract class BaseController extends AbstractController
{
    protected string $namespace = 'bookaro/v1';

    public function register_hooks(): void
    {
        add_action('rest_api_init', [ $this, 'register_routes' ]);
    }

    abstract public function register_routes(): void;

    protected function require_authenticated( WP_REST_Request $request, string $message ): bool|WP_Error
    {
        if ( ! is_user_logged_in() ) {
            return new WP_Error(
                'bookaro_auth_required',
                $message,
                [ 'status' => 401 ]
            );
        }

        $auth_header = $request->get_header( 'authorization' );
        if ( is_string( $auth_header ) && '' !== trim( $auth_header ) ) {
            return true;
        }

        $nonce_check = rest_cookie_check_errors( null );
        if ( is_wp_error( $nonce_check ) ) {
            return $nonce_check;
        }

        return true;
    }

    protected function require_authenticated_write( WP_REST_Request $request, string $message ): bool|WP_Error
    {
        if ( ! is_user_logged_in() ) {
            return new WP_Error(
                'bookaro_auth_required',
                $message,
                [ 'status' => 401 ]
            );
        }

        $auth_header = $request->get_header( 'authorization' );
        if ( is_string( $auth_header ) && '' !== trim( $auth_header ) ) {
            return true;
        }

        $nonce = $request->get_header( 'X-WP-Nonce' );
        if ( ! is_string( $nonce ) || $nonce === '' || ! wp_verify_nonce( $nonce, 'wp_rest' ) ) {
            return new WP_Error(
                'bookaro_invalid_nonce',
                __( 'Invalid REST nonce.', 'mybookaro' ),
                [ 'status' => 403 ]
            );
        }

        return true;
    }

    /**
     * Bug 31: shared cap for date-range REST endpoints.
     *
     * AvailabilityController and ProviderBookingsRangeController both accept
     * `from` / `to` params and iterate day-by-day over the range. Without a
     * cap a logged-in caller can ask for a hundred-year span and amplify CPU
     * cost by 4 orders of magnitude over the typical month-or-two query the
     * booking UI actually issues.
     *
     * 370 days (slightly more than a year) matches the existing
     * ScheduleService::DEFAULT_RANGE_MAX_DAYS used by the exceptions
     * range/copy endpoints, so all date-range REST endpoints share one limit.
     * Sites that need wider windows can extend via the
     * bookaro_range_max_days filter (same hook name across endpoints).
     */
    protected function require_range_within_limit( ?string $from, ?string $to ): bool|WP_Error
    {
        $from = is_string( $from ) ? trim( $from ) : '';
        $to   = is_string( $to ) ? trim( $to ) : '';
        if ( $from === '' || $to === '' ) {
            return true;
        }

        try {
            $tz       = function_exists( 'wp_timezone' ) ? wp_timezone() : new \DateTimeZone( 'UTC' );
            $start_dt = new \DateTime( $from, $tz );
            $end_dt   = new \DateTime( $to, $tz );
        } catch ( \Exception $e ) {
            return true; // bad date format will be caught by the existing regex validator
        }

        if ( $end_dt < $start_dt ) {
            return new WP_Error(
                'bookaro_invalid_range',
                __( 'Invalid date range.', 'mybookaro' ),
                [ 'status' => 400 ]
            );
        }

        $days  = (int) $start_dt->diff( $end_dt )->days + 1;
        $limit = (int) apply_filters( 'bookaro_range_max_days', 370 );
        if ( $limit > 0 && $days > $limit ) {
            return new WP_Error(
                'bookaro_range_too_large',
                sprintf(
                    /* translators: %d = max days */
                    __( 'Date range too large. Maximum %d days.', 'mybookaro' ),
                    $limit
                ),
                [ 'status' => 400 ]
            );
        }

        return true;
    }
}
