<?php

namespace App\Tests\Command;

use App\Command\CleanPendingDeleteCommand;
use App\Entity\Customer;
use App\Entity\User;
use App\Repository\CustomerRepository;
use App\Service\MeilisearchService;
use Doctrine\ORM\EntityManagerInterface;
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerInterface;
use Symfony\Component\Console\Tester\CommandTester;

class CleanPendingDeleteCommandTest extends TestCase
{
    private CustomerRepository $customerRepository;
    private EntityManagerInterface $em;
    private MeilisearchService $meilisearch;
    private LoggerInterface $logger;
    private string $stripeSecretKey = '';

    protected function setUp(): void
    {
        $this->customerRepository = $this->createStub(CustomerRepository::class);
        $this->em = $this->createStub(EntityManagerInterface::class);
        $this->meilisearch = $this->createStub(MeilisearchService::class);
        $this->logger = $this->createStub(LoggerInterface::class);
    }

    private function makeCommand(string $stripeKey = ''): CleanPendingDeleteCommand
    {
        return new CleanPendingDeleteCommand(
            $this->customerRepository,
            $this->em,
            $this->meilisearch,
            $this->logger,
            $stripeKey,
        );
    }

    private function execute(string $stripeKey = ''): CommandTester
    {
        $tester = new CommandTester($this->makeCommand($stripeKey));
        $tester->execute([]);

        return $tester;
    }

    private function makeCustomer(string $name = 'John Doe', string $email = 'john@example.com', ?string $stripeId = null): Customer
    {
        $user = $this->createStub(User::class);

        $customer = $this->createStub(Customer::class);
        $customer->method('getFullName')->willReturn($name);
        $customer->method('getEmail')->willReturn($email);
        $customer->method('getUser')->willReturn($user);
        $customer->method('getId')->willReturn(42);
        $customer->method('getStripeCustomerId')->willReturn($stripeId);

        return $customer;
    }

    // ------------------------------------------------------------------
    // No pending_delete customers → SUCCESS immediately
    // ------------------------------------------------------------------

    public function testNoPendingDeleteCustomers(): void
    {
        $this->customerRepository->method('findBy')->willReturn([]);

        $tester = $this->execute();

        $this->assertSame(0, $tester->getStatusCode());
        $this->assertStringContainsString('Aucun client en attente de suppression', $tester->getDisplay());
    }

    // ------------------------------------------------------------------
    // One customer deleted with no Stripe key and no Meilisearch error
    // ------------------------------------------------------------------

    public function testOneCustomerDeletedSuccessfully(): void
    {
        $customer = $this->makeCustomer();
        $this->customerRepository->method('findBy')->willReturn([$customer]);

        $em = $this->createMock(EntityManagerInterface::class);
        $em->expects($this->exactly(2))->method('remove');
        $em->expects($this->once())->method('flush');
        $this->em = $em;

        $tester = $this->execute();

        $this->assertSame(0, $tester->getStatusCode());
        $this->assertStringContainsString('1 client(s) supprime(s)', $tester->getDisplay());
    }

    // ------------------------------------------------------------------
    // Multiple customers deleted
    // ------------------------------------------------------------------

    public function testMultipleCustomersDeleted(): void
    {
        $c1 = $this->makeCustomer('Alice', 'alice@example.com');
        $c2 = $this->makeCustomer('Bob', 'bob@example.com');
        $this->customerRepository->method('findBy')->willReturn([$c1, $c2]);

        $tester = $this->execute();

        $this->assertSame(0, $tester->getStatusCode());
        $this->assertStringContainsString('2 client(s) supprime(s)', $tester->getDisplay());
    }

    // ------------------------------------------------------------------
    // Meilisearch error during delete → logged as warning, continues
    // ------------------------------------------------------------------

    public function testMeilisearchErrorIsLoggedAndContinues(): void
    {
        $customer = $this->makeCustomer();
        $this->customerRepository->method('findBy')->willReturn([$customer]);

        $meilisearch = $this->createStub(MeilisearchService::class);
        $meilisearch->method('removeCustomer')->willThrowException(new \RuntimeException('Meili down'));
        $this->meilisearch = $meilisearch;

        $logger = $this->createMock(LoggerInterface::class);
        $logger->expects($this->atLeastOnce())
            ->method('warning')
            ->with($this->stringContains('Meilisearch'));
        $this->logger = $logger;

        $tester = $this->execute();

        // Command must still succeed and delete the customer
        $this->assertSame(0, $tester->getStatusCode());
        $this->assertStringContainsString('1 client(s) supprime(s)', $tester->getDisplay());
    }

    // ------------------------------------------------------------------
    // deleteFromStripe: empty SK → skipped (no Stripe call, returns early)
    // ------------------------------------------------------------------

    public function testDeleteFromStripeSkipsOnEmptyKey(): void
    {
        $customer = $this->makeCustomer('Alice', 'alice@example.com', 'cus_123');
        $this->customerRepository->method('findBy')->willReturn([$customer]);

        // Empty string key — guard must skip before reaching Stripe API
        $tester = $this->execute('');

        $this->assertSame(0, $tester->getStatusCode());
    }

    // ------------------------------------------------------------------
    // deleteFromStripe: test placeholder SK → skipped
    // ------------------------------------------------------------------

    public function testDeleteFromStripeSkipsOnTestKey(): void
    {
        $customer = $this->makeCustomer('Alice', 'alice@example.com', 'cus_123');
        $this->customerRepository->method('findBy')->willReturn([$customer]);

        $tester = $this->execute('sk_test_***');

        $this->assertSame(0, $tester->getStatusCode());
    }

    // ------------------------------------------------------------------
    // deleteFromStripe: null stripeCustomerId → skipped
    // ------------------------------------------------------------------

    public function testDeleteFromStripeSkipsOnNullStripeCustomerId(): void
    {
        // stripeCustomerId is null → guard returns early even with a real-looking key
        $customer = $this->makeCustomer('Alice', 'alice@example.com', null);
        $this->customerRepository->method('findBy')->willReturn([$customer]);

        // Use a key that passes the first guard but hits the second guard (null id)
        $tester = $this->execute('sk_live_real_but_null_id');

        $this->assertSame(0, $tester->getStatusCode());
    }

    // ------------------------------------------------------------------
    // logger->info is called for each deleted customer
    // ------------------------------------------------------------------

    public function testLoggerInfoCalledForDeletedCustomer(): void
    {
        $customer = $this->makeCustomer('Alice', 'alice@example.com');
        $this->customerRepository->method('findBy')->willReturn([$customer]);

        $logger = $this->createMock(LoggerInterface::class);
        $logger->expects($this->once())
            ->method('info')
            ->with($this->stringContains('alice@example.com'));
        $this->logger = $logger;

        $tester = $this->execute();

        $this->assertSame(0, $tester->getStatusCode());
    }
}
