<?php

namespace TbiBank\FusionPay\Test\Unit\Setup;

use Magento\Framework\DB\Adapter\AdapterInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\SchemaSetupInterface;
use PHPUnit\Framework\TestCase;
use TbiBank\FusionPay\Setup\Uninstall;

/**
 * Behaviour under test: TbiBank\FusionPay\Setup\Uninstall::uninstall()
 *
 * CKT-3844 adds a 'sales_order_grid.tbi_status' column (denormalized copy of
 * 'sales_order.tbi_status'). Uninstall must clean up BOTH columns it introduced across the
 * module's lifetime — leaving the grid column behind on uninstall would be a data-integrity
 * leak of a module-specific column into a shared core Magento table.
 *
 * Uninstall::sendNotification() reaches out to an external HTTP endpoint via a $this->curl
 * property that is never assigned anywhere in the class (no constructor, no DI) — calling it
 * would fail with "Call to a member function post() on null". We test uninstall() through a
 * subclass that no-ops sendNotification(), since exercising that unrelated, pre-existing defect
 * is out of scope for this ticket's schema-cleanup behaviour (see report: flagged separately).
 *
 * Given a store where both 'sales_order.tbi_status' and 'sales_order_grid.tbi_status' exist
 * When  the module is uninstalled
 * Then  both columns are dropped, each guarded by a tableColumnExists() check so uninstall is
 *       safe to run even against a partially-migrated store.
 */
class UninstallTest extends TestCase
{
    private function makeUninstallWithoutNotification(): Uninstall
    {
        return new class extends Uninstall {
            protected function sendNotification()
            {
                // no-op: sendNotification()'s HTTP-notify behaviour is unrelated to this
                // ticket's schema cleanup and is pre-existing/unwired (see UninstallTest docblock).
            }
        };
    }

    public function testDropsBothCanonicalAndGridTbiColumnsWhenPresent(): void
    {
        $connection = $this->createMock(AdapterInterface::class);
        $connection->method('tableColumnExists')->willReturn(true);

        $dropped = [];
        $connection->expects($this->exactly(4))
            ->method('dropColumn')
            ->willReturnCallback(function ($table, $column) use (&$dropped) {
                $dropped[] = [$table, $column];
            });

        $setup = $this->createMock(SchemaSetupInterface::class);
        $setup->method('getConnection')->willReturn($connection);
        $setup->method('getTable')->willReturnArgument(0);
        $setup->expects($this->once())->method('startSetup');
        $setup->expects($this->once())->method('endSetup');

        $context = $this->createMock(ModuleContextInterface::class);

        $this->makeUninstallWithoutNotification()->uninstall($setup, $context);

        $this->assertContains(['sales_order', 'tbi_status'], $dropped);
        $this->assertContains(['sales_order', 'tbi_contract_id'], $dropped);
        $this->assertContains(['sales_order_grid', 'tbi_status'], $dropped);
        $this->assertContains(['sales_order_grid', 'tbi_contract_id'], $dropped);
    }

    /**
     * Given neither column exists (e.g. uninstalling a store that never ran the schema patches,
     * or a second uninstall run)
     * When the module is uninstalled
     * Then no dropColumn() call is made for either table — uninstall must be safe/idempotent.
     */
    public function testDropsNeitherColumnWhenAlreadyAbsent(): void
    {
        $connection = $this->createMock(AdapterInterface::class);
        $connection->method('tableColumnExists')->willReturn(false);
        $connection->expects($this->never())->method('dropColumn');

        $setup = $this->createMock(SchemaSetupInterface::class);
        $setup->method('getConnection')->willReturn($connection);
        $setup->method('getTable')->willReturnArgument(0);

        $context = $this->createMock(ModuleContextInterface::class);

        $this->makeUninstallWithoutNotification()->uninstall($setup, $context);
    }

    /**
     * Given only the grid column exists (sales_order.tbi_status was already removed by some
     * other process) — a mixed-state edge case
     * When the module is uninstalled
     * Then only the grid column is dropped; the missing canonical column is never touched.
     */
    public function testDropsOnlyGridColumnsWhenOnlyGridColumnsExist(): void
    {
        $connection = $this->createMock(AdapterInterface::class);
        $connection->method('tableColumnExists')->willReturnMap([
            ['sales_order', 'tbi_status', null, false],
            ['sales_order', 'tbi_contract_id', null, false],
            ['sales_order_grid', 'tbi_status', null, true],
            ['sales_order_grid', 'tbi_contract_id', null, true],
        ]);

        $dropped = [];
        $connection->method('dropColumn')->willReturnCallback(function ($table, $column) use (&$dropped) {
            $dropped[] = [$table, $column];
        });

        $setup = $this->createMock(SchemaSetupInterface::class);
        $setup->method('getConnection')->willReturn($connection);
        $setup->method('getTable')->willReturnArgument(0);

        $context = $this->createMock(ModuleContextInterface::class);

        $this->makeUninstallWithoutNotification()->uninstall($setup, $context);

        $this->assertSame(
            [
                ['sales_order_grid', 'tbi_status'],
                ['sales_order_grid', 'tbi_contract_id'],
            ],
            $dropped
        );
    }
}
