<?php

namespace routes;

use classes\authentication;
use classes\pdf_store;
use classes\response;
use classes\wash_certificate_store;
use objects\bookings_o;
use objects\departments_o;
use objects\logs_o;
use objects\order_bookings_o;
use traits\route_t;

class bookingsRoute
{
    use route_t;

    public function run(): void
    {
        /** All bookings */
        $this->get('/bookings', function () {
            // Require the user to be logged in
            global
            /** @var response $response */
            $EMAIL_WASH_CERTIFICATE_TOKEN,
            $response;
            $this->requirePermission('list_bookings');
            // Get the user object
            $user = (new authentication())->get_user();
            // Check if the request was successful
            if ($user) {
                // Log the incident
                (new logs_o())->add('bookings', 'global', 1, $user->id, 'LIST_BOOKINGS', 'Successfully listed bookings');
                // If the user has the permission to issue wash certificates, add the wash certificate key to the response
                if ($user->hasPermission('issue_wash_certificates')) {
                    $response->add_meta('wash_certificate_token', $EMAIL_WASH_CERTIFICATE_TOKEN);
                }
                $bookings_o = new bookings_o();
                // Check if the user has specified a booking id
                if (self::isParametersSet(['id'])) {
                    // Check if the booking id is a number
                    if (!is_numeric(self::getParameter('id'))) {
                        $response->error('Booking ID must be a number', 400);
                    }
                    // Check if the booking exists
                    if (!$bookings_o->select((int)self::getParameter('id'))->exists()) {
                        $response->error('Booking not found', 404);
                    }
                    // Check if the user has access to the booking
                    if (!$user->hasAccessToBooking((int)self::getParameter('id'))) {
                        $response->error('You are not allowed to access this booking', 403);
                    }
                    // Return the booking
                    $response->success(
                        $bookings_o->asArray()
                    );
                }
                // Return the list of bookings
                $response->success(
                    $bookings_o->parseBookings($bookings_o->listObjectsWithPaginationIfSet(
                        function ($booking) {
                            return (new bookings_o())->select($booking['id'])->asArray(
                                [
                                    'include_parsed_services' => true,
                                ]
                            );
                        },
                        $bookings_o->forceRestrictFilters(
                            [
                                'department' => $user->getGroup()->getDepartments(),
                            ]
                        )
                    ))
                );
            } else {
                // Log the incident
                (new logs_o())->add('bookings', 'global', 1, 0, 'LIST_BOOKINGS', 'No user found, or invalid session');
                // Return an error
                $response->error('Invalid session', 400);
            }
        },
            [
                'list_bookings' => 'List all bookings',
                'issue_wash_certificates' => 'When set, the response will include the wash certificate token for sending wash certificates'
            ]
        );
        /** Own bookings */
        $this->get('/user/bookings', function () {
            // Require the user to be logged in
            global /** @var response $response */
            $response;
            $this->requirePermission('list_own_bookings');
            // Get the user object
            $user = (new authentication())->get_user();
            // Check if the request was successful
            if ($user) {
                // Log the incident
                (new logs_o())->add('bookings', 'global', 1, $user->id, 'LIST_OWN_BOOKINGS', 'Successfully listed own bookings');
                // Return the list of departments
                $bookings_o = new bookings_o();
                $response->success(
                    $bookings_o->parseBookings($bookings_o->listObjectsWithPaginationIfSet(
                        function ($booking) {
                            return (new bookings_o())->select($booking['id'])->asArray(
                                [
                                    'include_parsed_services' => true,
                                ]
                            );
                        },
                        $bookings_o->forceRestrictFilters(
                            [
                                'customer_number' => [
                                    $user->customer_number->value(),
                                ],
                            ]
                        )
                    ))
                );
            } else {
                // Log the incident
                (new logs_o())->add('bookings', 'global', 1, 0, 'LIST_OWN_BOOKINGS', 'No user found, or invalid session');
                // Return an error
                $response->error('Invalid session', 400);
            }
        },
            [
                'list_own_bookings' => 'List all bookings for the logged in user'
            ]
        );

        $this->put('/bookings', function () {
            // Require the user to be logged in
            global /** @var response $response */
            $response;
            $this->requirePermission('list_own_bookings');
            // Get the user object
            $user = (new authentication())->get_user();
            // Check if the request was successful
            if ($user) {
                // Log the incident
                (new logs_o())->add('bookings', 'global', 1, $user->id, 'UPDATE_BOOKING', 'Successfully updated booking');
                // Require the booking id parameter
                self::requireParameters([
                    'id'
                ]);
                // Check if the booking id is a number
                self::requireType((int)$this->getParameter('id'), self::type_int());
                self::requireMinValue((int)$this->getParameter('id'), 1);
                self::requireSameLength(
                    (int)$this->getParameter('id'),
                    $this->getParameter('id'),
                );
                // Check if the booking exists
                $booking = (new bookings_o())->select((int)$this->getParameter('id'));
                if (!$booking->exists()) {
                    $response->error('Booking not found', 404);
                }
                // Check if the user has access to the booking
                if (!$user->hasAccessToBooking((int)$this->getParameter('id'))) {
                    $response->error('You are not allowed to update this booking', 403);
                }
                // Check if the optional parameters are set
                if (self::isParametersSet(['reference_number'])) {
                    // Check if the reference number is a string
                    self::requireType(
                        (string)$this->getParameter('reference_number'),
                        self::type_string()
                    );
                    self::requireMinLength(
                        'reference_number',
                        0
                    );
                    self::requireMaxLength(
                        'reference_number',
                        255
                    );
                    $booking->reference_number->set(
                        (string)$this->getParameter('reference_number')
                    );
                }
                // Return the booking
                $response->success(
                    $booking->asArray()
                );
            } else {
                // Log the incident
                (new logs_o())->add('bookings', 'global', 1, 0, 'ADD_BOOKING', 'No user found, or invalid session');
                // Return an error
                $response->error('Invalid session', 400);
            }
        },
            [
                'add_booking' => 'Add a new booking'
            ]
        );
        // Synchronize booking from the external system
        $this->post('/admin/bookings/sync', function () {
            // Require the user to be logged in
            global $response;
            if ($this->fromRequest('auth_key') !== 'earm8BX4MFTgS6JCNQdqW5EzHUutv2Vx')
                $this->requirePermission('sync_bookings');
            // Check if the request was successful
            $booking = [
                'id' => $this->fromRequest('id'),
                'customer_number' => $this->fromRequest('customer_number'),
                'wash_type' => $this->fromRequest('wash_type'),
                'contact_email' => $this->fromRequest('contact_email'),
                'reference_number' => $this->fromRequest('reference_number'),
                'regNrTraekker' => $this->fromRequest('regNrTraekker'),
                'regNrTrailer' => $this->fromRequest('regNrTrailer'),
                'washCertificateEmail' => $this->fromRequest('washCertificateEmail'),
                'date' => $this->fromRequest('date'),
                'department' => $this->fromRequest('department'),
                'pickup_bool' => $this->fromRequest('pickup_bool'),
                'notes' => $this->fromRequest('notes'),
                'washCertificateStatus' => $this->fromRequest('washCertificateStatus'),
                'washCertificateUrl' => $this->fromRequest('washCertificateUrl'),
                'status' => $this->fromRequest('status'),
            ];
            // Log the incident
            (new logs_o())->add('bookings', 'global', 1, 0, 'SYNC_BOOKINGS', 'Successfully synced bookings');
            // Add the booking, if it doesn't exist, update it if it does
            (new bookings_o())->addOrUpdate(
                (int)$booking['id'],
                (int)$booking['customer_number'],
                (string)$booking['wash_type'],
                (string)$booking['contact_email'],
                (string)$booking['reference_number'],
                (string)$booking['regNrTraekker'],
                (string)$booking['regNrTrailer'],
                (string)$booking['washCertificateEmail'],
                (string)$booking['date'],
                (string)$booking['department'],
                (string)$booking['pickup_bool'],
                (string)$booking['notes'],
                (string)$booking['washCertificateStatus'],
                (string)$booking['washCertificateUrl'],
                (string)$booking['status']
            );
            $response->success(
                ['message' => 'Successfully synced booking']
            );
        },
            [
                'sync_bookings' => 'Sync bookings from the external system NOTE: This permission is only required if the auth_key is not set'
            ]
        );

        // Get a departments unfulfilled bookings (count) for the day
        $this->get('/admin/bookings/department/count', function () {
            // Require the user to be logged in
            global /** @var response $response */
            $response;
            $this->requirePermission('list_department_bookings_count');
            // Get the user object
            $user = (new authentication())->get_user();
            // Check if the request was successful
            if ($user) {
                // Check if the department_id is set
                if ($this->fromRequest('department_id') === null) {
                    $response->error('Department ID is required', 400);
                }
                // Check if the department id is a valid number
                if (!is_numeric($this->fromRequest('department_id'))) {
                    $response->error('Department ID must be a number', 400);
                }
                // Check if the result is cached, if so, we don't need to query the database
                // Check if the department exists
                if (!(new departments_o())->selectId((int)$this->fromRequest('department_id'))->exists()) {
                    $response->error('Department not found', 404);
                }
                // Log the incident
                (new logs_o())->add('bookings', 'global', 1, $user->id, 'LIST_DEPARTMENT_BOOKINGS_COUNT', 'Successfully listed department bookings');
                // Return the list of departments
                $response->success(
                    (int)(new order_bookings_o())->getDailyUnfulfilledBookingsCountForDepartment((int)$this->fromRequest('department_id'))
                );
            } else {
                // Log the incident
                (new logs_o())->add('bookings', 'global', 1, 0, 'LIST_DEPARTMENT_BOOKINGS_COUNT', 'No user found, or invalid session');
                // Return an error
                $response->error('Invalid session', 400);
            }
        },
            [
                'list_department_bookings_count' => 'List the unfulfilled bookings count for a department'
            ]
        );

        $this->post('/user/bookings/washcertificate/download', function () {
            // Require the user to be logged in
            global /** @var response $response */
            $response;
            // Check if the user has access to the department
            $this->requirePermission('download_own_wash_certificate');
            // Get the user object
            $user = (new authentication())->get_user();
            // Check if the request was successful
            if (!$user->exists()) {
                $response->error('User not found', 400);
            }
            // Check if the required fields are set
            $id = $response->getRequestParameter('id');
            // Make sure the id is a number
            if (!is_numeric($id)) {
                $response->error('id parameter must be a number got: ' . $id, 400);
            }
            // Make sure the user is allowed to download the wash certificate
            if (!$user->hasAccessToBooking($id)) {
                $response->error('You are not allowed to download this wash certificate', 400);
            }
            // Check if the booking is completed
            if (!(new bookings_o())->select($id)->exists()) {
                $response->error('Booking not found', 404);
            }
            $bookings_new = (new bookings_o())->select($id);
            $wash_certificate_status = $bookings_new->washCertificateStatus->value();
            switch ($wash_certificate_status) {
                case 'pending':
                    $response->error('Wash certificate has not been issued yet', 400);
                case 'completed':
                    // The wash certificate has been issued, so we can proceed
                    break;
                case 'cancelled':
                    $response->error('Wash certificate is cancelled', 400);
                default:
                    $response->error('Wash certificate status is unknown', 500);
            }
            // Get the wash certificate object
            //$wash_certificate_object = $bookings_new->washCertificateUrl->value();
            // Create the connection
            $pdf_storage = new wash_certificate_store();
            // Check if the wash certificate exists.
            if (!$pdf_storage->washCertificateExists($bookings_new->id)) {
                $response->error('Wash certificate not found, but it should exist', 500);
            }
            // Generate the download link
            $response->success(
                ["link" => $pdf_storage->getWashCertificateDownload($bookings_new->id)]
            );
        },
            [
                'download_own_wash_certificate' => 'Download the wash certificate for a booking'
            ]
        );

        $this->get('/bookings/download_pdf', function () {
            // Require the user to be logged in
            global /** @var response $response */
            $response;
            // Check if the user has access to the department
            $this->requirePermission('download_own_wash_certificate');
            // Get the user object
            $user = (new authentication())->get_user();
            // Check if the request was successful
            if (!$user->exists()) {
                $response->error('User not found', 400);
            }
            self::requireParameters(['id']);
            $id = (int)$this->getParameter('id');
            self::requireType(
                $id,
                self::type_int()
            );
            self::requireMinValue($id, 1);
            self::requireSameLength(
                $id,
                self::getParameter('id'),
            );
            // Make sure the user is allowed to download the wash certificate
            if (!$user->hasAccessToBooking($id)) {
                $response->error('You are not allowed to download this wash certificate', 400);
            }
            $bookings = (new bookings_o())->select((int)$id);
            if (!$bookings->exists()) {
                $response->error('Booking not found', 404);
            }
            // Check if the booking has a wash certificate
            if (!empty($bookings->wash_certificate_pdf->value())) {
                // The booking has a wash certificate, so we can proceed
                $pdf_storage = new pdf_store();
                // Check if the wash certificate exists.
                if (!$pdf_storage->doesObjectExist($bookings->wash_certificate_pdf->value())) {
                    $response->error('Wash certificate not found, but it should exist', 500);
                }
                // Generate the download link
                $response->success(
                    ["link" => $pdf_storage->getPresignedUrl($bookings->wash_certificate_pdf->value())]
                );
            } else {
                // Check if the certificate is stored in the other bucket
                $wash_certificate_storage = new wash_certificate_store();
                // Check if the wash certificate exists.
                if (!$wash_certificate_storage->washCertificateExists($bookings->id)) {
                    $response->error('No wash certificate found for this booking', 404);
                }
                // Generate the download link
                $response->success(
                    ["link" => $wash_certificate_storage->getWashCertificateDownload($bookings->id)]
                );
            }
        },
            [
                'download_own_wash_certificate' => 'Download the wash certificate for a booking'
            ]
        );

        $this->post('/admin/bookings/delete', function () {
            // Require the user to be logged in
            global /** @var response $response */
            $response;
            $this->requirePermission('delete_booking');
            // Get the user object
            $user = (new authentication())->get_user();
            // Check if the request was successful
            if (!$user->exists()) {
                $response->error('User not found', 400);
            }
            // Check if the required fields are set
            $id = $response->getRequestParameter('id');
            // Make sure the id is a number
            if (!is_numeric($id)) {
                $response->error('id parameter must be a number got: ' . $id, 400);
            }
            // Make sure the user is allowed to delete the booking
            if (!$user->hasAccessToBooking($id)) {
                $response->error('You are not allowed to delete this booking', 400);
            }
            // Delete the booking
            (new bookings_o())->delete($id);
            // Return success
            $response->success(
                ["message" => "Booking deleted"]
            );
        },
            [
                'delete_booking' => 'Delete a booking'
            ]
        );

        $this->post('/superuser/bookings/sync/all', function () {
            // Require the user to be logged in
            global /** @var response $response */
            $response;
            $this->requirePermission('sync_all_bookings');
            // Get the user object
            $user = (new authentication())->get_user();
            // Check if the request was successful
            if (!$user->exists()) {
                $response->error('User not found', 400);
            }
            // Log the incident
            (new logs_o())->add('bookings', 'global', 1, $user->id, 'SYNC_ALL_BOOKINGS', 'Successfully synced all bookings');
            // Sync all bookings
            (new bookings_o())->syncBookings();
            // Return success
            $response->success(
                ["message" => "All bookings synced"]
            );
        },
            [
                'sync_all_bookings' => 'Sync all bookings from the external system'
            ]
        );

        $this->post('/admin/bookings/completeWashWithoutWashCertificate', function () {
            // Require the user to be logged in
            global /** @var response $response */
            $response;
            $this->requirePermission('complete_wash_without_wash_certificate');
            // Get the user object
            $user = (new authentication())->get_user();
            // Check if the request was successful
            if (!$user->exists()) {
                $response->error('User not found', 400);
            }
            // Check if the required fields are set
            $id = $response->getRequestParameter('id');
            // Make sure the id is a number
            if (!is_numeric($id)) {
                $response->error('id parameter must be a number got: ' . $id, 400);
            }
            // Make sure the user is allowed to complete the wash without a wash certificate
            if (!$user->hasAccessToBooking($id)) {
                $response->error('You are not allowed to complete this wash without a wash certificate', 400);
            }
            // Complete the wash without a wash certificate
            (new bookings_o())->completeWashWithoutWashCertificate($id);
            // Return success
            $response->success(
                ["message" => "Wash completed without wash certificate"]
            );
        },
            [
                'complete_wash_without_wash_certificate' => 'Complete a wash without a wash certificate'
            ]
        );

        $this->post('/user/bookings/delete', function () {
            // Require the user to be logged in
            global /** @var response $response */
            $response;
            $this->requirePermission('delete_own_booking');
            // Get the user object
            $user = (new authentication())->get_user();
            // Check if the request was successful
            if (!$user->exists()) {
                $response->error('User not found', 400);
            }
            // Check if the required fields are set
            $id = $response->getRequestParameter('id');
            // Make sure the id is a number
            if (!is_numeric($id)) {
                $response->error('id parameter must be a number got: ' . $id, 400);
            }
            // Make sure the user is allowed to delete the booking
            if (!$user->hasAccessToBooking($id)) {
                $response->error('You are not allowed to delete this booking', 400);
            }
            // Delete the booking
            (new bookings_o())->delete($id);
            // Return success
            $response->success(
                ["message" => "Booking deleted"]
            );
        },
            [
                'delete_own_booking' => 'Delete the users own booking'
            ]
        );
    }
}