<?php

declare(strict_types=1);

/**
 * APO-194 Card 4 — Video Asset action service.
 *
 * Video assets layer on top of files (one file = one rendition). Four verbs:
 * create-video-asset (file upload + video row), update-video-asset (metadata),
 * add-video-rendition (additional file upload bound to existing video),
 * delete-video-asset (cascade delete file rows + bytes).
 *
 * The file-upload steps reuse MyronFilesActionService — same JSON-content
 * envelope semantics; same multipart admin path semantics.
 */
final class MyronVideoAssetActionService
{
    public function __construct(
        private readonly MyronVideoAssetRepository $videos,
        private readonly MyronFilesActionService $files,
    ) {
    }

    /**
     * @param array<string, mixed> $envelope    JSON-content envelope for the initial rendition
     * @param array<string, mixed> $metadata    asset metadata (title, description, etc.)
     * @return array<string, mixed>             video asset row
     */
    public function createFromEnvelope(
        array $envelope,
        array $metadata,
        string $qualityLabel,
        MyronActionCallerContext $caller
    ): array {
        $metadata['asset_class'] = 'video';
        $file = $this->files->uploadFromEnvelope($envelope, $metadata, $caller, null, true);
        $userId = $caller->callerType === 'human-session' ? $caller->callerId : '';
        return (array) $this->videos->createVideoAsset(
            $metadata,
            $userId,
            (string) $file['id'],
            $qualityLabel !== '' ? $qualityLabel : '1080p'
        );
    }

    /**
     * Update video asset metadata.
     *
     * @param array<string, mixed> $input
     * @return array{assetId: string}
     */
    public function updateMetadata(string $assetId, array $input, MyronActionCallerContext $caller): array
    {
        $assetId = trim($assetId);
        if ($assetId === '') {
            throw new InvalidArgumentException('assetId is required.');
        }
        $userId = $caller->callerType === 'human-session' ? $caller->callerId : '';
        $this->videos->updateVideoAsset($assetId, $input, $userId);
        return ['assetId' => $assetId];
    }

    /**
     * Add a rendition (additional file bound to the video) via JSON-content envelope.
     *
     * @param array<string, mixed> $envelope
     * @return array{assetId: string, fileId: string, quality: string}
     */
    public function addRenditionFromEnvelope(
        string $assetId,
        array $envelope,
        string $qualityLabel,
        MyronActionCallerContext $caller
    ): array {
        $assetId = trim($assetId);
        if ($assetId === '') {
            throw new InvalidArgumentException('assetId is required.');
        }
        $file = $this->files->uploadFromEnvelope($envelope, ['asset_class' => 'video'], $caller, null, true);
        $this->videos->addRendition($assetId, (string) $file['id'], $qualityLabel !== '' ? $qualityLabel : '720p');
        return ['assetId' => $assetId, 'fileId' => (string) $file['id'], 'quality' => $qualityLabel];
    }

    /**
     * @return array{assetId: string, deleted: bool}
     */
    public function delete(string $assetId, MyronActionCallerContext $caller, bool $deleteFiles = true): array
    {
        $assetId = trim($assetId);
        if ($assetId === '') {
            throw new InvalidArgumentException('assetId is required.');
        }
        $this->videos->deleteVideoAsset($assetId, $deleteFiles);
        return ['assetId' => $assetId, 'deleted' => true];
    }
}
