<?php

use objects\customer_password_reset_keys_o;

app_require('objects/customer_password_reset_keys_o.php');

if (!class_exists('PasswordResetTokenExpiryFakeResult')) {
    class PasswordResetTokenExpiryFakeResult
    {
        public int $num_rows;

        public function __construct(private readonly array $rows)
        {
            $this->num_rows = count($rows);
        }

        public function fetch_assoc(): ?array
        {
            return $this->rows[0] ?? null;
        }
    }
}

if (!class_exists('PasswordResetTokenExpiryFakeDb')) {
    class PasswordResetTokenExpiryFakeDb
    {
        public array $queries = [];

        public function __construct(private readonly array $results)
        {
        }

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

        public function query(string $sql): PasswordResetTokenExpiryFakeResult
        {
            $this->queries[] = $sql;

            return $this->results[count($this->queries) - 1] ?? new PasswordResetTokenExpiryFakeResult([]);
        }
    }
}

if (!class_exists('PasswordResetTokenExpiryProbe')) {
    class PasswordResetTokenExpiryProbe extends customer_password_reset_keys_o
    {
        public function getObjectProperties(): void
        {
        }

        public function forceSelectedId(int $id): void
        {
            $this->id = $id;
        }
    }
}

beforeEach(function (): void {
    $this->previousDb = $GLOBALS['db'] ?? null;
});

afterEach(function (): void {
    if ($this->previousDb !== null) {
        $GLOBALS['db'] = $this->previousDb;
        return;
    }

    unset($GLOBALS['db']);
});

it('keeps password reset tokens valid for 72 hours', function (): void {
    expect(customer_password_reset_keys_o::TOKEN_EXPIRY_SECONDS)->toBe(72 * 60 * 60);
});

it('looks up reset tokens using the database 72 hour validity window', function (): void {
    $GLOBALS['db'] = new PasswordResetTokenExpiryFakeDb([
        new PasswordResetTokenExpiryFakeResult([['id' => 42]]),
    ]);

    $token = str_repeat('a', customer_password_reset_keys_o::TOKEN_LENGTH);
    $probe = new PasswordResetTokenExpiryProbe();

    $found = $probe->findValidByToken($token);

    expect($found)->toBe($probe)
        ->and($probe->id)->toBe(42)
        ->and($GLOBALS['db']->queries[0])->toContain('created_at >= DATE_SUB(NOW(), INTERVAL 259200 SECOND)')
        ->and($GLOBALS['db']->queries[0])->not->toContain("DATE_SUB('");
});

it('uses the same database 72 hour window for the selected token guard', function (): void {
    $GLOBALS['db'] = new PasswordResetTokenExpiryFakeDb([
        new PasswordResetTokenExpiryFakeResult([['id' => 42]]),
    ]);

    $probe = new PasswordResetTokenExpiryProbe();
    $probe->forceSelectedId(42);

    expect($probe->isValidToken())->toBeTrue()
        ->and($GLOBALS['db']->queries[0])->toContain('id = 42')
        ->and($GLOBALS['db']->queries[0])->toContain('created_at >= DATE_SUB(NOW(), INTERVAL 259200 SECOND)');
});
