<?php

app_require('classes/object_property.php');
app_require('objects/order_bookings_o.php');
app_require('objects/orders_o.php');

use classes\object_property;
use objects\order_bookings_o;
use objects\orders_o;

if (!class_exists('OrderBookingsCompletionOrderDouble')) {
    class OrderBookingsCompletionOrderDouble extends orders_o
    {
        public function __construct()
        {
            $this->id = -1;
            $this->booking_id = new object_property('orders', -1, 'booking_id', 'int', false);
            $this->customer_id = new object_property('orders', -1, 'customer_id', 'int', true);
            $this->department_id = new object_property('orders', -1, 'department_id', 'int', true);
            $this->safety_seal = new object_property('orders', -1, 'safety_seal', 'string', false);
            $this->completed_at = new object_property('orders', -1, 'completed_at', 'timestamp', false);
            $this->customer_id->set(111111);
            $this->department_id->set(10);
        }

        public bool $washCertificateAttached = false;
        public bool $containsWashCertificate = false;

        public function objectChanged(): void
        {
        }

        public function containsWashCertificateItem(): bool
        {
            return $this->containsWashCertificate;
        }

        public function hasWashCertificateAttached(): bool
        {
            return $this->washCertificateAttached;
        }
    }
}

if (!class_exists('OrderBookingsCompletionDouble')) {
    class OrderBookingsCompletionDouble extends order_bookings_o
    {
        public bool $containsWashCertificate = false;
        public bool $orderWasCreated = false;
        public bool $orderItemsWereCreated = false;
        public int $attachCalls = 0;
        public int $sendCalls = 0;
        public orders_o $linkedOrder;

        public function __construct(orders_o $linkedOrder)
        {
            $this->id = -1;
            $this->linkedOrder = $linkedOrder;
            $this->customer_number = new object_property('order_bookings', -1, 'customer_number', 'int', true);
            $this->department = new object_property('order_bookings', -1, 'department', 'int', true);
            $this->order_id = new object_property('order_bookings', -1, 'order_id', 'int', false);
            $this->items = new object_property('order_bookings', -1, 'items', 'json', false);
            $this->customer_number->set(111111);
            $this->department->set(10);
            $this->items->set([]);
        }

        public function createOrderBy(int $user_id): void
        {
            $this->orderWasCreated = true;
            $this->order_id->set(999);
        }

        public function createOrderItemsBy(int $user_id): void
        {
            $this->orderItemsWereCreated = true;
        }

        public function containsWashCertificateItem(): bool
        {
            return $this->containsWashCertificate;
        }

        public function getOrder(): orders_o
        {
            return $this->linkedOrder;
        }

        protected function attachWashCertificate(int $user_id, ?string $safety_seal = null): void
        {
            $this->attachCalls++;
            $this->linkedOrder->washCertificateAttached = true;
        }

        public function sendWashCertificateToCustomer(): void
        {
            $this->sendCalls++;
        }
    }
}

if (!class_exists('OrderBookingsCompletionRefreshDouble')) {
    class OrderBookingsCompletionRefreshDouble extends OrderBookingsCompletionDouble
    {
        public orders_o $refreshedOrder;

        public function __construct(orders_o $initialOrder, orders_o $refreshedOrder)
        {
            parent::__construct($initialOrder);
            $this->refreshedOrder = $refreshedOrder;
        }

        public function getOrder(): orders_o
        {
            return $this->attachCalls > 0 ? $this->refreshedOrder : $this->linkedOrder;
        }

        protected function attachWashCertificate(int $user_id, ?string $safety_seal = null): void
        {
            $this->attachCalls++;
            $this->refreshedOrder->washCertificateAttached = true;
            $this->refreshedOrder->safety_seal->set($safety_seal);
        }
    }
}

it('attaches and emails a wash certificate when a booking is already linked to a matching pos order without one', function (): void {
    $order = new OrderBookingsCompletionOrderDouble();
    $booking = new OrderBookingsCompletionDouble($order);
    $booking->order_id->set(321);
    $booking->containsWashCertificate = true;

    $booking->completeBooking(77, 'LINKED-SEAL');

    expect($order->getSafetySealValue())->toBe('LINKED-SEAL');
    expect($booking->attachCalls)->toBe(1);
    expect($booking->sendCalls)->toBe(1);
});

it('reloads the linked order before deciding whether to email a newly attached wash certificate', function (): void {
    $initialOrder = new OrderBookingsCompletionOrderDouble();
    $refreshedOrder = new OrderBookingsCompletionOrderDouble();
    $booking = new OrderBookingsCompletionRefreshDouble($initialOrder, $refreshedOrder);
    $booking->order_id->set(321);
    $booking->containsWashCertificate = true;

    $booking->completeBooking(77, 'REFRESH-SEAL');

    expect($initialOrder->washCertificateAttached)->toBeFalse();
    expect($refreshedOrder->washCertificateAttached)->toBeTrue();
    expect($refreshedOrder->getSafetySealValue())->toBe('REFRESH-SEAL');
    expect($booking->attachCalls)->toBe(1);
    expect($booking->sendCalls)->toBe(1);
});

it('rejects wash certificate completion when the linked pos order belongs to another booking context', function (): void {
    $order = new OrderBookingsCompletionOrderDouble();
    $order->customer_id->set(222222);
    $order->department_id->set(99);

    $booking = new OrderBookingsCompletionDouble($order);
    $booking->order_id->set(321);
    $booking->containsWashCertificate = true;

    expect(fn() => $booking->completeBooking(77, 'LINKED-SEAL'))
        ->toThrow(Exception::class, 'Linked order does not match booking customer or department');
    expect($order->getSafetySealValue())->toBeNull();
    expect($booking->attachCalls)->toBe(0);
    expect($booking->sendCalls)->toBe(0);
});

it('uses the linked pos order wash certificate item added during mobile completion', function (): void {
    $order = new OrderBookingsCompletionOrderDouble();
    $order->containsWashCertificate = true;
    $booking = new OrderBookingsCompletionDouble($order);
    $booking->order_id->set(321);
    $booking->containsWashCertificate = false;

    $booking->completeBooking(77, 'MOBILE-SEAL');

    expect($order->getSafetySealValue())->toBe('MOBILE-SEAL');
    expect($booking->attachCalls)->toBe(1);
    expect($booking->sendCalls)->toBe(1);
});

it('does not create or email a duplicate wash certificate when a linked pos order already has one', function (): void {
    $order = new OrderBookingsCompletionOrderDouble();
    $order->washCertificateAttached = true;
    $booking = new OrderBookingsCompletionDouble($order);
    $booking->order_id->set(321);
    $booking->containsWashCertificate = true;

    $booking->completeBooking(77, 'LINKED-SEAL');

    expect($order->getSafetySealValue())->toBe('LINKED-SEAL');
    expect($booking->attachCalls)->toBe(0);
    expect($booking->sendCalls)->toBe(0);
});

it('keeps standalone booking completion behavior unchanged for wash certificates', function (): void {
    $order = new OrderBookingsCompletionOrderDouble();
    $booking = new OrderBookingsCompletionDouble($order);
    $booking->containsWashCertificate = true;

    $booking->completeBooking(77, null);

    expect($booking->orderWasCreated)->toBeTrue();
    expect($booking->orderItemsWereCreated)->toBeTrue();
    expect($booking->attachCalls)->toBe(1);
    expect($booking->sendCalls)->toBe(1);
});
