<?php

app_require('classes/orders_schema_bootstrap.php');
app_require('objects/orders_o.php');

use objects\orders_o;

final class OrdersRegistrationDateRangeDbResultStub
{
    public int $num_rows;

    /** @var array<int, array<string, mixed>> */
    private array $rows;

    /**
     * @param array<int, array<string, mixed>> $rows
     */
    public function __construct(array $rows)
    {
        $this->rows = array_values($rows);
        $this->num_rows = count($this->rows);
    }

    /**
     * @return array<string, mixed>|null
     */
    public function fetch_assoc(): ?array
    {
        if ($this->rows === []) {
            return null;
        }

        return array_shift($this->rows);
    }
}

final class OrdersRegistrationDateRangeDbStub
{
    public string $lastQuery = '';
    public int $queryCalls = 0;

    public function escape_string(string $value): string
    {
        return addslashes($value);
    }

    public function query(string $sql): OrdersRegistrationDateRangeDbResultStub
    {
        $this->queryCalls++;
        $this->lastQuery = $sql;
        return new OrdersRegistrationDateRangeDbResultStub([]);
    }
}

it('applies created_at bounds directly in SQL when filtering orders by registration number', function (): void {
    $dbStub = new OrdersRegistrationDateRangeDbStub();
    $hadDb = array_key_exists('db', $GLOBALS);
    $previousDb = $hadDb ? $GLOBALS['db'] : null;
    $GLOBALS['db'] = $dbStub;

    try {
        $orders = (new orders_o())->getOrdersWithRegistrationNumberInDateRange(
            ' ec21235 ',
            '2025-03-01 00:00:00',
            '2025-04-30 23:59:59'
        );

        expect($orders)->toBe([]);
        expect($dbStub->queryCalls)->toBeGreaterThan(0);
        expect($dbStub->lastQuery)->toContain("UPPER(TRIM(reg_1)) = 'EC21235'");
        expect($dbStub->lastQuery)->toContain("UPPER(TRIM(reg_2)) = 'EC21235'");
        expect($dbStub->lastQuery)->toContain("UPPER(TRIM(reg_3)) = 'EC21235'");
        expect($dbStub->lastQuery)->toContain("created_at BETWEEN '2025-03-01 00:00:00' AND '2025-04-30 23:59:59'");
        expect($dbStub->lastQuery)->toContain('AND deleted_at IS NULL');
        expect($dbStub->lastQuery)->not->toContain('SELECT id, created_at');
    } finally {
        if ($hadDb) {
            $GLOBALS['db'] = $previousDb;
        } else {
            unset($GLOBALS['db']);
        }
    }
});

it('rejects an inverted date range for registration lookups', function (): void {
    $dbStub = new OrdersRegistrationDateRangeDbStub();
    $hadDb = array_key_exists('db', $GLOBALS);
    $previousDb = $hadDb ? $GLOBALS['db'] : null;
    $GLOBALS['db'] = $dbStub;

    try {
        (new orders_o())->getOrdersWithRegistrationNumberInDateRange(
            'EC21235',
            '2025-04-30 23:59:59',
            '2025-03-01 00:00:00'
        );
    } finally {
        if ($hadDb) {
            $GLOBALS['db'] = $previousDb;
        } else {
            unset($GLOBALS['db']);
        }
    }
})->throws(\Exception::class, 'The start date cannot be after the end date');

it('returns early when registration number is blank', function (): void {
    $dbStub = new OrdersRegistrationDateRangeDbStub();
    $hadDb = array_key_exists('db', $GLOBALS);
    $previousDb = $hadDb ? $GLOBALS['db'] : null;
    $GLOBALS['db'] = $dbStub;

    try {
        $orders = (new orders_o())->getOrdersWithRegistrationNumberInDateRange(
            '   ',
            '2025-03-01 00:00:00',
            '2025-04-30 23:59:59'
        );

        expect($orders)->toBe([]);
        expect($dbStub->queryCalls)->toBeGreaterThanOrEqual(0);
    } finally {
        if ($hadDb) {
            $GLOBALS['db'] = $previousDb;
        } else {
            unset($GLOBALS['db']);
        }
    }
});
