<?php

// Feature: wallet-logs-and-settings, Property 16: Disabled payments rejects all payment attempts
// Feature: wallet-logs-and-settings, Property 17: Payment reducing balance below min_balance is rejected

declare(strict_types=1);

namespace Tests;

use PHPUnit\Framework\TestCase;

require_once __DIR__ . '/../includes/wallet_helpers.php';

/**
 * Tasks 11.2 & 11.3 — Property tests for wallet payment enforcement.
 * Validates: Requirements 9.1, 9.2
 */
class WalletPaymentEnforcementTest extends TestCase
{
    private \mysqli $con;

    protected function setUp(): void
    {
        $host = defined('DB_SERVER') ? DB_SERVER : 'localhost';
        $user = defined('DB_USER')   ? DB_USER   : 'root';
        $pass = defined('DB_PASS')   ? DB_PASS   : '';
        $name = defined('DB_NAME')   ? DB_NAME   : 'sms';

        $this->con = new \mysqli($host, $user, $pass, $name);

        if ($this->con->connect_error) {
            $this->markTestSkipped('Cannot connect to DB: ' . $this->con->connect_error);
        }

        // Ensure wallet_settings table exists with seed rows
        $this->con->query("
            CREATE TABLE IF NOT EXISTS wallet_settings (
                id INT PRIMARY KEY AUTO_INCREMENT,
                setting_key VARCHAR(100) UNIQUE NOT NULL,
                setting_value VARCHAR(255) NOT NULL DEFAULT '',
                label VARCHAR(255) NOT NULL DEFAULT '',
                description TEXT,
                updated_at DATETIME DEFAULT NULL,
                updated_by INT DEFAULT NULL
            )
        ");
        $this->con->query("
            INSERT IGNORE INTO wallet_settings (setting_key, setting_value, label) VALUES
                ('payments_enabled', '1', 'Enable Wallet Payments'),
                ('min_balance', '0', 'Minimum Balance Threshold')
        ");
    }

    protected function tearDown(): void
    {
        // Restore defaults
        $this->con->query("UPDATE wallet_settings SET setting_value='1' WHERE setting_key='payments_enabled'");
        $this->con->query("UPDATE wallet_settings SET setting_value='0' WHERE setting_key='min_balance'");
        $this->con->close();
    }

    // -----------------------------------------------------------------------
    // Property 16: Disabled payments rejects all payment attempts
    // -----------------------------------------------------------------------

    /**
     * @dataProvider disabledPaymentsProvider
     */
    public function testDisabledPaymentsRejectsAllAttempts(float $balance, float $amount): void
    {
        // Disable payments
        $this->con->query("UPDATE wallet_settings SET setting_value='0' WHERE setting_key='payments_enabled'");

        $payments_enabled = (int)getWalletSetting($this->con, 'payments_enabled', 1);
        $this->assertSame(0, $payments_enabled, 'payments_enabled should be 0');

        // Simulate the enforcement check
        $msg = '';
        if (!$payments_enabled) {
            $msg = "danger|Wallet payments are currently disabled by the system administrator.";
        }

        $this->assertStringStartsWith(
            'danger|Wallet payments are currently disabled',
            $msg,
            "Expected rejection message for disabled payments (balance={$balance}, amount={$amount})"
        );
    }

    public static function disabledPaymentsProvider(): array
    {
        // [balance, amount] — various combinations; all should be rejected when disabled
        return array_map(
            fn($i) => [(float)random_int(100, 100000), (float)random_int(1, 100)],
            range(0, 99)
        );
    }

    // -----------------------------------------------------------------------
    // Property 17: Payment reducing balance below min_balance is rejected
    // -----------------------------------------------------------------------

    /**
     * @dataProvider minBalanceViolationProvider
     */
    public function testPaymentBelowMinBalanceIsRejected(float $balance, float $amount, float $min): void
    {
        // Ensure payments are enabled
        $this->con->query("UPDATE wallet_settings SET setting_value='1' WHERE setting_key='payments_enabled'");
        $this->con->query("UPDATE wallet_settings SET setting_value='{$min}' WHERE setting_key='min_balance'");

        $payments_enabled = (int)getWalletSetting($this->con, 'payments_enabled', 1);
        $min_balance      = (float)getWalletSetting($this->con, 'min_balance', 0);

        $msg = '';
        if (!$payments_enabled) {
            $msg = "danger|Wallet payments are currently disabled by the system administrator.";
        } elseif ($amount !== false && ($balance - $amount) < $min_balance) {
            $msg = "danger|This payment would reduce the wallet balance below the minimum threshold of ₦" . number_format($min_balance, 2) . ".";
        }

        $this->assertStringContainsString(
            '₦' . number_format($min, 2),
            $msg,
            "Expected min_balance rejection message (balance={$balance}, amount={$amount}, min={$min})"
        );
        $this->assertStringStartsWith('danger|', $msg);
    }

    public static function minBalanceViolationProvider(): array
    {
        // Generate triples where balance - amount < min (violation)
        $cases = [];
        for ($i = 0; $i < 100; $i++) {
            $min     = (float)random_int(100, 5000);
            $balance = $min + (float)random_int(1, 500);   // balance is slightly above min
            $amount  = $balance - $min + (float)random_int(1, 100); // amount pushes below min
            $cases[] = [$balance, $amount, $min];
        }
        return $cases;
    }

    /**
     * @dataProvider minBalanceRespectedProvider
     */
    public function testPaymentAboveMinBalanceIsAllowed(float $balance, float $amount, float $min): void
    {
        $this->con->query("UPDATE wallet_settings SET setting_value='1' WHERE setting_key='payments_enabled'");
        $this->con->query("UPDATE wallet_settings SET setting_value='{$min}' WHERE setting_key='min_balance'");

        $payments_enabled = (int)getWalletSetting($this->con, 'payments_enabled', 1);
        $min_balance      = (float)getWalletSetting($this->con, 'min_balance', 0);

        $msg = '';
        if (!$payments_enabled) {
            $msg = "danger|Wallet payments are currently disabled by the system administrator.";
        } elseif ($amount !== false && ($balance - $amount) < $min_balance) {
            $msg = "danger|This payment would reduce the wallet balance below the minimum threshold of ₦" . number_format($min_balance, 2) . ".";
        }

        $this->assertEmpty(
            $msg,
            "Expected no enforcement rejection (balance={$balance}, amount={$amount}, min={$min})"
        );
    }

    public static function minBalanceRespectedProvider(): array
    {
        // Generate triples where balance - amount >= min (no violation)
        $cases = [];
        for ($i = 0; $i < 100; $i++) {
            $min     = (float)random_int(0, 1000);
            $amount  = (float)random_int(1, 500);
            $balance = $min + $amount + (float)random_int(0, 500); // always leaves >= min
            $cases[] = [$balance, $amount, $min];
        }
        return $cases;
    }
}
