<?php
declare(strict_types=1);

namespace App\Controller;

use Cake\Http\Exception\NotFoundException;
use Cake\Http\Client;
use Cake\Mailer\Mailer;

class BookingController extends AppController
{
    private const DROPOFF_LOCATION = '1011 Sydney Road, Coburg North, Victoria 3047';
    private const DROPOFF_MAP_LINK = 'https://www.google.com/maps?q=1011+Sydney+Road,+Coburg+North,+Victoria+3047';
    private const DROPOFF_NOTE = 'Please drop your parcel between 8:00 AM and 4:00 PM.';

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

        if ($this->components()->has('Authentication')) {
            $this->Authentication->allowUnauthenticated(['index', 'invoice']);
        }
    }

    public function index()
    {
        $bookingsTable = $this->fetchTable('Bookings');
        $booking = $bookingsTable->newEmptyEntity();
        $saveErrorMessage = null;

        if ($this->request->is('get')) {
            $queryData = [
                'shipper_address' => trim((string)$this->request->getQuery('shipper_address')),
                'delivery_address' => trim((string)$this->request->getQuery('delivery_address')),
            ];

            $booking = $bookingsTable->patchEntity($booking, $queryData, [
                'validate' => false,
            ]);
        }

        if ($this->request->is('post')) {
            $data = $this->request->getData();

            $currency = $data['currency'] ?? 'AUD';
            $amount = (float)($data['value_of_goods'] ?? 0);

            if ($currency !== 'AUD' && $amount > 0) {
                $http = new Client();

                try {
                    $response = $http->get('https://api.exconvert.com/convert', [
                        'access_key' => '1f49b8b7-2ae438ef-61e99add-0ed50dc7',
                        'from'       => $currency,
                        'to'         => 'AUD',
                        'amount'     => $amount
                    ]);

                    if ($response->isOk()) {
                        $json = $response->getJson();

                        if (isset($json['result']['AUD'])) {
                            $data['value_of_goods'] = (float)$json['result']['AUD'];
                            $data['currency'] = 'AUD';
                        } else {
                            \Cake\Log\Log::error('FAILED: ' . json_encode($json));
                        }
                    } else {
                        \Cake\Log\Log::error('FAILED: STATUS CODE:' . $response->getStatusCode());
                    }
                } catch (\Exception $e) {
                    \Cake\Log\Log::error('FAILED' . $e->getMessage());
                }
            }

            $specialHandling = $data['special_handling'] ?? [];
            if (is_array($specialHandling)) {
                $data['special_handling'] = json_encode(array_values($specialHandling));
            } else {
                $data['special_handling'] = json_encode([]);
            }

            $data['booking_reference'] = $this->generateBookingReference();

            $collectionMethod = trim((string)($data['collection_method'] ?? 'Pickup'));

            if ($collectionMethod === 'Drop Off') {
                $data['collection_method'] = 'Drop Off';
                $data['dropoff_location'] = self::DROPOFF_LOCATION;
                $data['dropoff_map_link'] = self::DROPOFF_MAP_LINK;
                $data['dropoff_note'] = self::DROPOFF_NOTE;
                $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;
            }

            $booking = $bookingsTable->patchEntity($booking, $data);

            if ($booking->hasErrors()) {
                $saveErrorMessage = 'Please check all required fields and try again.';
            } else {
                if ($bookingsTable->save($booking)) {
                    $decodedSpecialHandling = [];

                    if (!empty($booking->special_handling)) {
                        $decoded = json_decode((string)$booking->special_handling, true);
                        if (is_array($decoded)) {
                            $decodedSpecialHandling = $decoded;
                        }
                    }

                    try {
                        $mailer = new Mailer('default');

                        $mailer->setFrom(['brewhub@u25s2212.iedev.org' => 'Impeccable Couriers'])
                            ->setTo($booking->shipper_email)
                            ->setSubject('Booking Confirmation - ' . $booking->booking_reference)
                            ->setEmailFormat('text')
                            ->setViewVars([
                                'booking' => $booking,
                                'specialHandling' => $decodedSpecialHandling,
                            ]);

                        $mailer->viewBuilder()->setTemplate('booking_confirmation');

                        $mailer->deliver();
                    } catch (\Exception $e) {
                        dd($e->getMessage());
                    }

                    $this->set('booking', $booking);
                    $this->set('specialHandling', $decodedSpecialHandling);

                    return $this->render('invoice');
                }

                $saveErrorMessage = 'Your booking could not be saved. Please try again.';
            }
        }

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

    public function invoice()
    {
        throw new NotFoundException('Please submit a booking form first.');
    }

    private function generateBookingReference(): string
    {
        $bookingsTable = $this->fetchTable('Bookings');

        do {
            $reference = 'IC-' . date('Ymd') . '-' . strtoupper(substr(bin2hex(random_bytes(4)), 0, 8));
            $exists = $bookingsTable->find()
                ->where(['booking_reference' => $reference])
                ->count() > 0;
        } while ($exists);

        return $reference;
    }
}