<?php

namespace TbiBank\FusionPay\Test\Unit\Model;

use Magento\Framework\App\ResourceConnection;
use Magento\Framework\DB\Adapter\AdapterInterface;
use PHPUnit\Framework\TestCase;
use TbiBank\FusionPay\Model\TbiStatusUpdate;

/**
 * Behaviour under test: TbiBank\FusionPay\Model\TbiStatusUpdate::updateStatus()
 *
 * Before CKT-3844, the admin order grid computed tbi_status at read time via a JOIN plugin
 * (Plugin\AddTbiStatusToOrderGridCollection, now deleted). That plugin is replaced by a
 * denormalized `sales_order_grid.tbi_status` column, kept in sync on writes instead of computed
 * on every grid load. TbiStatusUpdate::updateStatus() is the single write path responsible for
 * keeping BOTH the canonical `sales_order` row and the denormalized `sales_order_grid` row in
 * sync — because this update is invoked directly from the Gateway API and bypasses the order
 * model/events entirely, so nothing else would ever propagate the change to the grid table.
 *
 * Given an order identified by its increment id
 * When  updateStatus($orderId, $newStatus) is called
 * Then  BOTH sales_order.tbi_status AND sales_order_grid.tbi_status are updated, matched by
 *       increment_id (not entity_id — a prior version of this method matched on entity_id,
 *       which is wrong because $orderId here is the order's *increment* id coming from the
 *       Gateway payload / order model, not its internal entity id).
 */
class TbiStatusUpdateTest extends TestCase
{
    public function testUpdatesBothCanonicalAndGridTablesKeyedByIncrementId(): void
    {
        $connection = $this->createMock(AdapterInterface::class);
        $resource = $this->createMock(ResourceConnection::class);

        $resource->method('getConnection')->willReturn($connection);
        $resource->method('getTableName')->willReturnMap([
            ['sales_order', 'sales_order'],
            ['sales_order_grid', 'sales_order_grid'],
        ]);

        $calls = [];
        $connection->expects($this->exactly(2))
            ->method('update')
            ->willReturnCallback(function ($table, $data, $where) use (&$calls) {
                $calls[] = [$table, $data, $where];
                return 1;
            });

        $tbiStatusUpdate = new TbiStatusUpdate($resource);

        $tbiStatusUpdate->updateStatus('100000123', 'Approved');

        $this->assertCount(2, $calls);

        // Given/When above — Then: the canonical sales_order row is updated...
        [$table, $data, $where] = $calls[0];
        $this->assertSame('sales_order', $table);
        $this->assertSame(['tbi_status' => 'Approved'], $data);
        $this->assertSame(['increment_id = ?' => 100000123], $where);

        // ...and the denormalized sales_order_grid row is updated with the same data/condition,
        // which is the actual new behaviour this ticket introduces.
        [$table, $data, $where] = $calls[1];
        $this->assertSame('sales_order_grid', $table);
        $this->assertSame(['tbi_status' => 'Approved'], $data);
        $this->assertSame(['increment_id = ?' => 100000123], $where);
    }

    /**
     * Given a non-numeric order id string coming in from an untrusted payload
     * When updateStatus() builds the WHERE clause
     * Then it is coerced to (int) before being used, exactly like the sales_order branch — so
     * both tables stay consistent about what "the order" means, and a stray non-numeric value
     * can never widen the update to unrelated rows.
     */
    public function testCoercesOrderIdToIntForBothTables(): void
    {
        $connection = $this->createMock(AdapterInterface::class);
        $resource = $this->createMock(ResourceConnection::class);
        $resource->method('getConnection')->willReturn($connection);
        $resource->method('getTableName')->willReturnMap([
            ['sales_order', 'sales_order'],
            ['sales_order_grid', 'sales_order_grid'],
        ]);

        $seenWheres = [];
        $connection->method('update')->willReturnCallback(function ($table, $data, $where) use (&$seenWheres) {
            $seenWheres[] = $where;
            return 1;
        });

        (new TbiStatusUpdate($resource))->updateStatus('42abc', 'In Progress');

        $this->assertSame(['increment_id = ?' => 42], $seenWheres[0]);
        $this->assertSame(['increment_id = ?' => 42], $seenWheres[1]);
    }

    public function testAlsoPersistsContractIdWhenProvided(): void
    {
        $connection = $this->createMock(AdapterInterface::class);
        $resource = $this->createMock(ResourceConnection::class);

        $resource->method('getConnection')->willReturn($connection);
        $resource->method('getTableName')->willReturnMap([
            ['sales_order', 'sales_order'],
            ['sales_order_grid', 'sales_order_grid'],
        ]);

        $calls = [];
        $connection->expects($this->exactly(2))
            ->method('update')
            ->willReturnCallback(function ($table, $data, $where) use (&$calls) {
                $calls[] = [$table, $data, $where];
                return 1;
            });

        $tbiStatusUpdate = new TbiStatusUpdate($resource);

        $tbiStatusUpdate->updateStatus('100000125', 'Approved', '20266032281');

        $this->assertSame(
            ['tbi_status' => 'Approved', 'tbi_contract_id' => '20266032281'],
            $calls[0][1]
        );
        $this->assertSame(
            ['tbi_status' => 'Approved', 'tbi_contract_id' => '20266032281'],
            $calls[1][1]
        );
    }
}
