<?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('OrdersAutoWashCertificateLinkedBookingDouble')) {
    class OrdersAutoWashCertificateLinkedBookingDouble extends order_bookings_o
    {
        public int $send_count = 0;

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

if (!class_exists('OrdersAutoWashCertificateCompletionDouble')) {
    class OrdersAutoWashCertificateCompletionDouble extends orders_o
    {
        public bool $containsWashCertificate = false;
        public bool $washCertificateAttached = false;
        /** @var array<int, array{seal_number: ?string, operator: ?string, date: mixed}> */
        public array $generatedCertificates = [];
        public ?order_bookings_o $linkedBooking = null;

        public function __construct()
        {
            $this->id = -1;
            $this->customer_id = new object_property('orders', -1, 'customer_id', 'int', true);
            $this->cashier_id = new object_property('orders', -1, 'cashier_id', 'int', true);
            $this->reference = new object_property('orders', -1, 'reference', 'string', false);
            $this->notes = new object_property('orders', -1, 'notes', 'string', false);
            $this->department_id = new object_property('orders', -1, 'department_id', 'int', true);
            $this->reg_1 = new object_property('orders', -1, 'reg_1', 'string', false);
            $this->reg_2 = new object_property('orders', -1, 'reg_2', 'string', false);
            $this->reg_3 = new object_property('orders', -1, 'reg_3', 'string', false);
            $this->created_at = new object_property('orders', -1, 'created_at', 'timestamp', false);
            $this->include_in_invoice = new object_property('orders', -1, 'include_in_invoice', 'bool', false);
            $this->completed_at = new object_property('orders', -1, 'completed_at', 'timestamp', false);
            $this->deleted_at = new object_property('orders', -1, 'deleted_at', 'timestamp', false);
            $this->invoice_collection_id = new object_property('orders', -1, 'invoice_collection_id', 'int', false);
            $this->booking_id = new object_property('orders', -1, 'booking_id', 'int', false);
            $this->wash_id = new object_property('orders', -1, 'wash_id', 'string', false);
            $this->lane = new object_property('orders', -1, 'lane', 'string', false);
            $this->po = new object_property('orders', -1, 'po', 'string', false);
            $this->safety_seal = new object_property('orders', -1, 'safety_seal', 'string', false);
            $this->using_hand_held = new object_property('orders', -1, 'using_hand_held', 'bool', false);
        }

        public function objectChanged(): void
        {
        }

        protected function resolveDepartmentIncludedInInvoicing(): bool
        {
            return true;
        }

        public function isPendingHandheld(): bool
        {
            return false;
        }

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

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

        public function generateWashCertificate(string|null $safety_seal = null, string|null $operator = null, $date = null): void
        {
            if ($this->washCertificateAttached) {
                return;
            }

            $this->generatedCertificates[] = [
                'seal_number' => self::normalizeSafetySealValue($safety_seal),
                'operator' => $operator,
                'date' => $date,
            ];
            $this->washCertificateAttached = true;
        }

        public function getOrderBooking(): order_bookings_o|null
        {
            return $this->linkedBooking;
        }
    }
}

it('auto-attaches a wash certificate on order completion when a wash certificate item is present', function (): void {
    $booking = new OrdersAutoWashCertificateLinkedBookingDouble();
    $order = new OrdersAutoWashCertificateCompletionDouble();
    $order->containsWashCertificate = true;
    $order->booking_id->set(123);
    $order->linkedBooking = $booking;
    $order->safety_seal->set('SEAL-41');

    $order->markAsCompleted('Operator One');

    expect($order->completed_at->value())->not->toBeNull();
    expect($order->generatedCertificates)->toHaveCount(1);
    expect($order->generatedCertificates[0])->toMatchArray([
        'seal_number' => 'SEAL-41',
        'operator' => 'Operator One',
    ]);
    expect($booking->send_count)->toBe(1);
});

it('keeps order completion idempotent when a wash certificate is already attached', function (): void {
    $booking = new OrdersAutoWashCertificateLinkedBookingDouble();
    $order = new OrdersAutoWashCertificateCompletionDouble();
    $order->containsWashCertificate = true;
    $order->washCertificateAttached = true;
    $order->booking_id->set(456);
    $order->linkedBooking = $booking;

    $order->markAsCompleted('Operator Two');

    expect($order->generatedCertificates)->toBe([]);
    expect($booking->send_count)->toBe(0);
});

it('allows blank safety seal values when auto-attaching a wash certificate on completion', function (): void {
    $order = new OrdersAutoWashCertificateCompletionDouble();
    $order->containsWashCertificate = true;
    $order->safety_seal->set(null);

    $order->markAsCompleted('Operator Three');

    expect($order->generatedCertificates)->toHaveCount(1);
    expect($order->generatedCertificates[0]['seal_number'])->toBeNull();
});
