<?php

declare(strict_types=1);

namespace App\Application\Api\Images;

use App\Domain\Media\ImageRepository;
use App\Framework\Attributes\Route;
use App\Framework\Exception\ErrorCode;
use App\Framework\Exception\FrameworkException;
use App\Framework\Http\Exception\NotFound;
use App\Framework\Http\HttpRequest;
use App\Framework\Http\Method;
use App\Framework\Http\Responses\JsonResponse;
use App\Framework\Http\Status;

final readonly class UpdateImageController
{
    public function __construct(
        private ImageRepository $imageRepository
    ) {
    }

    #[Route(path: '/api/images/{ulid}', method: Method::PUT)]
    public function __invoke(HttpRequest $request): JsonResponse
    {
        $ulid = $request->routeParameters->getString('ulid');

        // Find image by ULID
        $image = $this->imageRepository->findByUlid($ulid);
        if (! $image) {
            throw NotFound::create(
                ErrorCode::ENTITY_NOT_FOUND,
                "Image with ULID {$ulid} not found"
            )->withData(['ulid' => $ulid]);
        }

        // Parse request body
        $updateData = $this->parseUpdateData($request);

        // Update only allowed fields
        $updatedImage = $image;

        if (isset($updateData['alt_text'])) {
            $updatedImage = $updatedImage->withAltText($updateData['alt_text']);
        }

        if (isset($updateData['filename'])) {
            $this->validateFilename($updateData['filename']);
            $updatedImage = $updatedImage->withFilename($updateData['filename']);
        }

        // Save updated image
        $this->imageRepository->save($updatedImage);

        // Return updated image data
        return new JsonResponse([
            'ulid' => $updatedImage->getUlidString(),
            'filename' => $updatedImage->filename,
            'original_filename' => $updatedImage->originalFilename,
            'url' => '/media/images/' . $updatedImage->path->toString(),
            'thumbnail_url' => '/media/images/thumbnails/' . $updatedImage->path->toString(),
            'alt_text' => $updatedImage->altText,
            'dimensions' => [
                'width' => $updatedImage->width,
                'height' => $updatedImage->height,
                'aspect_ratio' => $updatedImage->getAspectRatio(),
                'orientation' => $updatedImage->getDimensions()->getOrientation()->value,
            ],
            'mime_type' => $updatedImage->mimeType->value,
            'file_size' => [
                'bytes' => $updatedImage->fileSize->toBytes(),
                'human_readable' => $updatedImage->getHumanReadableFileSize(),
            ],
            'hash' => $updatedImage->hash->toString(),
            'is_image' => $updatedImage->isImageFile(),
            'created_at' => $updatedImage->ulid->getDateTime()->format('c'),
            'variants' => array_map(fn ($variant) => [
                'type' => $variant->variantType,
                'width' => $variant->width,
                'height' => $variant->height,
                'path' => $variant->path,
                'url' => '/media/images/' . $variant->path,
            ], $updatedImage->variants ?? []),
        ], Status::OK);
    }

    /**
     * Parse update data from request body
     */
    private function parseUpdateData(HttpRequest $request): array
    {
        $body = $request->parsedBody->toArray();

        // Only allow specific fields to be updated
        $allowedFields = ['alt_text', 'filename'];
        $updateData = [];

        foreach ($allowedFields as $field) {
            if (array_key_exists($field, $body)) {
                $updateData[$field] = $body[$field];
            }
        }

        return $updateData;
    }

    /**
     * Validate filename
     */
    private function validateFilename(string $filename): void
    {
        if (empty(trim($filename))) {
            throw FrameworkException::create(
                ErrorCode::VAL_INVALID_FORMAT,
                'Filename cannot be empty'
            )->withData(['field' => 'filename']);
        }

        // Check for invalid characters
        if (preg_match('/[\/\\\\:*?"<>|]/', $filename)) {
            throw FrameworkException::create(
                ErrorCode::VAL_INVALID_FORMAT,
                'Filename contains invalid characters'
            )->withData([
                'field' => 'filename',
                'value' => $filename,
                'invalid_chars' => ['/', '\\', ':', '*', '?', '"', '<', '>', '|'],
            ]);
        }

        // Check length
        if (strlen($filename) > 255) {
            throw FrameworkException::create(
                ErrorCode::VAL_INVALID_FORMAT,
                'Filename is too long (maximum 255 characters)'
            )->withData([
                'field' => 'filename',
                'length' => strlen($filename),
                'max_length' => 255,
            ]);
        }
    }
}
