<?php

namespace App\Tests\Command;

use App\Command\StripeSyncCommand;
use App\Entity\User;
use App\Service\StripeService;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Tester\CommandTester;

class StripeSyncCommandTest extends TestCase
{
    private function createOrganizer(string $stripeId, bool $charges = false, bool $payouts = false): User
    {
        $user = new User();
        $user->setEmail('orga-'.uniqid().'@test.fr');
        $user->setFirstName('Test');
        $user->setLastName('Orga');
        $user->setPassword('hashed');
        $user->setRoles(['ROLE_ORGANIZER']);
        $user->setStripeAccountId($stripeId);
        $user->setStripeChargesEnabled($charges);
        $user->setStripePayoutsEnabled($payouts);
        $user->setCompanyName('Asso Test');

        return $user;
    }

    private function createCommandTester(array $users, StripeService $stripeService): CommandTester
    {
        $repo = $this->createMock(EntityRepository::class);
        $repo->method('findAll')->willReturn($users);

        $em = $this->createMock(EntityManagerInterface::class);
        $em->method('getRepository')->willReturn($repo);

        $command = new StripeSyncCommand($em, $stripeService);
        $app = new Application();
        $app->addCommand($command);

        return new CommandTester($app->find('app:stripe:sync'));
    }

    public function testSyncUpdatesStripeStatus(): void
    {
        $user = $this->createOrganizer('acct_123');

        $stripeService = $this->createMock(StripeService::class);
        $stripeService->method('retrieveAccountStatus')
            ->with('acct_123')
            ->willReturn(['charges_enabled' => true, 'payouts_enabled' => true]);

        $tester = $this->createCommandTester([$user], $stripeService);
        $tester->execute([]);

        self::assertTrue($user->isStripeChargesEnabled());
        self::assertTrue($user->isStripePayoutsEnabled());
        self::assertStringContainsString('1 synced', $tester->getDisplay());
        self::assertSame(0, $tester->getStatusCode());
    }

    public function testSyncDetectsChanges(): void
    {
        $user = $this->createOrganizer('acct_456', true, true);

        $stripeService = $this->createMock(StripeService::class);
        $stripeService->method('retrieveAccountStatus')
            ->willReturn(['charges_enabled' => true, 'payouts_enabled' => false]);

        $tester = $this->createCommandTester([$user], $stripeService);
        $tester->execute([]);

        self::assertTrue($user->isStripeChargesEnabled());
        self::assertFalse($user->isStripePayoutsEnabled());
        self::assertStringContainsString('UPDATED', $tester->getDisplay());
    }

    public function testSyncWithNoOrganizers(): void
    {
        $stripeService = $this->createMock(StripeService::class);

        $tester = $this->createCommandTester([], $stripeService);
        $tester->execute([]);

        self::assertStringContainsString('No organizers', $tester->getDisplay());
        self::assertSame(0, $tester->getStatusCode());
    }

    public function testSyncHandlesStripeError(): void
    {
        $user = $this->createOrganizer('acct_bad');

        $stripeService = $this->createMock(StripeService::class);
        $stripeService->method('retrieveAccountStatus')
            ->willThrowException(new \RuntimeException('Account not found'));

        $tester = $this->createCommandTester([$user], $stripeService);
        $tester->execute([]);

        self::assertStringContainsString('1 error', $tester->getDisplay());
        self::assertSame(1, $tester->getStatusCode());
    }

    public function testSyncSkipsOrganizersWithoutStripeAccount(): void
    {
        $userWithStripe = $this->createOrganizer('acct_ok');

        $userWithoutStripe = new User();
        $userWithoutStripe->setEmail('no-stripe@test.fr');
        $userWithoutStripe->setFirstName('No');
        $userWithoutStripe->setLastName('Stripe');
        $userWithoutStripe->setPassword('hashed');
        $userWithoutStripe->setRoles(['ROLE_ORGANIZER']);

        $stripeService = $this->createMock(StripeService::class);
        $stripeService->method('retrieveAccountStatus')
            ->willReturn(['charges_enabled' => true, 'payouts_enabled' => false]);

        $tester = $this->createCommandTester([$userWithStripe, $userWithoutStripe], $stripeService);
        $tester->execute([]);

        self::assertStringContainsString('Syncing 1 organizer', $tester->getDisplay());
        self::assertStringContainsString('1 synced', $tester->getDisplay());
        self::assertTrue($userWithStripe->isStripeChargesEnabled());
        self::assertFalse($userWithStripe->isStripePayoutsEnabled());
    }
}
