<?php

namespace Booktics\Customer\Controllers;

use Booktics\Abstracts\Base_Rest_Controller;
use Booktics\Abstracts\Booktics_Database;
use Booktics\Abstracts\User_Model;
use Booktics\Customer\Handlers\Customer_Event_Handler;
use Booktics\Models\Appointment_Model;
use Booktics\Models\Customer_Model;
use Booktics\Models\Guest_Model;
use Booktics\Models\Order_Model;
use Booktics\Models\Payment_Model;
use WP_Error;
use WP_HTTP_Response;
use WP_REST_Response;
use WP_REST_Server;

/**
 * Customer controller
 *
 * @package Booktics/Customer
 */
class Customer_Controller extends Base_Rest_Controller {

    /**
     * Store namespace of api
     *
     * @var string
     */
    protected $namespace = 'booktics/v1';

    /**
     * Store base url
     *
     * @var string
     */
    protected $base = 'customers';

    /**
     *
     *
     * @return void
     */
    public function register_routes(): void {
        register_rest_route(
            $this->namespace, $this->base, array(
                array(
                    'methods'             => WP_REST_Server::CREATABLE,
                    'callback'            => array( $this, 'create_item' ),
                    'permission_callback' => array(
                        $this,
                        'create_customer_permission',
                    ),
                ),
                array(
                    'methods'             => WP_REST_Server::READABLE,
                    'callback'            => array( $this, 'get_items' ),
                    'permission_callback' => array(
                        $this,
                        'get_customer_permission',
                    ),
                ),
                array(
                    'methods'             => WP_REST_Server::DELETABLE,
                    'callback'            => array( $this, 'delete_items' ),
                    'permission_callback' => array(
                        $this,
                        'delete_customer_permission',
                    ),
                ),
            )
        );

        register_rest_route(
            $this->namespace, $this->base . '/profile', array(
                array(
                    'methods'             => WP_REST_Server::READABLE,
                    'callback'            => array( $this, 'get_profile' ),
                    'permission_callback' => array(
                        $this,
                        'customer_profile_permission',
                    ),
                ),
            )
        );

        register_rest_route(
            $this->namespace, '/' . $this->base . '/(?P<id>[\d]+)', array(
                array(
                    'methods'             => WP_REST_Server::EDITABLE,
                    'callback'            => array( $this, 'update_item' ),
                    'permission_callback' => array(
                        $this,
                        'update_customer_permission',
                    ),
                    'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
                ),
                array(
                    'methods'             => WP_REST_Server::DELETABLE,
                    'callback'            => array( $this, 'delete_item' ),
                    'permission_callback' => array(
                        $this,
                        'delete_customer_permission',
                    ),
                ),
                array(
                    'method'              => WP_REST_Server::READABLE,
                    'callback'            => array( $this, 'get_item' ),
                    'permission_callback' => array(
                        $this,
                        'get_customer_permission',
                    ),
                ),
            )
        );

        register_rest_route(
            $this->namespace, '/' . $this->base . '/(?P<id>[\d]+)/connect', array(
                array(
                    'methods'             => WP_REST_Server::EDITABLE,
                    'callback'            => array( $this, 'connect_customer' ),
                    'permission_callback' => array(
                        $this,
                        'update_customer_permission',
                    ),
                ),
            )
        );

        register_rest_route(
            $this->namespace, '/' . $this->base . '/(?P<id>[\d]+)/disconnect', array(
                array(
                    'methods'             => WP_REST_Server::EDITABLE,
                    'callback'            => array( $this, 'disconnect_customer' ),
                    'permission_callback' => array(
                        $this,
                        'update_customer_permission',
                    ),
                ),
            )
        );
    }

    /**
     * Check if a given request has access to create items.
     *
     * @param $request Full data about the request.
     */
    public function create_customer_permission( $request ) {
        return current_user_can( 'manage_options' );
    }

    /**
     * Prepare and validate customer data
     *
     * @param $request
     *
     * @return array|bool|WP_Error
     */
    protected function prepare_customer_data( $request ) {
        $input_data = json_decode( $request->get_body(), true );

        $validate = booktics_validate(
            $input_data, array(
                'name'       => array(
                    'required',
                ),
                'email'      => array(
                    'required',
                    'unique:' . booktics_get_table_name( 'booktics_guests' ) . ',email',
                ),
            )
        );

        if ( is_wp_error( $validate ) ) {
            return $validate;
        }

        return array(
            'name'       => $input_data['name'],
            'email'      => $input_data['email'],
            'user_login' => User_Model::generate_username( $input_data['email'] ),
            'phone'      => $input_data['phone'],
        );
    }

    /**
     * Store new customer data
     *
     * @param $request
     *
     * @return array|WP_Error|WP_HTTP_Response|WP_REST_Response
     */
    public function create_item( $request ) {
        $prepared_data = $this->prepare_customer_data( $request );
        if ( is_wp_error( $prepared_data ) ) {
            return $this->error(
                $prepared_data->get_error_message(),
                422,
                'Validation',
                $prepared_data->errors
            );
        }

        $db       = new Booktics_Database();
        $customer = ( new Guest_Model( $db ) )->create( $prepared_data );
        $image_url = $request->get_param( 'image' );
        if ( $image_url ) {
            $customer->update( array( 'image' => esc_url_raw( $image_url ) ) );
        }

        if ( ! $customer ) {
            return $this->error( __( 'Customer create error', 'booktics' ), 500 );
        }

        do_action( 'booktics_customer_created', $customer, $request );

        return $this->response( $customer, __( 'Successfully created customer', 'booktics' ), 201 );
    }

    /**
     * Check if a given request has access to get all customer
     *
     * @param $request
     *
     * @return bool
     */
    public function get_customer_permission( $request ) {
        if ( current_user_can( 'manage_options' ) ) {
            return true;
        }
 
        if ( is_user_logged_in() ) {
            $current_user = wp_get_current_user();
 
            if ( in_array( 'booktics_team_member', (array) $current_user->roles, true ) ) {
                return true;
            }
 
            $requested_customer_id = isset( $request['id'] ) ? intval( $request['id'] ) : 0;
 
            if ( $requested_customer_id > 0 ) {
                $db                = new Booktics_Database();
                $requested_customer = ( new Guest_Model( $db ) )->find( array( 'id' => $requested_customer_id ) );
 
                if ( $requested_customer && intval( $requested_customer->wp_user_id ) === get_current_user_id() ) {
                    return true;
                }
            }
        }

        return new WP_Error(
            'booktics_forbidden',
            __( 'You are not allowed to view this customer data.', 'booktics' ),
            array( 'status' => 403 )
        );
    }

    /**
     * Get paginated list items for customer
     *
     * @param $request
     *
     * @return WP_Error|WP_HTTP_Response|WP_REST_Response
     */
    public function get_items( $request ) {
        $per_page   = ! empty( $request['per_page'] ) ? intval( $request['per_page'] ) : 20;
        $paged      = ! empty( $request['paged'] ) ? intval( $request['paged'] ) : 1;
        $name       = ! empty( $request['name'] ) ? sanitize_text_field( $request['name'] ) : '';
        $email      = ! empty( $request['email'] ) ? sanitize_email( $request['email'] ) : '';
        $phone      = ! empty( $request['phone'] ) ? sanitize_text_field( $request['phone'] ) : '';
        $user_name  = ! empty( $request['user_name'] ) ? sanitize_text_field( $request['user_name'] ) : '';
        $search     = ! empty( $request['search'] ) ? sanitize_text_field( $request['search'] ) : '';

        $db        = new Booktics_Database();
        $customers = ( new Guest_Model( $db ) )->query();

        $args = array(
            'name'       => array( 'LIKE', $name ),
            'email'      => array( 'LIKE', $email ),
            'phone'      => array( 'LIKE', $phone ),
            'user_login' => array( 'LIKE', $user_name ),
        );

        foreach ( $args as $key => $value ) {
            if ( $value[1] ) {
                $customers->where( $key, $value[0], $value[1] );
            }
        }

        if ( $search ) {
            $customers->where( 'name', 'LIKE', $search )
                        ->or_where( 'email', 'LIKE', $search )
                        ->or_where( 'phone', 'LIKE', $search );
        }

        $customers = $customers->paginate( $paged, $per_page );
        foreach ( $customers['items'] as &$customer ) {
            $customer = ( new Guest_Model( $db ) )->with_virtual_attributes( $customer );
            
            // Mask wp_user_id and user_login from non-admins (use empty string instead of unset for API consistency)
            if ( ! current_user_can( 'manage_options' ) ) {
                $customer->wp_user_id = '';
                $customer->user_login = '';
            }
        }

        return $this->response( $customers, __( 'Successfully fetched customer', 'booktics' ) );
    }

    /**
     * Checks if a given request has access to delete a team member.
     *
     * @param $request
     *
     * @return bool
     */
    public function delete_customer_permission( $request ) {
        return current_user_can( 'manage_options' );
    }

    /**
     * Delete a customer
     *
     * @param $request
     *
     * @return array|WP_Error|WP_HTTP_Response|WP_REST_Response
     */
    public function delete_item( $request ) {
        $id = intval( $request['id'] );

        $db          = new Booktics_Database();
        $order_model = new Order_Model( $db );
        $orders      = $order_model->get( array( 'customer_id' => $id ) );
        foreach ( $orders['items'] as $order ) {
            $appointment_model = new Appointment_Model();
            $appointments      = $appointment_model->where( 'order_id', $order->id );
            foreach ( $appointments as $appointment ) {
                $appointment->delete();
            }
            $payment_model = new Payment_Model( $db );
            $payments      = $payment_model->get( array( 'order_id' => $order->id ) );
            foreach ( $payments['items'] as $payment ) {
                ( new Payment_Model( $db ) )->delete( $payment->id );
            }
            $order_model->delete( $order->id );
        }

        $guest_model = ( new Guest_Model( $db ) );
        $guest = $guest_model->find( array( 'id' => $id ) );

        if ( $guest->wp_user_id ) {
            $customer = new Customer_Model( $guest->wp_user_id );
            $customer->delete();
        }

        $deleted = $guest_model->delete( $id );
        if ( ! $deleted ) {
            return $this->error( __( 'Customer delete error', 'booktics' ), 500 );
        }

        do_action( 'booktics_customers_deleted', $request );

        return $this->response( array(), __( 'Successfully deleted customer', 'booktics' ) );
    }

    /**
     * Delete multiple customer
     *
     * @param $request
     *
     * @return array|WP_Error|WP_HTTP_Response
     */
    public function delete_items( $request ) {
        $ids = ! empty( $request['ids'] ) ? $request['ids'] : array();

        if ( ! $ids ) {
            return $this->error( __( 'Customer ids cannot be empty', 'booktics' ), 500 );
        }
        $count = 0;
        $db    = new Booktics_Database();
        foreach ( $ids as $id ) {
            // Delete all orders (and their related appointments and payments) for this customer
            $order_model = new Order_Model( $db );
            $orders      = $order_model->get( array( 'customer_id' => $id ) );
            foreach ( $orders['items'] as $order ) {
                // Delete related appointments
                $appointment_model = new Appointment_Model();
                $appointments      = $appointment_model->where( 'order_id', $order->id );
                foreach ( $appointments as $appointment ) {
                    $appointment->delete();
                }
                // Delete related payments
                $payment_model = new Payment_Model( $db );
                $payments      = $payment_model->get( array( 'order_id' => $order->id ) );
                foreach ( $payments['items'] as $payment ) {
                    ( new Payment_Model( $db ) )->delete( $payment->id );
                }
                // Delete the order
                $order_model->delete( $order->id );
            }
            $guest_model = ( new Guest_Model( $db ) );
            $guest = $guest_model->find( array( 'id' => $id ) );
            if ( $guest->wp_user_id ) {
                $customer = new Customer_Model( $guest->wp_user_id );
                $customer->delete();
            }
            if ( $guest_model->delete( $id ) ) {
                ++$count;
            }
        }
        if ( 0 == $count ) {
            return $this->error( __( 'Customer delete error', 'booktics' ), 500 );
        }

        do_action( 'booktics_customers_deleted', $request );

        return $this->response( array(), __( 'Successfully deleted customer', 'booktics' ) );
    }

    /**
     * Get single customer
     *
     * @param $request
     *
     * @return array|WP_Error|WP_HTTP_Response|WP_REST_Response
     */
    public function get_item( $request ) {
        $id       = intval( $request['id'] );
        $db       = new Booktics_Database();
        $customer = ( new Guest_Model( $db ) )->find( array( 'id' => $id ) );
        if ( ! $customer ) {
            return $this->error( __( 'Customer not found', 'booktics' ), 404 );
        }

        $orders = ( new Order_Model( $db ) )->get( array( 'customer_id' => $id ) );
        $customer->orders = array_map(
            function ( $order ) {
                $booking_count = ( new Appointment_Model() )
                    ->get_appointments_count_by_order_id( $order->id );

                return array(
                    'date'          => $order->created_at ? gmdate( 'j F, Y', strtotime( $order->created_at ) ) : '',
                    'total_booking' => $booking_count,
                    'total_amount'  => $order->total,
                );
            }, $orders['items']
        );

        // Mask wp_user_id and user_login from non-admins (use empty string instead of unset for API consistency)
        if ( ! current_user_can( 'manage_options' ) ) {
            $customer->wp_user_id = '';
            $customer->user_login = '';
        }

        return $this->response( $customer, __( 'Customer found successfully', 'booktics' ) );
    }

    /**
     * Checks if a given request has access to update a customer.
     *
     * @param $request
     *
     * @return bool True if the request has access to update the item, WP_Error object otherwise.
     */
    public function update_customer_permission( $request ) {
        return current_user_can( 'manage_options' );
    }

    /**
     * Prepare data for customer update
     *
     * @param $request
     *
     * @return array|bool|WP_Error
     */
    public function prepare_customer_update( $request ) {
        $input_data = json_decode( $request->get_body(), true );

        $validation_rules        = array(
            'first_name' => array(
                'required',
            ),
            'email'      => array(
                'required',
                'unique:' . booktics_get_table_name( 'booktics_guests' ) . ',email,' . $request['id'],
            ),
        );
        $update_validation_rules = array();
        $validated_data          = array();

        if ( ! is_array( $input_data ) ) {
            return new WP_Error(
                'invalid_input_data',
                'Invalid data'
            );
        }

        foreach ( $input_data as $key => $value ) {
            $validated_data[ $key ] = $value;
        }

        $validate = booktics_validate( $input_data, $update_validation_rules );

        if ( is_wp_error( $validate ) ) {
            return $validate;
        }

        return $validated_data;
    }

    /**
     * @param $request
     *
     * @return array|WP_Error|WP_HTTP_Response|WP_REST_Response
     */
    public function update_item( $request ) {
        if ( ! isset( $request['id'] ) ) {
            return $this->error( 'Invalid customer id', 400 );
        }
        $prepared_data = $this->prepare_customer_update( $request );

        if ( is_wp_error( $prepared_data ) ) {
            return $this->error( __( 'Customer validation error', 'booktics' ), 422, 'Validation', $prepared_data->errors );
        }

        $db        = new Booktics_Database();
        $customer  = ( new Guest_Model( $db ) )->update( $request['id'], $prepared_data );
        $image_url = $request->get_param( 'image' );
        if ( $image_url ) {
            ( new Guest_Model( $db ) )->update( $request['id'], array( 'image' => esc_url_raw( $image_url ) ) );
            ( new Guest_Model( $db ) )->update( absint( $request['id'] ), array( 'image' => esc_url_raw( $image_url ) ) );
        }

        if ( ! $customer ) {
            return $this->error( __( 'Customer update error', 'booktics' ), 500 );
        }

        do_action( 'booktics_customer_updated', $customer, $request );
        $customer = ( new Guest_Model( $db ) )->find( array( 'id' => $request['id'] ) );

        return $this->response( $customer, __( 'Successfully updated customer', 'booktics' ) );
    }

    /**
     * Check if a given request has access to get customer profile.
     *
     * @param $request
     *
     * @return bool
     */
    public function customer_profile_permission( $request ) {
        if ( is_user_logged_in() ) {
            return true;
        }

        return new WP_Error(
            'booktics_unauthorized',
            __( 'You must be logged in to view your profile.', 'booktics' ),
            array( 'status' => 401 )
        );
    }

    /**
     * Get customer profile
     *
     * @param $request
     *
     * @return array|WP_Error|WP_HTTP_Response|WP_REST_Response
     */
    public function get_profile( $request ) {
        $user_id = get_current_user_id();
        $db = new Booktics_Database();
        $customer = ( new Guest_Model( $db ) )->find( array( 'wp_user_id' => $user_id ) );
        $args = array(
            // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Meta query is necessary for filtering customers by meta fields
			'meta_query' => array(
				array(
					'key'     => 'customer_id',
					'value'   => $customer->id,
					'compare' => '=',
				),
			),
		);

        $appointments = ( new Appointment_Model() )->all( $args );
        $customer->appointments = $appointments;

        return $this->response( $customer, __( 'Successfully fetched customer profile', 'booktics' ) );
    }

    /**
     * Connect a guest to a customer
     *
     * @param $request
     *
     * @return array|WP_Error|WP_HTTP_Response|WP_REST_Response
     */
    public function connect_customer( $request ) {
        $id = intval( $request['id'] );
        $db = new Booktics_Database();
        $guest = ( new Guest_Model( $db ) )->find( array( 'id' => $id ) );

        if ( ! $guest ) {
            return $this->error( __( 'Customer not found', 'booktics' ), 404 );
        }

        if ( $guest->wp_user_id ) {
            return $this->error( __( 'Customer already connected', 'booktics' ), 400 );
        }

        $customer_model  = ( new Customer_Model() );
        $customer_id = $customer_model->save(
            array(
				'user_login'   => $guest->user_login,
				'user_email'   => $guest->email,
				'first_name'   => $guest->first_name ?? '',
				'last_name'    => $guest->last_name ?? '',
				'display_name' => $guest->display_name ?? '',
				'phone'        => $guest->phone ?? '',
            )
        );

        if ( is_wp_error( $customer_id ) ) {
            return $this->error( __( 'Customer create error', 'booktics' ), 500 );
        }
        ( new Customer_Event_Handler() )->on_customer_create( $customer_id );

        ( new Guest_Model( $db ) )->update( $id, array( 'wp_user_id' => $customer_id ) );
        $guest = ( new Guest_Model( $db ) )->find( array( 'id' => $id ) );
        return $this->response( $guest, __( 'Successfully connected customer', 'booktics' ) );
    }


    /**
     * Disconnect a guest from a customer
     *
     * @param $request
     *
     * @return array|WP_Error|WP_HTTP_Response|WP_REST_Response
     */
    public function disconnect_customer( $request ) {
        $id = intval( $request['id'] );
        $db = new Booktics_Database();
        $guest = ( new Guest_Model( $db ) )->find( array( 'id' => $id ) );

        if ( ! $guest ) {
            return $this->error( __( 'Customer not found', 'booktics' ), 404 );
        }
        if ( $guest->wp_user_id ) {
            $customer = new Customer_Model( $guest->wp_user_id );
            $customer->delete();
        }

		( new Guest_Model( $db ) )->update( $id, array( 'wp_user_id' => null ) );
		$guest = ( new Guest_Model( $db ) )->find( array( 'id' => $id ) );
        return $this->response( $guest, __( 'Successfully disconnected customer', 'booktics' ) );
    }
}
