<?php
namespace Bookly\Backend\Modules\Appointments;

use Bookly\Lib;
use Bookly\Backend\Components\Dialogs\Queue\NotificationList;

class Ajax extends Lib\Base\Ajax
{
    /**
     * @inheritDoc
     */
    protected static function permissions()
    {
        return array( '_default' => 'supervisor' );
    }

    /**
     * Get list of appointments.
     */
    public static function getAppointments()
    {
        $columns = Lib\Utils\Tables::filterColumns( self::parameter( 'columns' ), Lib\Utils\Tables::APPOINTMENTS );
        $order = self::parameter( 'order', array() );
        $filter = self::parameter( 'filter' );
        $limits = array(
            'length' => self::parameter( 'length' ),
            'start' => self::parameter( 'start' ),
        );

        $data = self::getAppointmentsTableData( $filter, $limits, $columns, $order, false );

        Lib\Utils\Tables::updateSettings( Lib\Utils\Tables::APPOINTMENTS, $columns, $order, $filter );

        wp_send_json( array(
            'draw' => ( int ) self::parameter( 'draw' ),
            'recordsTotal' => $data['total'],
            'recordsFiltered' => $data['filtered'],
            'data' => $data['data'],
        ) );
    }

    /**
     * Delete customer appointments.
     */
    public static function deleteCustomerAppointments()
    {
        // Customer appointments to delete
        $ca_list = array();
        // Appointments without customers to delete
        $appointments_list = array();
        foreach ( self::parameter( 'data', array() ) as $ca_data ) {
            if ( $ca_data['ca_id'] === 'null' ) {
                $appointments_list[] = $ca_data['id'];
            } else {
                $ca_list[] = $ca_data['ca_id'];
            }
        }
        $queue = new NotificationList();
        /** @var Lib\Entities\CustomerAppointment $ca */
        foreach ( Lib\Entities\CustomerAppointment::query()->whereIn( 'id', $ca_list )->find() as $ca ) {
            if ( self::parameter( 'notify' ) ) {
                switch ( $ca->getStatus() ) {
                    case Lib\Entities\CustomerAppointment::STATUS_PENDING:
                    case Lib\Entities\CustomerAppointment::STATUS_WAITLISTED:
                        $ca->setStatus( Lib\Entities\CustomerAppointment::STATUS_REJECTED );
                        break;
                    case Lib\Entities\CustomerAppointment::STATUS_APPROVED:
                        $ca->setStatus( Lib\Entities\CustomerAppointment::STATUS_CANCELLED );
                        break;
                    default:
                        $busy_statuses = Lib\Proxy\CustomStatuses::prepareBusyStatuses( array() );
                        if ( in_array( $ca->getStatus(), $busy_statuses ) ) {
                            $ca->setStatus( Lib\Entities\CustomerAppointment::STATUS_CANCELLED );
                        }
                }
                Lib\Notifications\Booking\Sender::sendForCA(
                    $ca,
                    null,
                    array( 'cancellation_reason' => self::parameter( 'reason' ) ),
                    false,
                    $queue
                );
            }
            $ca->deleteCascade();
        }

        /** @var Lib\Entities\Appointment $appointment */
        foreach ( Lib\Entities\Appointment::query()->whereIn( 'id', $appointments_list )->find() as $appointment ) {
            $ca = $appointment->getCustomerAppointments();
            if ( empty( $ca ) ) {
                $appointment->delete();
            }
        }
        $response = array();
        $list = $queue->getList();
        if ( $list ) {
            $db_queue = new Lib\Entities\NotificationQueue();
            $db_queue
                ->setData( json_encode( array( 'all' => $list ) ) )
                ->save();

            $response['queue'] = array( 'token' => $db_queue->getToken(), 'all' => $queue->getInfo() );
        }
        wp_send_json_success( $response );
    }

    /**
     * @param array $filter
     * @param array $limits
     * @param array $columns
     * @param array $order
     * @param bool $export
     * @param string|null $display_tz
     * @return array
     */
    public static function getAppointmentsTableData( $filter = array(), $limits = array(), $columns = array(), $order = array(), $export = false, $display_tz = null )
    {
        $postfix_any = sprintf( ' (%s)', get_option( 'bookly_l10n_option_employee' ) );
        $postfix_archived = sprintf( ' (%s)', __( 'Archived', 'bookly' ) );

        $query = Lib\Entities\Appointment::query( 'a' )
            ->select( 'a.id,
                ca.payment_id,
                ca.status,
                ca.id        AS ca_id,
                ca.notes,
                ca.number_of_persons,
                ca.extras,
                ca.extras_multiply_nop,
                ca.rating,
                ca.rating_comment,
                COALESCE(ca.created_at, a.created_at) AS created_date,
                a.start_date,
                a.staff_any,
                a.online_meeting_provider,
                a.online_meeting_id,
                a.online_meeting_data,
                a.internal_note,
                c.full_name  AS customer_full_name,
                c.phone      AS customer_phone,
                c.email      AS customer_email,
                c.birthday   AS customer_birthday,
                c.country    AS customer_country,
                c.state      AS customer_state,
                c.postcode   AS customer_postcode,
                c.city       AS customer_city,
                c.street     AS customer_street,
                c.street_number AS customer_street_number,
                c.additional_address AS customer_additional_address,
                c.full_address AS customer_full_address,
                st.full_name AS staff_name,
                st.visibility AS staff_visibility,
                p.paid       AS payment,
                p.total      AS payment_total,
                p.type       AS payment_type,
                p.status     AS payment_status,
                s.price      AS service_price,
                COALESCE(s.title, a.custom_service_name) AS service_title,
                (TIMESTAMPDIFF(SECOND, a.start_date, a.end_date) + a.extras_duration) AS service_duration' )
            ->leftJoin( 'CustomerAppointment', 'ca', 'a.id = ca.appointment_id' )
            ->leftJoin( 'Service', 's', 's.id = a.service_id' )
            ->leftJoin( 'Customer', 'c', 'c.id = ca.customer_id' )
            ->leftJoin( 'Payment', 'p', 'p.id = ca.payment_id' )
            ->leftJoin( 'Staff', 'st', 'st.id = a.staff_id' )
            ->leftJoin( 'StaffService', 'ss', 'ss.staff_id = st.id AND ss.service_id = s.id AND ss.location_id = a.location_id' );

        $total = $query->count();

        $sub_query = Lib\Proxy\Files::getSubQueryAttachmentExists();
        if ( ! $sub_query ) {
            $sub_query = '0';
        }
        $query->addSelect( '(' . $sub_query . ') AS attachment' );

        Lib\Proxy\Locations::prepareAppointmentsQuery( $query );

        if ( $filter['date'] == 'any' ) {
            $query->whereNot( 'a.start_date', null );
        } elseif ( $filter['date'] == 'null' ) {
            $query->where( 'a.start_date', null );
        } else {
            list ( $start, $end ) = explode( ' - ', $filter['date'], 2 );
            $end = date( 'Y-m-d 23:59:59', strtotime( $end ) );
            $query->whereBetween( 'a.start_date', $start, $end );
        }

        if ( $filter['created_date'] != 'any' ) {
            list ( $start, $end ) = explode( ' - ', $filter['created_date'], 2 );
            $end = date( 'Y-m-d', strtotime( $end ) + DAY_IN_SECONDS );
            $query->whereBetween( 'COALESCE(ca.created_at, a.created_at)', $start, $end );
        }

        if ( $filter['staff'] != '' ) {
            $query->where( 'a.staff_id', $filter['staff'] );
        }

        if ( $filter['customer'] != '' ) {
            $query->where( 'ca.customer_id', $filter['customer'] );
        }

        if ( $filter['service'] != '' ) {
            $query->where( 'a.service_id', $filter['service'] ?: null );
        }

        if ( isset( $filter['location'] ) && $filter['location'] != '' ) {
            if ( $filter['location'] == 'w/o' ) {
                $query->where( 'a.location_id', null );
            } else {
                $query->where( 'a.location_id', $filter['location'] );
            }
        }

        if ( isset( $filter['status'] ) && count( $filter['status'] ) !== count( Lib\Entities\CustomerAppointment::getStatuses() ) && count( $filter['status'] ) !== 0 ) {
            $query->whereIn( 'ca.status', $filter['status'] );
        }

        if ( ! empty( $filter['search'] ) ) {
            $like = '%' . $filter['search'] . '%';
            $query->whereRaw(
                'a.id LIKE %s OR ca.id LIKE %s OR c.full_name LIKE %s OR c.phone LIKE %s OR c.email LIKE %s OR st.full_name LIKE %s OR COALESCE(s.title, a.custom_service_name) LIKE %s',
                array( $like, $like, $like, $like, $like, $like, $like )
            );
        }

        foreach ( $order as $sort_by ) {
            $column = str_replace( '.', '_', $columns[ $sort_by['column'] ]['data'] );
            if ( $column === 'no' ) {
                if ( Lib\Config::groupBookingActive() ) {
                    $query->sortBy( 'id' )
                        ->order( $sort_by['dir'] === 'desc' ? Lib\Query::ORDER_DESCENDING : Lib\Query::ORDER_ASCENDING );
                }
                $column = 'ca_id';
            }

            $direction = $sort_by['dir'] === 'desc' ? Lib\Query::ORDER_DESCENDING : Lib\Query::ORDER_ASCENDING;
            if ( $column !== 'id' ) {
                $direction .= ', a.id ' . $direction;
            }
            $query->sortBy( $column )
                ->order( $direction );
        }

        $custom_fields = array();
        $fields_data = Lib\Proxy\CustomFields::getWhichHaveData() ?: array();
        foreach ( $fields_data as $field_data ) {
            $custom_fields[ $field_data->id ] = '';
        }

        $filtered = $query->count();

        if ( ! empty( $limits ) ) {
            $query->limit( $limits['length'] )->offset( $limits['start'] );
        }

        $locations_active = Lib\Config::locationsActive();

        if ( $display_tz === null ) {
            $convert_tz = false;
        } else {
            $wp_tz = Lib\Config::getWPTimeZone();
            $convert_tz = $display_tz != $wp_tz;
        }

        $data = array();
        foreach ( $query->fetchArray() as $row ) {
            $service_duration = $export
                ? (int) ( $row['service_duration'] / MINUTE_IN_SECONDS )
                : Lib\Utils\DateTime::secondsToInterval( $row['service_duration'] );
            $payment_title = '';
            $payment_amount = '';
            $payment_gateway = '';
            $payment_status_html = '';
            $payment_raw_title = '';
            if ( $row['payment'] !== null && $row['status'] !== Lib\Entities\CustomerAppointment::STATUS_WAITLISTED ) {
                $payment_amount = Lib\Utils\Price::format( $row['payment'] );
                if ( $row['payment'] != $row['payment_total'] ) {
                    $payment_amount = sprintf( __( '%s of %s', 'bookly' ), $payment_amount, Lib\Utils\Price::format( $row['payment_total'] ) );
                }

                $payment_gateway = Lib\Entities\Payment::typeToString( $row['payment_type'] );
                $payment_status_class_map = array(
                    Lib\Entities\Payment::STATUS_COMPLETED => 'bookly:text-green-700',
                    Lib\Entities\Payment::STATUS_PENDING   => 'bookly:text-amber-700',
                    Lib\Entities\Payment::STATUS_REJECTED  => 'bookly:text-red-700',
                    Lib\Entities\Payment::STATUS_REFUNDED  => 'bookly:text-gray-500',
                );
                $payment_status_class = isset( $payment_status_class_map[ $row['payment_status'] ] )
                    ? $payment_status_class_map[ $row['payment_status'] ]
                    : '';
                $payment_status_html = sprintf(
                    '<span%s>%s</span>',
                    $payment_status_class ? ' class="' . esc_attr( $payment_status_class ) . '"' : '',
                    Lib\Entities\Payment::statusToString( $row['payment_status'] )
                );

                $payment_raw_title = trim( sprintf(
                    '%s %s %s',
                    $payment_amount,
                    $payment_gateway,
                    Lib\Entities\Payment::statusToString( $row['payment_status'] )
                ) );

                // Full title kept for back-compat (raw export, other consumers).
                $payment_title = $payment_amount . ' ' . $payment_gateway . ' ' . $payment_status_html;
            }
            // Appointment status. Keep the raw enum code so the client can map it
            // to a badge variant; row['status'] is then replaced with the localized label.
            $row['status_code'] = $row['status'];
            $row['status'] = Lib\Entities\CustomerAppointment::statusToString( $row['status'] );
            $customer_appointment = new Lib\Entities\CustomerAppointment();
            $customer_appointment->load( $row['ca_id'] );
            // Custom fields
            foreach ( Lib\Proxy\CustomFields::getForCustomerAppointment( $customer_appointment ) ?: array() as $custom_field ) {
                $custom_fields[ $custom_field['id'] ] = $custom_field['value'];
            }
            if ( $row['ca_id'] !== null ) {
                $extras = Lib\Proxy\ServiceExtras::getInfo( json_decode( $row['extras'], true ) ?: array(), false ) ?: array();
                if ( $row['extras_multiply_nop'] && $row['number_of_persons'] > 1 ) {
                    foreach ( $extras as $index => $extra ) {
                        $extras[ $index ]['title'] = '<i class="far fa-user"></i>&nbsp;' . $row['number_of_persons'] . '&nbsp;&times;&nbsp;' . $extra['title'];
                    }
                }
            } else {
                $extras = array();
            }

            if ( $row['online_meeting_provider'] === 'bbb' ) {
                $appointment = new Lib\Entities\Appointment();
                $appointment->setOnlineMeetingData( $row['online_meeting_data'] )
                    ->setOnlineMeetingProvider( $row['online_meeting_provider'] )
                    ->setOnlineMeetingId( $row['online_meeting_id'] );
                $online_meeting_start_url = Lib\Proxy\Shared::buildOnlineMeetingStartUrl( '', $appointment );
            } else {
                $online_meeting_start_url = $row['online_meeting_id'];
            }

            if ( $convert_tz ) {
                $row['start_date'] = $row['start_date'] ? Lib\Utils\DateTime::convertTimeZone( $row['start_date'], $wp_tz, $display_tz ) : null;
                $row['created_date'] = Lib\Utils\DateTime::convertTimeZone( $row['created_date'], $wp_tz, $display_tz );
            }

            $data[] = array(
                'id' => $row['id'],
                'no' => Lib\Config::groupBookingActive() && $row['ca_id'] ? $row['id'] . '-' . $row['ca_id'] : $row['ca_id'],
                'start_date' => $row['start_date'] === null
                    ? __( 'N/A', 'bookly' )
                    : ( $export ? $row['start_date'] : Lib\Utils\DateTime::formatDate( $row['start_date'] ) ),
                'start_time' => $row['start_date'] === null || $export ? '' : Lib\Utils\DateTime::formatTime( $row['start_date'] ),
                'staff' => array(
                    'name' => $row['staff_name'] . ( $row['staff_any'] ? $postfix_any : '' ) . ( $row['staff_visibility'] == 'archive' ? $postfix_archived : '' ),
                ),
                'customer' => array(
                    'full_name' => Lib\Utils\Common::stripWpKses( $row['ca_id'] === null ? __( 'N/A', 'bookly' ) : $row['customer_full_name'] ),
                    'phone' => $row['ca_id'] === null ? '' : Lib\Utils\Common::stripWpKses( $row['customer_phone'] ),
                    'email' => $row['ca_id'] === null ? '' : Lib\Utils\Common::stripWpKses( $row['customer_email'] ),
                    'birthday' => Lib\Utils\Common::stripWpKses( $row['customer_birthday'] ? Lib\Utils\DateTime::formatDate( $row['customer_birthday'] ) : '' ),
                    'address' => Lib\Proxy\Pro::getFullAddressByCustomerData( array(
                        'country' => Lib\Utils\Common::stripWpKses( $row['customer_country'] ),
                        'state' => Lib\Utils\Common::stripWpKses( $row['customer_state'] ),
                        'postcode' => Lib\Utils\Common::stripWpKses( $row['customer_postcode'] ),
                        'city' => Lib\Utils\Common::stripWpKses( $row['customer_city'] ),
                        'street' => Lib\Utils\Common::stripWpKses( $row['customer_street'] ),
                        'street_number' => Lib\Utils\Common::stripWpKses( $row['customer_street_number'] ),
                        'additional_address' => Lib\Utils\Common::stripWpKses( $row['customer_additional_address'] ),
                        'full_address' => Lib\Utils\Common::stripWpKses( $row['customer_full_address'] ),
                    ) ),
                ),
                'service' => array(
                    'title' => $row['service_title'],
                    'duration' => $service_duration,
                    'extras' => $extras,
                    'price' => Lib\Utils\Price::format( $row['service_price'] ),
                ),
                'status' => $row['status'],
                'status_code' => $row['status_code'],
                'location' => $locations_active ? $row['location'] : '',
                'payment' => $payment_title,
                'payment_amount' => $payment_amount,
                'payment_gateway' => $payment_gateway,
                'payment_status' => $payment_status_html,
                'payment_raw_title' => $payment_raw_title,
                'notes' => $row['notes'],
                'number_of_persons' => (int) $row['number_of_persons'],
                'rating' => $row['rating'],
                'rating_comment' => $row['rating_comment'],
                'custom_fields' => $custom_fields,
                'ca_id' => $row['ca_id'],
                'attachment' => $row['attachment'],
                'payment_id' => $row['payment_id'],
                'internal_note' => $row['internal_note'],
                'online_meeting_provider' => $row['online_meeting_provider'],
                'online_meeting_start_url' => $online_meeting_start_url,
                'created_date' => $export ? $row['created_date'] : Lib\Utils\DateTime::formatDate( $row['created_date'] ),
                'created_time' => $export ? '' : Lib\Utils\DateTime::formatTime( $row['created_date'] ),
            );

            $custom_fields = array_map( function() { return ''; }, $custom_fields );
        }

        return compact( 'data', 'total', 'filtered' );
    }
}