<?php
/**
 * @package     MefisAssetBridge
 * @subpackage  MefisAssetBridgeCore_Api_Endpoints
 *
 * @copyright   Copyright (C) 2025 Goran Usljebrka. All rights reserved.
 * @license     Proprietary — No resale or incorporation into other projects without explicit written permission. see LICENSE.txt
 */

namespace MefisAssetBridgeCore\Api\Endpoints\Asset;

use MefisAssetBridgeCore\Api\ApiClient;
use MefisAssetBridgeCore\Api\Storage\S3Storage;
use MefisAssetBridgeCore\Cache\CoreCache;
use MefisAssetBridgeCore\DTO\ThumbnailDto;
use MefisAssetBridgeCore\Traits\Encryptor;
use MefisAssetBridgeCore\Auth\AuthenticatedUser;
use MefisAssetBridgeCore\Api\Response\ApiResponse;
use MefisAssetBridgeCore\Interfaces\EndpointInterface;

/**
 * ChangeAssetThumbnail API Endpoint
 */
class ChangeAssetThumbnail implements EndpointInterface
{
    use Encryptor;

    /** @var ApiClient API client instance */
    private ApiClient $ApiClient;

    /** @var AuthenticatedUser|null Authenticated user */
    private ?AuthenticatedUser $user;

    private string $uuid;
    private string $pathHash;
    private string $_masterKey;
    private bool $verifySSL;
    private ?CoreCache $cache;
    /** @var array Required parameters */
    private array $requiredParams = [
        'id',
        'thumbnail',
        'domain',
        'userUuid',
        'assetUuid',
    ];

    private string $validationError = '';

    public function __construct(string $uuid, ApiClient $ApiClient, ?AuthenticatedUser $user, string $pathHash = '', string $masterKey = '', bool $verifySSL = true, ?CoreCache $cache = null)
    {
        $this->ApiClient = $ApiClient;
        $this->user      = $user;
        $this->uuid      = $uuid;
        $this->pathHash  = $pathHash;
        $this->verifySSL = $verifySSL;
        $this->cache     = $cache;

        if(empty($masterKey)) {
            throw new \RuntimeException('Master key is required to save asset');
        }
        $this->_masterKey = $masterKey;
        $this->setEncryptorKey($masterKey);
    }

    /**
     * Validate request parameters
     *
     * @param array $params Request parameters
     * @return bool True if parameters are valid
     */
    public function validateParams(array $params): bool
    {

        foreach ($this->requiredParams as $param) {
            if (! isset($params[$param])) {
                $this->validationError = sprintf('Missing required parameter: %s', $param);
                return false;
            }
        }

        return true;
    }

    /**
     * Send request to change asset thumbnail via presigned S3 URL flow.
     *
     * Flow (mirrors SaveAsset):
     *   1. Validate + encrypt thumbnail → temp file
     *   2. POST /assets/{id}/thumbnail  (no file, metadata only) → server returns presigned S3 POST URL
     *   3. Upload encrypted binary to S3
     *   4. PATCH /assets/{id}/thumbnail/storage → confirm upload result
     *   5. Return response containing _binary_thumb for caller to cache
     *
     * @param array $params  Required: id (int), thumbnail (array), domain (string)
     * @return ApiResponse
     */
    public function sendRequest(array $params): ApiResponse
    {
        // Validate required parameters
        if (! $this->validateParams($params)) {
            return ApiResponse::error($this->validationError);
        }

        $this->ApiClient->setUser($this->user);

        // Step 1: Build and validate encrypted thumbnail DTO
        $thumbnail = new ThumbnailDto($params['thumbnail'] ?? [], $this->_masterKey);

        if (!$thumbnail->isValid()) {
            return ApiResponse::error('Invalid thumbnail');
        }
        if ($thumbnail->isTooBig(500 * 1024)) {
            return ApiResponse::error('Thumbnail size exceeds the maximum allowed size of 500KB');
        }

        // Read encrypted binary from temp file now — used for both S3 upload and cache
        $thumbnailBinary = file_get_contents($thumbnail->getTemporaryFilePath());
        if ($thumbnailBinary === false) {
            return ApiResponse::error('Failed to read encrypted thumbnail file');
        }

        // Step 2: POST metadata to server — receive presigned S3 upload URL
        $requestData = [
            'id'     => $params['id'],
            'path'   => $this->pathHash,
            'domain' => $params['domain'],
        ];

        $response = $this->ApiClient->put('assets/' . $params['id'] . '/storage/thumbnail', $requestData);

        // Cleanup temp file regardless of server response
        if (file_exists($thumbnail->getTemporaryFilePath())) {
            @unlink($thumbnail->getTemporaryFilePath());
        }

        if (!$response->isSuccess()) {
            return $response;
        }

        $responseData = $response->getData();

        // Validate server returned upload credentials
        if (!isset($responseData['upload']['thumbnail'])) {
            return ApiResponse::error('Server did not provide thumbnail upload credentials');
        }

        // Step 3: Upload encrypted binary to S3
        $s3Storage = new S3Storage($this->verifySSL);

        $thumbnailUpload = $s3Storage->upload(
            $responseData['upload']['thumbnail']['url'],
            $responseData['upload']['thumbnail']['fields'],
            $thumbnailBinary
        );

        // Step 4: Confirm upload result with server, including ETag for integrity verification
        $uploadResults = [
            'files' => [
                [
                    'type'   => 'thumb',
                    'status' => $thumbnailUpload->isSuccess() ? 1 : 0,
                    'etag'   => $thumbnailUpload->isSuccess() ? ($thumbnailUpload->getData()['eTag'] ?? null) : null,
                ],
            ],
        ];

        $storageResponse = $this->ApiClient->patch(
            'assets/' . $params['id'] . '/storage',
            $uploadResults
        );

        // Remove upload credentials before returning
        unset($responseData['upload']);

        if (!$thumbnailUpload->isSuccess()) {
            return ApiResponse::error('Failed to upload thumbnail to S3');
        }

        if (!$storageResponse->isSuccess()) {
            return ApiResponse::error('Thumbnail uploaded but failed to confirm with server');
        }

        // Cache the new encrypted thumbnail binary.
        // assetUuid and userUuid must be passed in $params by the caller.
        $assetUuid = $params['assetUuid'] ?? '';
        $userUuid  = $params['userUuid']  ?? '';
        if ($this->cache !== null && $assetUuid !== '' && $userUuid !== '') {
            $this->cache->set($userUuid, $thumbnailBinary, $assetUuid . '.thumb');
        }

        return ApiResponse::success($responseData, 'Thumbnail updated successfully');
    }

    public function setMasterKey(string $masterKey)
    {
        $this->setEncryptorKey($masterKey);
    }
}
