<?php

declare(strict_types=1);

namespace App\Tests\Functional\Controller;

use App\Entity\Device;
use App\Entity\Image;
use App\Entity\RenderedAsset;
use App\Entity\SharedImage;
use App\Enum\DeviceModel;
use App\Enum\Orientation;
use App\Enum\RenderStatus;
use App\Enum\SharedImageStatus;
use App\Service\RotationService;
use App\Tests\Functional\AppWebTestCase;
use Symfony\Component\Messenger\Transport\InMemory\InMemoryTransport;

class SharedImageApiControllerTest extends AppWebTestCase
{
    private function makeImage($user): Image
    {
        $image = (new Image())->setUser($user)->setOriginalFilename('x.jpg')->setStoragePath('x');
        $this->em()->persist($image);
        return $image;
    }

    private function makeShared(Image $image, $recipient, $sharedBy, SharedImageStatus $status = SharedImageStatus::Pending): SharedImage
    {
        $shared = new SharedImage($image, $recipient, $sharedBy);
        $shared->setStatus($status);
        $this->em()->persist($shared);
        return $shared;
    }

    public function test_list_returns_paginated_result_for_recipient(): void
    {
        $sender    = $this->createUser('sender@example.com');
        $recipient = $this->createUser('recip@example.com');
        $image     = $this->makeImage($sender);
        $this->makeShared($image, $recipient, $sender);
        $this->em()->flush();

        $client = $this->loginAs($recipient);
        $client->request('GET', '/api/shared-images');

        $this->assertResponseIsSuccessful();
        $data = json_decode($client->getResponse()->getContent(), true);
        $this->assertArrayHasKey('items', $data);
        $this->assertArrayHasKey('total', $data);
        $this->assertCount(1, $data['items']);
        $this->assertSame($image->getId(), $data['items'][0]['imageId']);
    }

    public function test_list_filters_by_status_pending(): void
    {
        $sender    = $this->createUser('senderf@example.com');
        $recipient = $this->createUser('recipf@example.com');
        $img1      = $this->makeImage($sender);
        $img2      = $this->makeImage($sender);
        $this->makeShared($img1, $recipient, $sender, SharedImageStatus::Pending);
        $this->makeShared($img2, $recipient, $sender, SharedImageStatus::Approved);
        $this->em()->flush();

        $client = $this->loginAs($recipient);
        $client->request('GET', '/api/shared-images?status=pending');

        $this->assertResponseIsSuccessful();
        $data = json_decode($client->getResponse()->getContent(), true);
        $this->assertCount(1, $data['items']);
        $this->assertSame('pending', $data['items'][0]['status']);
    }

    public function test_approve_sets_status_approved(): void
    {
        $sender    = $this->createUser('sendera@example.com');
        $recipient = $this->createUser('recipa@example.com');
        $image     = $this->makeImage($sender);
        $shared    = $this->makeShared($image, $recipient, $sender);
        $this->em()->flush();

        $client = $this->loginAs($recipient);
        $client->request('POST', '/api/shared-images/' . $shared->getId() . '/approve', [], [], [
            'CONTENT_TYPE' => 'application/json',
        ], json_encode(['deviceIds' => []]));

        $this->assertResponseIsSuccessful();
        $data = json_decode($client->getResponse()->getContent(), true);
        $this->assertSame('approved', $data['status']);

        $sharedId = $shared->getId();
        $this->em()->clear();
        $shared = $this->em()->find(SharedImage::class, $sharedId);
        $this->assertSame(SharedImageStatus::Approved, $shared->getStatus());

        // SH-03: verify that render messages were dispatched
        /** @var \Symfony\Component\Messenger\Transport\InMemory\InMemoryTransport $transport */
        $transport = static::getContainer()->get('messenger.transport.image_processing');
        $this->assertGreaterThan(0, count($transport->get()), 'Expected RenderImageMessage(s) to be dispatched after approve');
    }

    public function test_decline_sets_status_declined(): void
    {
        $sender    = $this->createUser('senderd@example.com');
        $recipient = $this->createUser('recipd@example.com');
        $image     = $this->makeImage($sender);
        $shared    = $this->makeShared($image, $recipient, $sender);
        $this->em()->flush();

        $client = $this->loginAs($recipient);
        $client->request('POST', '/api/shared-images/' . $shared->getId() . '/decline');

        $this->assertResponseIsSuccessful();
        $data = json_decode($client->getResponse()->getContent(), true);
        $this->assertSame('declined', $data['status']);

        $sharedId = $shared->getId();
        $this->em()->clear();
        $shared = $this->em()->find(SharedImage::class, $sharedId);
        $this->assertSame(SharedImageStatus::Declined, $shared->getStatus());
    }

    public function test_approve_on_another_users_shared_returns_404(): void
    {
        $sender    = $this->createUser('senderx@example.com');
        $recipient = $this->createUser('recipx@example.com');
        $attacker  = $this->createUser('attack@example.com');
        $image     = $this->makeImage($sender);
        $shared    = $this->makeShared($image, $recipient, $sender);
        $this->em()->flush();

        $client = $this->loginAs($attacker);
        $client->request('POST', '/api/shared-images/' . $shared->getId() . '/approve', [], [], [
            'CONTENT_TYPE' => 'application/json',
        ], json_encode(['deviceIds' => []]));

        $this->assertResponseStatusCodeSame(404);
    }

    public function test_pending_count_returns_count(): void
    {
        $sender    = $this->createUser('pcountsend@example.com');
        $recipient = $this->createUser('pcountrecip@example.com');
        $img1      = $this->makeImage($sender);
        $img2      = $this->makeImage($sender);
        $this->makeShared($img1, $recipient, $sender, SharedImageStatus::Pending);
        $this->makeShared($img2, $recipient, $sender, SharedImageStatus::Approved);
        $this->em()->flush();

        $client = $this->loginAs($recipient);
        $client->request('GET', '/api/shared-images/pending-count');

        $this->assertResponseIsSuccessful();
        $data = json_decode($client->getResponse()->getContent(), true);
        $this->assertSame(1, $data['count']);
    }

    public function test_decline_previously_approved_revokes_device_approvals(): void
    {
        $sender    = $this->createUser('declrevoke_send@example.com');
        $recipient = $this->createUser('declrevoke_recip@example.com');

        $device = new Device();
        $device->setMac('AA:BB:CC:DD:99:01');
        $device->setName('Revoke Frame');
        $device->setUser($recipient);
        $this->em()->persist($device);

        $image = $this->makeImage($sender);
        $image->approveForDevice($device);
        $shared = $this->makeShared($image, $recipient, $sender, SharedImageStatus::Approved);
        $this->em()->flush();

        $imageId    = $image->getId();
        $deviceId   = $device->getId();
        $sharedId   = $shared->getId();

        $client = $this->loginAs($recipient);
        $client->request('POST', '/api/shared-images/' . $sharedId . '/decline');

        $this->assertResponseIsSuccessful();

        $this->em()->clear();
        $imageReloaded  = $this->em()->find(\App\Entity\Image::class, $imageId);
        $deviceReloaded = $this->em()->find(Device::class, $deviceId);
        $this->assertFalse($imageReloaded->isApprovedForDevice($deviceReloaded));
    }

    public function test_approve_with_unowned_device_id_skips_it(): void
    {
        $sender    = $this->createUser('senderunown@example.com');
        $recipient = $this->createUser('recipunown@example.com');
        $other     = $this->createUser('otherunown@example.com');
        $image     = $this->makeImage($sender);
        $shared    = $this->makeShared($image, $recipient, $sender);

        $otherDevice = new Device();
        $otherDevice->setMac('AA:BB:CC:DD:99:88');
        $otherDevice->setName('Other Frame');
        $otherDevice->setUser($other);
        $this->em()->persist($otherDevice);
        $this->em()->flush();

        $client = $this->loginAs($recipient);
        $client->request('POST', '/api/shared-images/' . $shared->getId() . '/approve', [], [], [
            'CONTENT_TYPE' => 'application/json',
        ], json_encode(['deviceIds' => [$otherDevice->getId()]]));

        $this->assertResponseIsSuccessful();
        $this->em()->clear();
        $reloaded = $this->em()->find(SharedImage::class, $shared->getId());
        $this->assertSame(SharedImageStatus::Approved, $reloaded->getStatus());
    }

    public function test_decline_not_found_returns_404(): void
    {
        $user   = $this->createUser('declnotfound@example.com');
        $client = $this->loginAs($user);

        $client->request('POST', '/api/shared-images/99999/decline');

        $this->assertResponseStatusCodeSame(404);
    }

    /**
     * SH-06: After approve + pre-existing ready RenderedAsset, image appears in rotation pool.
     *
     * The approve endpoint dispatches RenderImageMessage but does NOT create RenderedAssets itself.
     * We pre-create a Ready RenderedAsset before the approve call, then verify that after approval
     * the image is approved for the device and RotationService::advance() returns it.
     */
    public function test_approved_image_with_ready_asset_appears_in_rotation(): void
    {
        $sender    = $this->createUser('sendersh6@example.com');
        $recipient = $this->createUser('recipsh6@example.com');

        // Create a device owned by the recipient
        $device = new Device();
        $device->setMac('AA:BB:CC:DD:EE:06')->setName('Test Frame');
        $device->setUser($recipient);
        $this->em()->persist($device);

        // Create the shared image
        $image  = $this->makeImage($sender);
        $shared = $this->makeShared($image, $recipient, $sender);

        // Pre-create a Ready RenderedAsset for the device's model+orientation
        $asset = (new RenderedAsset())
            ->setImage($image)
            ->setDeviceModel($device->getModel())
            ->setOrientation($device->getOrientation())
            ->setStatus(RenderStatus::Ready);
        $this->em()->persist($asset);

        $this->em()->flush();

        // Approve with the device ID so the image is approved for this device
        $client = $this->loginAs($recipient);
        $client->request('POST', '/api/shared-images/' . $shared->getId() . '/approve', [], [], [
            'CONTENT_TYPE' => 'application/json',
        ], json_encode(['deviceIds' => [$device->getId()]]));

        $this->assertResponseIsSuccessful();

        // Reload image and assert device approval
        $this->em()->clear();
        $imageReloaded = $this->em()->find(Image::class, $image->getId());
        $deviceReloaded = $this->em()->find(Device::class, $device->getId());
        $this->assertTrue($imageReloaded->isApprovedForDevice($deviceReloaded), 'Image should be approved for device');

        // Assert image appears in the rotation pool via RotationService::advance()
        $rotationService = static::getContainer()->get(RotationService::class);
        $next = $rotationService->advance($deviceReloaded);
        $this->assertNotNull($next, 'RotationService::advance() should return the approved image');
        $this->assertSame($imageReloaded->getId(), $next->getId());
    }
}
