<?php
declare(strict_types=1);

namespace App\Controller\Admin;

use App\Controller\AppController;
use Cake\I18n\FrozenTime;
use Cake\Mailer\Mailer;

class BookingManagementController extends AppController
{
    protected $Bookings;

    public function initialize(): void
    {
        parent::initialize();

        $this->viewBuilder()
            ->setLayout('admin')
            ->setLayoutPath('Admin');

        $this->Bookings = $this->fetchTable('Bookings');
        $this->loadComponent('Flash');

        if (!$this->isAdminOrStaff()) {
            throw new \Cake\Http\Exception\ForbiddenException('Staffs only.');
        }
    }

    private function isAdminOrStaff(): bool
    {
        $id = $this->request->getAttribute('identity');
        return (bool)($id && in_array($id->get('role'), ['admin', 'staff'], true));
    }

    public function index()
    {
        $search = trim((string)$this->request->getQuery('q', ''));
        $selectedStatus = trim((string)$this->request->getQuery('status', ''));
        $selectedPickupDate = trim((string)$this->request->getQuery('pickup_date', ''));
        $showArchived = $this->request->getQuery('archived') === '1';

        $bookingsQuery = $this->Bookings->find()
            ->orderBy(['created' => 'DESC']);

        if ($showArchived) {
            $bookingsQuery->where(['archived' => 1]);
        } else {
            $bookingsQuery->where(['archived' => 0]);
        }

        if ($search !== '') {
            $bookingsQuery->where([
                'OR' => [
                    'booking_reference LIKE' => '%' . $search . '%',
                    'shipper_name LIKE' => '%' . $search . '%',
                    'receiver_name LIKE' => '%' . $search . '%',
                    'shipper_address LIKE' => '%' . $search . '%',
                    'delivery_address LIKE' => '%' . $search . '%',
                    'shipper_phone LIKE' => '%' . $search . '%',
                    'receiver_phone LIKE' => '%' . $search . '%',
                ]
            ]);
        }

        if ($selectedStatus !== '') {
            $bookingsQuery->where(['status' => $selectedStatus]);
        }

        if ($selectedPickupDate !== '') {
            $bookingsQuery->where(['pickup_date' => $selectedPickupDate]);
        }

        $bookings = $bookingsQuery->all();

        $statusOptions = [
            'Approved',
            'Rejected',
            'Pending',
            'Confirmed',
            'Picked Up',
            'Collected at Depot',
            'In Transit',
            'Out for Delivery',
            'Delivered',
            'Delivery Failed',
            'Returned',
            'Cancelled',
            'On Hold',
        ];

        $this->set(compact(
            'bookings',
            'statusOptions',
            'selectedStatus',
            'selectedPickupDate',
            'search',
            'showArchived'
        ));
    }

    public function view($id = null)
    {
        $booking = $this->Bookings->find()
            ->where([
                'id' => $id,
            ])
            ->firstOrFail();

        $statusOptions = [
            'Approved',
            'Rejected',
            'Pending',
            'Confirmed',
            'Picked Up',
            'Collected at Depot',
            'In Transit',
            'Out for Delivery',
            'Delivered',
            'Delivery Failed',
            'Returned',
            'Cancelled',
            'On Hold',
        ];

        if ($this->request->is(['patch', 'post', 'put'])) {
            $data = $this->request->getData();

            if (($data['collection_method'] ?? 'Drop Off') === 'Drop Off') {
                $data['collection_method'] = 'Drop Off';
                $data['dropoff_location'] = '1011 Sydney Road, Coburg North, Victoria 3047';
                $data['dropoff_map_link'] = 'https://www.google.com/maps?q=1011+Sydney+Road,+Coburg+North,+Victoria+3047';
                $data['dropoff_note'] = 'Please drop your parcel between 8:00 AM and 4:00 PM.';
                $data['pickup_date'] = null;
                $data['pickup_time'] = null;
            } else {
                $data['collection_method'] = 'Pickup';
                $data['dropoff_location'] = null;
                $data['dropoff_map_link'] = null;
                $data['dropoff_note'] = null;
            }

            $data['last_status_update'] = \Cake\I18n\FrozenTime::now();

            $booking = $this->Bookings->patchEntity($booking, $data);
            if ($this->Bookings->save($booking)) {
                $excludeStatuses = ['Rejected', 'Archived', 'Delivery Failed', 'Cancelled', 'Returned', 'On Hold', ''];
                if (!empty($data['status']) && !in_array($data['status'], $excludeStatuses)) {
                    try {
                        $mailer = new \Cake\Mailer\Mailer('default');
                        $mailer->setEmailFormat('both')
                            ->setTo($booking->shipper_email)
                            ->setSubject('Shipment update: Your parcel ' . $booking->booking_reference)
                            ->viewBuilder()
                            ->setTemplate('status_notification');

                        $mailer->setViewVars([
                            'name' => $booking->shipper_name,
                            'email' => $booking->shipper_email,
                            'booking_ref' => $booking->booking_reference,
                            'status' => $data['status'],
                        ]);

                        $mailer->deliver();
                    } catch (\Exception $e) {
                        \Cake\Log\Log::error("Email delivery failed: " . $e->getMessage());
                    }
                }

                $this->Flash->success('Booking updated successfully.');
                return $this->redirect(['action' => 'view', $booking->id]);
            }

            $this->Flash->error('Unable to update booking.');
        }

        $this->set(compact('booking', 'statusOptions'));
    }

    public function archive($id = null)
    {
        $this->request->allowMethod(['post']);

        $booking = $this->Bookings->get($id);
        $booking->archived = 1;
        $booking->last_status_update = FrozenTime::now();
        $booking->status = 'Archived';

        if ($this->Bookings->save($booking)) {
            $this->Flash->success('Booking archived successfully.');
        } else {
            $this->Flash->error('Could not archive booking.');
        }

        return $this->redirect([
            'prefix' => 'Admin',
            'controller' => 'BookingManagement',
            'action' => 'index'
        ]);
    }

    public function unarchive($id = null)
    {
        $this->request->allowMethod(['post']);

        $booking = $this->Bookings->get($id);
        $booking->archived = 0;
        $booking->last_status_update = FrozenTime::now();

        if ($booking->status === 'Archived') {
            $booking->status = 'Pending';
        }

        if ($this->Bookings->save($booking)) {
            $this->Flash->success('Booking moved back to recent bookings.');
        } else {
            $this->Flash->error('Could not restore booking.');
        }

        return $this->redirect([
            'prefix' => 'Admin',
            'controller' => 'BookingManagement',
            'action' => 'index',
            '?' => ['archived' => 1]
        ]);
    }

    public function delete($id = null)
    {
        $this->request->allowMethod(['post', 'delete']);

        $booking = $this->Bookings->get($id);

        if ($this->Bookings->delete($booking)) {
            $this->Flash->success('Booking deleted successfully.');
        } else {
            $this->Flash->error('Could not delete booking.');
        }

        return $this->redirect([
            'prefix' => 'Admin',
            'controller' => 'BookingManagement',
            'action' => 'index'
        ]);
    }

    public function reject($id = null)
    {
        $this->request->allowMethod(['post','ajax']);
        if (!$id) {
            $id = $this->request->getData('booking_id');
        }
        $bookingId = $id ?: $this->request->getData('booking_id');

        if (!$bookingId) {
            return $this->response
                ->withStatus(400)
                ->withType('application/json')
                ->withStringBody(json_encode(['success' => false, 'message' => 'Invalid ID']));
        }
        $booking = $this->Bookings->get($bookingId);


        $reason = $this->request->getData('reason');

        $booking->last_status_update = FrozenTime::now();
        $booking->status = 'Rejected';

        $mailer = new Mailer('default');
        $mailer
            ->setEmailFormat('both')
            ->setTo($booking->shipper_email)
            ->setSubject('Regarding your booking request: ' . $booking->booking_reference)
            ->viewBuilder()
            ->setTemplate('reject_booking');

        $mailer->setViewVars([
            'name' => $booking->shipper_name,
            'email' => $booking->shipper_email,
            'booking_ref' => $booking->booking_reference,
            'reason' => $reason,
        ]);

        $success = false;
        $message = '';

        if ($mailer->deliver()) {
            if ($this->Bookings->save($booking)) {
                $success = true;
                $message = 'Booking rejected successfully.';
            } else {
                $message = 'Could not update booking status in database.';
            }
        } else {
            $message = 'Failed to send the rejection email.';
        }
        $responseData = [
            'success' => $success,
            'message' => $message
        ];

        return $this->response
            ->withType('application/json')
            ->withStringBody(json_encode($responseData));
    }

    public function approve($id = null)
{
    $this->request->allowMethod(['post']);

    $booking = $this->Bookings->get($id);
    $booking->last_status_update = FrozenTime::now();
    $booking->status = 'Approved';

    if ($this->Bookings->save($booking)) {
        $shipmentsTable = $this->fetchTable('Shipments');

        $existingShipment = $shipmentsTable->find()
            ->where(['booking_id' => $booking->id])
            ->first();

        if (!$existingShipment) {
            $trackingNo = !empty($booking->tracking_number)
                ? $booking->tracking_number
                : 'IC-SHP-' . strtoupper(substr(md5((string)$booking->id . time()), 0, 8));

            $shipment = $shipmentsTable->newEntity([
                'booking_id' => $booking->id,
                'tracking_no' => $trackingNo,
                'shipment_event' => 'Booked',
                'status' => 'Approved',
                'airline' => $booking->assigned_courier ?? null,
                'carrier' => $booking->assigned_courier ?? null,
                'expected_arrived' => $booking->delivery_deadline ?? null,
                'pickup_date' => $booking->pickup_date ?? null,
                'archived' => 0,
            ]);

            $shipmentsTable->save($shipment);
        }

        $this->Flash->success('Booking approved and moved to shipment list.');
    } else {
        $this->Flash->error('Could not approve booking.');
    }

    return $this->redirect([
        'prefix' => 'Admin',
        'controller' => 'BookingManagement',
        'action' => 'index'
    ]);
}
}
