<?php
namespace Bookaro\Controller\Rest;

defined( 'ABSPATH' ) || exit;

use Bookaro\Service\BookingActionService;
use WP_REST_Request;
use WP_REST_Response;
use WP_REST_Server;

class BookingActionsController extends BaseController
{
    private BookingActionService $actions;

    public function __construct( BookingActionService $actions )
    {
        $this->actions = $actions;
    }

    public function register_routes(): void
    {
        register_rest_route( $this->namespace, '/bookings', [
            [
                'methods'             => WP_REST_Server::CREATABLE,
                'callback'            => [ $this, 'create_booking' ],
                'permission_callback' => [ $this, 'can_create_booking' ],
                'args'                => $this->get_create_booking_args(),
            ],
        ] );

        register_rest_route( $this->namespace, '/bookings/provider', [
            [
                'methods'             => WP_REST_Server::READABLE,
                'callback'            => [ $this, 'get_provider_bookings' ],
                'permission_callback' => [ $this, 'can_view_provider_bookings' ],
            ],
        ] );

        register_rest_route( $this->namespace, '/bookings/owner', [
            [
                'methods'             => WP_REST_Server::READABLE,
                'callback'            => [ $this, 'get_owner_bookings' ],
                'permission_callback' => [ $this, 'can_view_owner_bookings' ],
            ],
        ] );

        register_rest_route( $this->namespace, '/bookings/(?P<id>\\d+)/status', [
            [
                'methods'             => WP_REST_Server::CREATABLE,
                'callback'            => [ $this, 'update_status' ],
                'permission_callback' => [ $this, 'can_owner_manage_booking' ],
                'args'                => $this->get_status_args(),
            ],
        ] );

        register_rest_route( $this->namespace, '/bookings/bulk-status', [
            [
                'methods'             => WP_REST_Server::CREATABLE,
                'callback'            => [ $this, 'bulk_update_status' ],
                'permission_callback' => [ $this, 'can_provider_manage_booking' ],
                'args'                => $this->get_bulk_status_args(),
            ],
        ] );

        register_rest_route( $this->namespace, '/bookings/(?P<id>\\d+)/cancel', [
            [
                'methods'             => WP_REST_Server::CREATABLE,
                'callback'            => [ $this, 'cancel_booking' ],
                'permission_callback' => [ $this, 'can_owner_manage_booking' ],
            ],
        ] );

        register_rest_route( $this->namespace, '/bookings/(?P<id>\\d+)/note', [
            [
                'methods'             => WP_REST_Server::CREATABLE,
                'callback'            => [ $this, 'add_note' ],
                'permission_callback' => [ $this, 'can_owner_manage_booking' ],
            ],
        ] );

        register_rest_route( $this->namespace, '/bookings/(?P<id>\\d+)/reschedule', [
            [
                'methods'             => WP_REST_Server::CREATABLE,
                'callback'            => [ $this, 'reschedule_booking' ],
                'permission_callback' => [ $this, 'can_owner_manage_booking' ],
            ],
        ] );

        register_rest_route( $this->namespace, '/bookings/(?P<id>\\d+)/notifications', [
            [
                'methods'             => WP_REST_Server::READABLE,
                'callback'            => [ $this, 'get_notification_logs' ],
                'permission_callback' => [ $this, 'can_view_notification_logs' ],
            ],
        ] );
    }

    private function ensure_error_status( \WP_Error $error ): \WP_Error
    {
        $data = $error->get_error_data();
        if ( ! is_array( $data ) || ! isset( $data['status'] ) ) {
            $error->add_data( [ 'status' => 400 ] );
        }
        return $error;
    }

    public function can_create_booking( WP_REST_Request $request ): bool|\WP_Error
    {
        // Bug 53: the bookaro_settings.require_login toggle is honoured by
        // the frontend modal (no login gate when 0) and by the services
        // permission callback (ServicesController:34) but was IGNORED here.
        // Result: admin flips require_login=0, anonymous visitor fills the
        // booking form, hits Confirm, server returns bookaro_auth_required
        // 401 anyway. Gate require_authenticated_write behind the setting.
        // When require_login=0, anonymous bookings ARE allowed but we still
        // demand a valid wp_rest nonce so we're not a wide-open POST sink
        // for spam (the existing honeypot + min-fill-time + rate-limit
        // checks fire later in the create handler).
        $require_login = (bool) bookaro_get_option( 'require_login', 1 );

        if ( $require_login ) {
            $auth = $this->require_authenticated_write(
                $request,
                __( 'You must be logged in to book a service.', 'mybookaro' )
            );
            if ( is_wp_error( $auth ) ) {
                return $auth;
            }

            if ( ! bookaro_user_has_customer_access() ) {
                return new \WP_Error(
                    'bookaro_forbidden',
                    __( 'Permission denied.', 'mybookaro' ),
                    [ 'status' => 403 ]
                );
            }
        } else {
            // Anonymous bookings permitted — still require a valid nonce so
            // every public POST is at least bound to a real site visit.
            $auth_header = $request->get_header( 'authorization' );
            if ( ! is_string( $auth_header ) || '' === trim( $auth_header ) ) {
                $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;
    }

    public function can_owner_manage_booking( WP_REST_Request $request ): bool|\WP_Error
    {
        return $this->can_manage_booking_for_roles(
            $request,
            [ 'customer', 'provider' ],
            __( 'You are not allowed to manage this booking.', 'mybookaro' )
        );
    }

    public function can_provider_manage_booking( WP_REST_Request $request ): bool|\WP_Error
    {
        $auth = $this->require_authenticated_write(
            $request,
            __( 'You are not allowed to manage bookings.', 'mybookaro' )
        );
        if ( is_wp_error( $auth ) ) {
            return $auth;
        }

        if ( current_user_can( 'manage_options' ) ) {
            return true;
        }

        if ( ! bookaro_user_has_provider_access() ) {
            return new \WP_Error(
                'bookaro_forbidden',
                __( 'You are not allowed to manage bookings.', 'mybookaro' ),
                [ 'status' => 403 ]
            );
        }

        $booking_ids = $this->sanitize_booking_ids( $request->get_param( 'booking_ids' ) );
        if ( empty( $booking_ids ) ) {
            return new \WP_Error(
                'bookaro_invalid_params',
                __( 'Invalid parameters.', 'mybookaro' ),
                [ 'status' => 400 ]
            );
        }

        $user_id = get_current_user_id();
        foreach ( $booking_ids as $booking_id ) {
            $role = $this->actions->get_booking_actor_role( $booking_id, $user_id );
            if ( null === $role || 'provider' !== $role ) {
                return new \WP_Error(
                    'bookaro_forbidden',
                    __( 'You are not allowed to update this booking.', 'mybookaro' ),
                    [ 'status' => 403 ]
                );
            }
        }

        return true;
    }

    public function can_provider_manage_single_booking( WP_REST_Request $request ): bool|\WP_Error
    {
        return $this->can_manage_booking_for_roles(
            $request,
            [ 'provider' ],
            __( 'You are not allowed to manage this booking.', 'mybookaro' )
        );
    }

    public function can_admin_manage_booking( WP_REST_Request $request ): bool|\WP_Error
    {
        $auth = $this->require_authenticated_write(
            $request,
            __( 'You are not allowed to perform this action.', 'mybookaro' )
        );
        if ( is_wp_error( $auth ) ) {
            return $auth;
        }

        if ( current_user_can( 'manage_options' ) ) {
            return true;
        }

        return new \WP_Error(
            'bookaro_forbidden',
            __( 'You are not allowed to perform this action.', 'mybookaro' ),
            [ 'status' => 403 ]
        );
    }

    public function can_view_provider_bookings( WP_REST_Request $request ): bool|\WP_Error
    {
        $auth = $this->require_authenticated(
            $request,
            __( 'You must be logged in to view bookings.', 'mybookaro' )
        );
        if ( is_wp_error( $auth ) ) {
            return $auth;
        }

        if ( current_user_can( 'manage_options' ) ) {
            return true;
        }

        if ( ! bookaro_user_has_provider_access() ) {
            return new \WP_Error(
                'bookaro_forbidden',
                __( 'You do not have permission to view provider bookings.', 'mybookaro' ),
                [ 'status' => 403 ]
            );
        }

        return true;
    }

    public function can_view_owner_bookings( WP_REST_Request $request ): bool|\WP_Error
    {
        $auth = $this->require_authenticated(
            $request,
            __( 'You must be logged in to view bookings.', 'mybookaro' )
        );
        if ( is_wp_error( $auth ) ) {
            return $auth;
        }

        if ( current_user_can( 'manage_options' ) ) {
            return true;
        }

        if ( ! bookaro_user_has_customer_access() ) {
            return new \WP_Error(
                'bookaro_forbidden',
                __( 'You do not have permission to view customer bookings.', 'mybookaro' ),
                [ 'status' => 403 ]
            );
        }

        return true;
    }

    public function can_view_notification_logs( WP_REST_Request $request ): bool|\WP_Error
    {
        $auth = $this->require_authenticated(
            $request,
            __( 'You must be logged in to view notifications.', 'mybookaro' )
        );
        if ( is_wp_error( $auth ) ) {
            return $auth;
        }

        if ( current_user_can( 'manage_options' ) ) {
            return true;
        }

        $booking_id = absint( $request->get_param( 'id' ) );
        if ( $booking_id < 1 ) {
            return new \WP_Error(
                'bookaro_invalid_booking',
                __( 'Invalid booking.', 'mybookaro' ),
                [ 'status' => 400 ]
            );
        }

        // Bug 23: previously this gated on bookaro_user_has_provider_access()
        // and required the actor's role on the booking to be `provider`, which
        // 403'd a logged-in customer trying to view notifications for their
        // OWN booking — even though the same customer can add a note and
        // cancel the same booking. Mirror the customer-OR-provider pattern
        // used by /note and /cancel: the per-booking actor role check is the
        // only ownership signal we need.
        $role = $this->actions->get_booking_actor_role( $booking_id, get_current_user_id() );
        if ( null === $role ) {
            return new \WP_Error(
                'bookaro_invalid_booking',
                __( 'Invalid booking.', 'mybookaro' ),
                [ 'status' => 400 ]
            );
        }
        if ( ! in_array( $role, [ 'customer', 'provider' ], true ) ) {
            return new \WP_Error(
                'bookaro_forbidden',
                __( 'You are not allowed to view this booking.', 'mybookaro' ),
                [ 'status' => 403 ]
            );
        }

        return true;
    }

    private function can_manage_booking_for_roles( WP_REST_Request $request, array $roles, string $message ): bool|\WP_Error
    {
        $auth = $this->require_authenticated_write( $request, $message );
        if ( is_wp_error( $auth ) ) {
            return $auth;
        }

        if ( current_user_can( 'manage_options' ) ) {
            return true;
        }

        $booking_id = absint( $request->get_param( 'id' ) );
        if ( $booking_id < 1 ) {
            return new \WP_Error(
                'bookaro_invalid_booking',
                __( 'Invalid parameters.', 'mybookaro' ),
                [ 'status' => 400 ]
            );
        }

        $role = $this->actions->get_booking_actor_role( $booking_id, get_current_user_id() );
        if ( null === $role ) {
            return new \WP_Error(
                'bookaro_invalid_booking',
                __( 'Invalid parameters.', 'mybookaro' ),
                [ 'status' => 400 ]
            );
        }
        if ( '' === $role || ! in_array( $role, $roles, true ) ) {
            return new \WP_Error(
                'bookaro_forbidden',
                $message,
                [ 'status' => 403 ]
            );
        }

        return true;
    }

    private function get_create_booking_args(): array
    {
        return [
            'listing_id'  => [
                'required'          => false,
                'sanitize_callback' => 'absint',
                'validate_callback' => [ $this, 'validate_non_negative_id' ],
            ],
            'provider_id' => [
                'required'          => true,
                'sanitize_callback' => 'absint',
                'validate_callback' => [ $this, 'validate_positive_id' ],
            ],
            'service_id'  => [
                'sanitize_callback' => 'absint',
                'validate_callback' => [ $this, 'validate_non_negative_id' ],
            ],
            'bookaro_honeypot' => [
                'required' => false,
                'sanitize_callback' => 'sanitize_text_field',
            ],
            'bookaro_hp_website' => [
                'required' => false,
                'sanitize_callback' => 'sanitize_text_field',
            ],
            'bookaro_form_started_at' => [
                'required' => false,
                'sanitize_callback' => 'absint',
                'validate_callback' => [ $this, 'validate_non_negative_id' ],
            ],
            'bookaro_form_elapsed_seconds' => [
                'required' => false,
                'sanitize_callback' => 'absint',
                'validate_callback' => [ $this, 'validate_non_negative_id' ],
            ],
        ];
    }

    private function get_status_args(): array
    {
        return [
            'status'     => [
                'sanitize_callback' => [ $this, 'sanitize_status' ],
                'validate_callback' => [ $this, 'validate_status' ],
            ],
            'new_status' => [
                'sanitize_callback' => [ $this, 'sanitize_status' ],
                'validate_callback' => [ $this, 'validate_status' ],
            ],
        ];
    }

    private function get_bulk_status_args(): array
    {
        return [
            'booking_ids' => [
                'required'          => true,
                'sanitize_callback' => [ $this, 'sanitize_booking_ids' ],
                'validate_callback' => [ $this, 'validate_booking_ids' ],
            ],
            'bulk_action' => [
                'sanitize_callback' => [ $this, 'sanitize_status' ],
                'validate_callback' => [ $this, 'validate_bulk_action' ],
            ],
        ];
    }

    public function sanitize_booking_ids( $value, WP_REST_Request $request = null, string $param = '' ): array
    {
        $raw = is_array( $value ) ? $value : [];
        return array_values( array_filter( array_map( 'absint', $raw ) ) );
    }

    public function validate_booking_ids( $value, WP_REST_Request $request = null, string $param = '' ): bool
    {
        $ids = $this->sanitize_booking_ids( $value );
        return ! empty( $ids );
    }

    public function validate_positive_id( $value, WP_REST_Request $request = null, string $param = '' ): bool
    {
        if ( $value === null || $value === '' ) {
            return true;
        }
        return absint( $value ) > 0;
    }

    public function validate_non_negative_id( $value, WP_REST_Request $request = null, string $param = '' ): bool
    {
        if ( $value === null || $value === '' ) {
            return true;
        }
        if ( ! is_numeric( $value ) ) {
            return false;
        }
        return (int) $value >= 0;
    }

    public function sanitize_status( $value, WP_REST_Request $request = null, string $param = '' ): string
    {
        return sanitize_key( (string) $value );
    }

    public function validate_status( $value, WP_REST_Request $request = null, string $param = '' ): bool
    {
        $value = $this->sanitize_status( $value );
        if ( $value === '' ) {
            return false;
        }
        return in_array( $value, [ 'approved', 'rejected', 'completed', 'no_show' ], true );
    }

    public function validate_bulk_action( $value, WP_REST_Request $request = null, string $param = '' ): bool
    {
        $value = $this->sanitize_status( $value );
        if ( $value === '' ) {
            return false;
        }
        return in_array( $value, [ 'approved', 'rejected', 'cancelled' ], true );
    }

    public function create_booking( WP_REST_Request $request ): WP_REST_Response|\WP_Error
    {
        $listing_id = absint( $request->get_param( 'listing_id' ) );
        if ( $listing_id < 1 ) {
            $single_business = function_exists( 'bookaro_is_single_business_site' )
                && bookaro_is_single_business_site();
            if ( ! $single_business ) {
                return $this->ensure_error_status(
                    new \WP_Error(
                        'bookaro_invalid_listing',
                        __( 'Listing is required for this booking flow.', 'mybookaro' ),
                        [ 'status' => 400 ]
                    )
                );
            }
        }

        $params = $request->get_params();
        $payload = $this->actions->sanitize_booking_payload(
            is_array( $params ) ? $params : [],
            [ 'source' => 'rest', 'action' => 'create_booking' ]
        );
        $result = $this->actions->create_booking( $payload, get_current_user_id() );
        if ( is_wp_error( $result ) ) {
            return $this->ensure_error_status( $result );
        }
        $response = is_array( $result ) ? $result : [];
        $response['message'] = __( 'Booking submitted successfully!', 'mybookaro' );
        return rest_ensure_response( $response );
    }

    public function get_provider_bookings( WP_REST_Request $request ): WP_REST_Response|\WP_Error
    {
        $rows = $this->actions->get_provider_bookings( get_current_user_id() );
        if ( is_wp_error( $rows ) ) {
            return $this->ensure_error_status( $rows );
        }
        return rest_ensure_response( $rows );
    }

    public function get_owner_bookings( WP_REST_Request $request ): WP_REST_Response|\WP_Error
    {
        $rows = $this->actions->get_owner_bookings( get_current_user_id() );
        if ( is_wp_error( $rows ) ) {
            return $this->ensure_error_status( $rows );
        }
        return rest_ensure_response( $rows );
    }

    public function get_notification_logs( WP_REST_Request $request ): WP_REST_Response|\WP_Error
    {
        $booking_id = (int) $request->get_param( 'id' );
        $logs = $this->actions->get_notification_logs(
            $booking_id,
            get_current_user_id(),
            current_user_can( 'manage_options' )
        );
        if ( is_wp_error( $logs ) ) {
            return $this->ensure_error_status( $logs );
        }
        return rest_ensure_response( $logs );
    }

    public function cancel_booking( WP_REST_Request $request ): WP_REST_Response|\WP_Error
    {
        $booking_id = (int) $request->get_param( 'id' );
        // Round 10 fix (Issue #19): accept both `cancel_reason` (canonical) and
        // `reason` (more natural for API integrators). The canonical name wins
        // if both are sent.
        $reason_raw = $request->get_param( 'cancel_reason' ) ?? $request->get_param( 'reason' ) ?? '';
        $reason = sanitize_text_field( (string) $reason_raw );
        $result = $this->actions->cancel_booking( $booking_id, get_current_user_id(), $reason );
        if ( is_wp_error( $result ) ) {
            return $this->ensure_error_status( $result );
        }
        if ( is_array( $result ) ) {
            return rest_ensure_response( $result );
        }
        return rest_ensure_response( [ 'message' => __( 'Booking cancelled successfully.', 'mybookaro' ) ] );
    }

    public function add_note( WP_REST_Request $request ): WP_REST_Response|\WP_Error
    {
        $booking_id = (int) $request->get_param( 'id' );
        $note = sanitize_textarea_field( $request->get_param( 'note' ) ?? '' );
        $result = $this->actions->add_note( $booking_id, get_current_user_id(), $note );
        if ( is_wp_error( $result ) ) {
            return $this->ensure_error_status( $result );
        }
        return rest_ensure_response( [ 'message' => __( 'Note saved.', 'mybookaro' ) ] );
    }

    public function reschedule_booking( WP_REST_Request $request ): WP_REST_Response|\WP_Error
    {
        $booking_id = (int) $request->get_param( 'id' );
        $params = $request->get_params();
        $payload = $this->actions->sanitize_booking_payload(
            is_array( $params ) ? $params : [],
            [ 'source' => 'rest', 'action' => 'reschedule_booking' ]
        );
        $result = $this->actions->reschedule_booking( $booking_id, get_current_user_id(), $payload );
        if ( is_wp_error( $result ) ) {
            return $this->ensure_error_status( $result );
        }
        if ( is_array( $result ) ) {
            return rest_ensure_response( $result );
        }
        return rest_ensure_response( [ 'message' => __( 'Booking rescheduled successfully.', 'mybookaro' ) ] );
    }

    public function update_status( WP_REST_Request $request ): WP_REST_Response|\WP_Error
    {
        $booking_id = (int) $request->get_param( 'id' );
        $status = sanitize_text_field( $request->get_param( 'status' ) ?? $request->get_param( 'new_status' ) ?? '' );
        $result = $this->actions->update_status(
            $booking_id,
            $status,
            get_current_user_id(),
            current_user_can( 'manage_options' )
        );
        if ( is_wp_error( $result ) ) {
            return $this->ensure_error_status( $result );
        }
        return rest_ensure_response( [ 'message' => __( 'Booking status updated.', 'mybookaro' ) ] );
    }

    public function bulk_update_status( WP_REST_Request $request ): WP_REST_Response|\WP_Error
    {
        $action = sanitize_text_field( $request->get_param( 'bulk_action' ) ?? '' );
        $ids = $request->get_param( 'booking_ids' );
        if ( ! is_array( $ids ) ) {
            $ids = [];
        }
        $result = $this->actions->bulk_update_status(
            $action,
            $ids,
            get_current_user_id(),
            current_user_can( 'manage_options' )
        );
        if ( is_wp_error( $result ) ) {
            return $this->ensure_error_status( $result );
        }
        return rest_ensure_response( $result );
    }
}
