<?php

namespace App\Tests\Command;

use App\Command\StripeSyncCommand;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Tester\CommandTester;

class StripeSyncCommandTest extends TestCase
{
    public function testNoStripeKeyConfigured(): void
    {
        $command = new StripeSyncCommand('');
        $tester = new CommandTester($command);
        $tester->execute([]);

        $this->assertSame(0, $tester->getStatusCode());
        $this->assertStringContainsString('Cle Stripe non configuree', $tester->getDisplay());
    }

    public function testTestPlaceholderKey(): void
    {
        $command = new StripeSyncCommand('sk_test_***');
        $tester = new CommandTester($command);
        $tester->execute([]);

        $this->assertSame(0, $tester->getStatusCode());
        $this->assertStringContainsString('Cle Stripe non configuree', $tester->getDisplay());
    }

    public function testSyncWithEmptyResults(): void
    {
        $command = $this->createTestCommand([], [], [], []);
        $tester = new CommandTester($command);
        $tester->execute([]);

        $this->assertSame(0, $tester->getStatusCode());
        $display = $tester->getDisplay();
        $this->assertStringContainsString('0 paiement(s)', $display);
        $this->assertStringContainsString('0 remboursement(s)', $display);
        $this->assertStringContainsString('0 versement(s)', $display);
        $this->assertStringContainsString('0 compte(s) Connect', $display);
        $this->assertStringContainsString('Synchronisation Stripe terminee', $display);
    }

    public function testSyncWithData(): void
    {
        $charge = (object) ['id' => 'ch_123'];
        $refund = (object) ['id' => 're_456'];
        $payout = (object) ['id' => 'po_789'];
        $account = (object) [
            'id' => 'acct_abc',
            'email' => 'test@test.com',
            'charges_enabled' => true,
            'payouts_enabled' => false,
        ];

        $command = $this->createTestCommand([$charge], [$refund], [$payout], [$account]);
        $tester = new CommandTester($command);
        $tester->execute([]);

        $this->assertSame(0, $tester->getStatusCode());
        $display = $tester->getDisplay();
        $this->assertStringContainsString('ch_123', $display);
        $this->assertStringContainsString('re_456', $display);
        $this->assertStringContainsString('po_789', $display);
        $this->assertStringContainsString('acct_abc', $display);
        $this->assertStringContainsString('actif', $display);
        $this->assertStringContainsString('non', $display);
        $this->assertStringContainsString('1 paiement(s)', $display);
        $this->assertStringContainsString('1 remboursement(s)', $display);
        $this->assertStringContainsString('1 versement(s)', $display);
        $this->assertStringContainsString('1 compte(s) Connect', $display);
    }

    public function testSyncAccountWithNoEmail(): void
    {
        $account = (object) [
            'id' => 'acct_noemail',
            'email' => null,
            'charges_enabled' => false,
            'payouts_enabled' => true,
        ];

        $command = $this->createTestCommand([], [], [], [$account]);
        $tester = new CommandTester($command);
        $tester->execute([]);

        $display = $tester->getDisplay();
        $this->assertStringContainsString('N/A', $display);
        $this->assertStringContainsString('inactif', $display);
        $this->assertStringContainsString('oui', $display);
    }

    public function testSyncPaymentsThrows(): void
    {
        $command = $this->createTestCommand(new \RuntimeException('payment error'), [], [], []);
        $tester = new CommandTester($command);
        $tester->execute([]);

        $this->assertStringContainsString('Erreur paiements', $tester->getDisplay());
    }

    public function testSyncRefundsThrows(): void
    {
        $command = $this->createTestCommand([], new \RuntimeException('refund error'), [], []);
        $tester = new CommandTester($command);
        $tester->execute([]);

        $this->assertStringContainsString('Erreur remboursements', $tester->getDisplay());
    }

    public function testSyncPayoutsThrows(): void
    {
        $command = $this->createTestCommand([], [], new \RuntimeException('payout error'), []);
        $tester = new CommandTester($command);
        $tester->execute([]);

        $this->assertStringContainsString('Erreur versements', $tester->getDisplay());
    }

    public function testSyncConnectAccountsThrows(): void
    {
        $command = $this->createTestCommand([], [], [], new \RuntimeException('connect error'));
        $tester = new CommandTester($command);
        $tester->execute([]);

        $this->assertStringContainsString('Erreur comptes Connect', $tester->getDisplay());
    }

    private function createTestCommand(
        array|\Throwable $charges,
        array|\Throwable $refunds,
        array|\Throwable $payouts,
        array|\Throwable $accounts,
    ): StripeSyncCommand {
        return new class('sk_test_real', $charges, $refunds, $payouts, $accounts) extends StripeSyncCommand {
            public function __construct(
                string $key,
                private array|\Throwable $charges,
                private array|\Throwable $refunds,
                private array|\Throwable $payouts,
                private array|\Throwable $accounts,
            ) {
                parent::__construct($key);
            }

            protected function fetchCharges(): iterable
            {
                if ($this->charges instanceof \Throwable) {
                    throw $this->charges;
                }

                return $this->charges;
            }

            protected function fetchRefunds(): iterable
            {
                if ($this->refunds instanceof \Throwable) {
                    throw $this->refunds;
                }

                return $this->refunds;
            }

            protected function fetchPayouts(): iterable
            {
                if ($this->payouts instanceof \Throwable) {
                    throw $this->payouts;
                }

                return $this->payouts;
            }

            protected function fetchConnectAccounts(): iterable
            {
                if ($this->accounts instanceof \Throwable) {
                    throw $this->accounts;
                }

                return $this->accounts;
            }
        };
    }
}
