<?php

declare(strict_types=1);

use App\Application\Api\Images\ImageApiController;
use App\Domain\Media\Image;
use App\Domain\Media\ImageRepository;
use App\Framework\Core\ValueObjects\FileSize;
use App\Framework\Core\ValueObjects\Hash;
use App\Framework\Filesystem\ValueObjects\FilePath;
use App\Framework\Http\Exception\NotFound;
use App\Framework\Http\HttpRequest;
use App\Framework\Http\MimeType;
use App\Framework\Http\Responses\JsonResponse;
use App\Framework\Ulid\Ulid;

beforeEach(function () {
    $this->imageRepository = Mockery::mock(ImageRepository::class);
    $this->uploadSecurityService = Mockery::mock();
    $this->imageProcessor = Mockery::mock();
    $this->imageVariantRepository = Mockery::mock();
    $this->ulidGenerator = Mockery::mock();
    $this->pathProvider = Mockery::mock();
    $this->clock = Mockery::mock();

    $this->controller = new ImageApiController(
        $this->imageRepository,
        $this->uploadSecurityService,
        $this->imageProcessor,
        $this->imageVariantRepository,
        $this->ulidGenerator,
        $this->pathProvider,
        $this->clock
    );

    // Test images
    $this->testImages = [
        new Image(
            ulid: Ulid::fromString('01ABCDEFGHIJKLMNOPQRSTUVWX'),
            filename: 'image1.jpg',
            originalFilename: 'original1.jpg',
            mimeType: MimeType::fromString('image/jpeg'),
            fileSize: FileSize::fromBytes(1024),
            width: 800,
            height: 600,
            hash: Hash::fromString('hash1'),
            path: FilePath::create('/test/path'),
            altText: 'Test image 1'
        ),
        new Image(
            ulid: Ulid::fromString('01BCDEFGHIJKLMNOPQRSTUVWY'),
            filename: 'image2.png',
            originalFilename: 'original2.png',
            mimeType: MimeType::fromString('image/png'),
            fileSize: FileSize::fromBytes(2048),
            width: 400,
            height: 300,
            hash: Hash::fromString('hash2'),
            path: FilePath::create('/test/path'),
            altText: 'Test image 2'
        ),
    ];
});

afterEach(function () {
    Mockery::close();
});

it('can get paginated list of images', function () {
    // Arrange
    $request = Mockery::mock(HttpRequest::class);
    $request->queryParams = [
        'limit' => '10',
        'offset' => '0',
    ];

    $this->imageRepository
        ->shouldReceive('findAll')
        ->with(10, 0, null)
        ->andReturn($this->testImages);

    $this->imageRepository
        ->shouldReceive('count')
        ->with(null)
        ->andReturn(2);

    // Act
    $response = $this->controller->getImages($request);

    // Assert
    expect($response)->toBeInstanceOf(JsonResponse::class);

    $data = $response->getData();
    expect($data['images'])->toHaveCount(2);
    expect($data['pagination']['total'])->toBe(2);
    expect($data['pagination']['limit'])->toBe(10);
    expect($data['pagination']['offset'])->toBe(0);
    expect($data['pagination']['has_more'])->toBeFalse();
});

it('can search images with query parameter', function () {
    // Arrange
    $request = Mockery::mock(HttpRequest::class);
    $request->queryParams = [
        'limit' => '50',
        'offset' => '0',
        'search' => 'test',
    ];

    $this->imageRepository
        ->shouldReceive('findAll')
        ->with(50, 0, 'test')
        ->andReturn([$this->testImages[0]]);

    $this->imageRepository
        ->shouldReceive('count')
        ->with('test')
        ->andReturn(1);

    // Act
    $response = $this->controller->getImages($request);

    // Assert
    $data = $response->getData();
    expect($data['images'])->toHaveCount(1);
    expect($data['pagination']['total'])->toBe(1);
});

it('returns correct image URLs pointing to ShowImage controller', function () {
    // Arrange
    $request = Mockery::mock(HttpRequest::class);
    $request->queryParams = [];

    $this->imageRepository
        ->shouldReceive('findAll')
        ->andReturn([$this->testImages[0]]);

    $this->imageRepository
        ->shouldReceive('count')
        ->andReturn(1);

    // Act
    $response = $this->controller->getImages($request);

    // Assert
    $data = $response->getData();
    $image = $data['images'][0];

    expect($image['url'])->toBe('/images/image1.jpg');
    expect($image['thumbnail_url'])->toBe('/images/image1.jpg'); // Same for non-thumbnail
});

it('generates correct thumbnail URLs for original images', function () {
    // Test with original filename
    $originalImage = new Image(
        ulid: Ulid::generate(),
        filename: 'test_original.jpg',
        originalFilename: 'test.jpg',
        mimeType: MimeType::fromString('image/jpeg'),
        fileSize: FileSize::fromBytes(1024),
        width: 800,
        height: 600,
        hash: Hash::fromString('hash'),
        path: FilePath::create('/test'),
        altText: 'Test'
    );

    $request = Mockery::mock(HttpRequest::class);
    $request->queryParams = [];

    $this->imageRepository
        ->shouldReceive('findAll')
        ->andReturn([$originalImage]);

    $this->imageRepository
        ->shouldReceive('count')
        ->andReturn(1);

    $response = $this->controller->getImages($request);
    $data = $response->getData();
    $image = $data['images'][0];

    expect($image['url'])->toBe('/images/test_original.jpg');
    expect($image['thumbnail_url'])->toBe('/images/test_thumbnail.jpg');
});

it('can get single image by ULID', function () {
    // Arrange
    $ulid = '01ABCDEFGHIJKLMNOPQRSTUVWX';
    $testImage = $this->testImages[0];

    $this->imageRepository
        ->shouldReceive('findByUlid')
        ->with($ulid)
        ->andReturn($testImage);

    // Act
    $response = $this->controller->getImage($ulid);

    // Assert
    expect($response)->toBeInstanceOf(JsonResponse::class);

    $data = $response->getData();
    expect($data['ulid'])->toBe($ulid);
    expect($data['filename'])->toBe('image1.jpg');
    expect($data['url'])->toBe('/media/images/test/path/image1.jpg');
});

it('throws NotFound exception when image ULID not found', function () {
    // Arrange
    $ulid = 'NONEXISTENT_ULID_123456789';

    $this->imageRepository
        ->shouldReceive('findByUlid')
        ->with($ulid)
        ->andReturn(null);

    // Act & Assert
    expect(fn () => $this->controller->getImage($ulid))
        ->toThrow(NotFound::class, "Image with ULID {$ulid} not found");
});

it('can search images with advanced parameters', function () {
    // Arrange
    $request = Mockery::mock(HttpRequest::class);
    $request->queryParams = [
        'q' => 'landscape',
        'type' => 'jpeg',
        'min_width' => '800',
        'min_height' => '600',
    ];

    $this->imageRepository
        ->shouldReceive('search')
        ->with('landscape', 'jpeg', 800, 600)
        ->andReturn([$this->testImages[0]]);

    // Act
    $response = $this->controller->searchImages($request);

    // Assert
    expect($response)->toBeInstanceOf(JsonResponse::class);

    $data = $response->getData();
    expect($data['results'])->toHaveCount(1);
    expect($data['count'])->toBe(1);
});

it('handles empty search results', function () {
    // Arrange
    $request = Mockery::mock(HttpRequest::class);
    $request->queryParams = ['q' => 'nonexistent'];

    $this->imageRepository
        ->shouldReceive('search')
        ->andReturn([]);

    // Act
    $response = $this->controller->searchImages($request);

    // Assert
    $data = $response->getData();
    expect($data['results'])->toBeEmpty();
    expect($data['count'])->toBe(0);
});
