<?php

// Feature: wallet-logs-and-settings, Property 14: Disabled funding rejects all deposit attempts
// Feature: wallet-logs-and-settings, Property 15: Deposit exceeding max_balance is rejected

declare(strict_types=1);

namespace Tests;

use PHPUnit\Framework\TestCase;

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

/**
 * Tasks 10.2 & 10.3 — Property tests for wallet funding enforcement.
 * Validates: Requirements 8.1, 8.2
 */
class WalletFundingEnforcementTest 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);
        }

        $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
                ('funding_enabled', '1', 'Enable Wallet Funding'),
                ('max_balance', '100000', 'Maximum Wallet Balance')
        ");
    }

    protected function tearDown(): void
    {
        $this->con->query("UPDATE wallet_settings SET setting_value='1' WHERE setting_key='funding_enabled'");
        $this->con->query("UPDATE wallet_settings SET setting_value='100000' WHERE setting_key='max_balance'");
        $this->con->close();
    }

    // -----------------------------------------------------------------------
    // Property 14: Disabled funding rejects all deposit attempts
    // -----------------------------------------------------------------------

    /**
     * @dataProvider disabledFundingProvider
     */
    public function testDisabledFundingRejectsAllDeposits(float $amount): void
    {
        $this->con->query("UPDATE wallet_settings SET setting_value='0' WHERE setting_key='funding_enabled'");

        $funding_enabled = getWalletSetting($this->con, 'funding_enabled', '1');

        $msg = '';
        if ($funding_enabled == '0') {
            $msg = "danger|Wallet funding is currently disabled by the system administrator.";
        }

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

    public static function disabledFundingProvider(): array
    {
        return array_map(fn($i) => [(float)random_int(1, 100000)], range(0, 99));
    }

    // -----------------------------------------------------------------------
    // Property 15: Deposit exceeding max_balance is rejected
    // -----------------------------------------------------------------------

    /**
     * @dataProvider maxBalanceViolationProvider
     */
    public function testDepositExceedingMaxBalanceIsRejected(float $current_balance, float $amount, float $max): void
    {
        $this->con->query("UPDATE wallet_settings SET setting_value='1' WHERE setting_key='funding_enabled'");
        $this->con->query("UPDATE wallet_settings SET setting_value='{$max}' WHERE setting_key='max_balance'");

        $funding_enabled = getWalletSetting($this->con, 'funding_enabled', '1');
        $max_balance     = (float)getWalletSetting($this->con, 'max_balance', 100000);

        $msg = '';
        if ($funding_enabled == '0') {
            $msg = "danger|Wallet funding is currently disabled by the system administrator.";
        } elseif ($current_balance + $amount > $max_balance) {
            $msg = "danger|This deposit would exceed the maximum wallet balance of ₦" . number_format($max_balance, 2) . ".";
        }

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

    public static function maxBalanceViolationProvider(): array
    {
        $cases = [];
        for ($i = 0; $i < 100; $i++) {
            $max             = (float)random_int(1000, 50000);
            $current_balance = (float)random_int(0, (int)$max - 1);
            // amount that pushes over max
            $amount = $max - $current_balance + (float)random_int(1, 1000);
            $cases[] = [$current_balance, $amount, $max];
        }
        return $cases;
    }

    /**
     * @dataProvider maxBalanceRespectedProvider
     */
    public function testDepositWithinMaxBalanceIsAllowed(float $current_balance, float $amount, float $max): void
    {
        $this->con->query("UPDATE wallet_settings SET setting_value='1' WHERE setting_key='funding_enabled'");
        $this->con->query("UPDATE wallet_settings SET setting_value='{$max}' WHERE setting_key='max_balance'");

        $funding_enabled = getWalletSetting($this->con, 'funding_enabled', '1');
        $max_balance     = (float)getWalletSetting($this->con, 'max_balance', 100000);

        $msg = '';
        if ($funding_enabled == '0') {
            $msg = "danger|Wallet funding is currently disabled by the system administrator.";
        } elseif ($current_balance + $amount > $max_balance) {
            $msg = "danger|This deposit would exceed the maximum wallet balance of ₦" . number_format($max_balance, 2) . ".";
        }

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

    public static function maxBalanceRespectedProvider(): array
    {
        $cases = [];
        for ($i = 0; $i < 100; $i++) {
            $max             = (float)random_int(5000, 100000);
            $current_balance = (float)random_int(0, (int)($max / 2));
            // amount that stays within max
            $remaining = $max - $current_balance;
            $amount    = (float)random_int(1, max(1, (int)$remaining));
            $cases[]   = [$current_balance, $amount, $max];
        }
        return $cases;
    }
}
