<?php

declare(strict_types=1);

namespace App\Scenario;

use App\Util\ParsedCommand;
use Pkirillw\MaxBotApi\Builder\Message as MessageBuilder;
use Pkirillw\MaxBotApi\Scheme\Attachment\PhotoTokens;
use Pkirillw\MaxBotApi\Scheme\Attachment\UploadedInfo;
use Pkirillw\MaxBotApi\Scheme\Enum\Format;
use Pkirillw\MaxBotApi\Scheme\Enum\UploadType;
use Pkirillw\MaxBotApi\Scheme\Update\MessageCreatedUpdate;

final class UploadsScenario extends AbstractScenario
{
    private const DEFAULT_PHOTO_URL = 'https://placehold.co/600x400/png';
    private const DEFAULT_FILE_URL = 'https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf';

    /**
     * Look for a fixture in storage/uploads/ (override, gitignored) first, fall back to fixtures/ (committed).
     */
    private static function fixturePath(string $filename): string
    {
        $override = __DIR__ . '/../../storage/uploads/' . $filename;
        if (is_file($override)) {
            return $override;
        }
        return __DIR__ . '/../../fixtures/' . $filename;
    }

    public function handle(MessageCreatedUpdate $update, ParsedCommand $command): void
    {
        switch ($command->name) {
            case 'upload_photo_file':
                $this->uploadPhotoFromFile($update, $command);
                return;
            case 'upload_photo_url':
                $this->uploadPhotoFromUrl($update, $command);
                return;
            case 'upload_photo_bytes':
                $this->uploadPhotoFromBytes($update);
                return;
            case 'upload_photo_base64':
                $this->uploadPhotoFromBase64($update);
                return;
            case 'upload_audio':
                $this->uploadMediaFromFile($update, $command, UploadType::Audio);
                return;
            case 'upload_video':
                $this->uploadMediaFromFile($update, $command, UploadType::Video);
                return;
            case 'upload_file':
                $this->uploadMediaFromFile($update, $command, UploadType::File);
                return;
            case 'upload_url':
                $this->uploadMediaFromUrl($update, $command);
                return;
        }
    }

    private function uploadPhotoFromFile(MessageCreatedUpdate $update, ParsedCommand $command): void
    {
        $path = $command->arg(0, self::fixturePath('photo.png'));
        if (!is_file($path)) {
            $this->replyMarkdown($update, "File not found: `$path`. Положи PNG в fixtures/photo.png или storage/uploads/photo.png.");
            return;
        }
        $tokens = $this->api->uploads->uploadPhotoFromFile($path);
        $this->sendPhotoWithCaption($update, $tokens, 'photo from file');
    }

    private function uploadPhotoFromUrl(MessageCreatedUpdate $update, ParsedCommand $command): void
    {
        $url = $command->arg(0, self::DEFAULT_PHOTO_URL);
        $tokens = $this->api->uploads->uploadPhotoFromUrl($url);
        $this->sendPhotoWithCaption($update, $tokens, "photo from $url");
    }

    private function uploadPhotoFromBytes(MessageCreatedUpdate $update): void
    {
        // Synthesize a 1x1 PNG via PNG header bytes (no GD dependency).
        $png = base64_decode(
            'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=',
            strict: true,
        );
        $tokens = $this->api->uploads->uploadPhotoFromBytes($png, 'pixel.png');
        $this->sendPhotoWithCaption($update, $tokens, 'photo from raw bytes');
    }

    private function uploadPhotoFromBase64(MessageCreatedUpdate $update): void
    {
        $base64 = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=';
        $tokens = $this->api->uploads->uploadPhotoFromBase64($base64, 'pixel.png');
        $this->sendPhotoWithCaption($update, $tokens, 'photo from base64');
    }

    private function uploadMediaFromFile(MessageCreatedUpdate $update, ParsedCommand $command, UploadType $type): void
    {
        $filename = match ($type) {
            UploadType::Audio => 'audio.mp3',
            UploadType::Video => 'video.mp4',
            UploadType::File => 'doc.pdf',
            UploadType::Photo => 'photo.png',
        };
        $path = $command->arg(0, self::fixturePath($filename));
        if (!is_file($path)) {
            $this->replyMarkdown($update, "File not found: `$path`. Положи файл в fixtures/ или storage/uploads/.");
            return;
        }
        $info = $this->api->uploads->uploadMediaFromFile($type, $path);
        $this->sendMediaWithCaption($update, $info, $type);
    }

    private function uploadMediaFromUrl(MessageCreatedUpdate $update, ParsedCommand $command): void
    {
        $typeStr = $command->arg(0);
        $url = $command->arg(1, self::DEFAULT_FILE_URL);
        if ($typeStr === null) {
            $this->replyMarkdown($update, 'Usage: `/upload_url <audio|video|file> [url]`');
            return;
        }
        $type = UploadType::tryFrom($typeStr);
        if ($type === null || $type === UploadType::Photo) {
            $this->replyMarkdown($update, 'Type must be audio, video or file. Use /upload_photo_url for photos.');
            return;
        }
        $info = $this->api->uploads->uploadMediaFromUrl($type, $url);
        $this->sendMediaWithCaption($update, $info, $type);
    }

    private function sendPhotoWithCaption(MessageCreatedUpdate $update, PhotoTokens $tokens, string $caption): void
    {
        if ($tokens->photos === []) {
            $this->replyMarkdown($update, 'Upload returned no photo tokens (server may still be processing).');
            return;
        }
        $this->api->messages->send(
            MessageBuilder::new()
                ->setChat($update->getChatId())
                ->setText($caption)
                ->setFormat(Format::Markdown)
                ->addPhoto($tokens),
        );
    }

    private function sendMediaWithCaption(MessageCreatedUpdate $update, UploadedInfo $info, UploadType $type): void
    {
        $caption = match ($type) {
            UploadType::Audio => 'audio upload',
            UploadType::Video => 'video upload',
            UploadType::File => 'file upload',
            UploadType::Photo => 'photo upload',
        };
        $builder = MessageBuilder::new()
            ->setChat($update->getChatId())
            ->setText($caption . ' (token=' . $info->token . ')');
        match ($type) {
            UploadType::Audio => $builder->addAudio($info),
            UploadType::Video => $builder->addVideo($info),
            UploadType::File => $builder->addFile($info),
            UploadType::Photo => $builder,
        };
        $this->api->messages->send($builder);
    }
}
