<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use OpenApi\Attributes as OA;

class LauncherController extends Controller
{
    #[OA\Get(
        path: '/api/launcher/manifest',
        summary: 'Получить манифест файлов клиента',
        description: 'Возвращает JSON-манифест со списком файлов клиента, их размерами и SHA-256 хешами. Лаунчер сверяет локальные файлы с манифестом и докачивает недостающие.',
        tags: ['Launcher'],
        security: [['bearerAuth' => []]],
        responses: [
            new OA\Response(
                response: 200,
                description: 'Манифест файлов',
                content: new OA\JsonContent(
                    properties: [
                        new OA\Property(
                            property: 'files',
                            type: 'array',
                            items: new OA\Items(
                                properties: [
                                    new OA\Property(property: 'path', type: 'string', example: 'Data/common.MPQ'),
                                    new OA\Property(property: 'size', type: 'integer', format: 'int64', example: 2881154862),
                                    new OA\Property(property: 'sha256', type: 'string', format: 'hex', example: 'd850ffa5efd6a1ba845899a7d9f9ad27f6eea7ac606e95033506c72c86ee0236'),
                                ],
                            ),
                        ),
                    ],
                ),
            ),
            new OA\Response(
                response: 401,
                description: 'Не авторизован',
                content: new OA\JsonContent(
                    properties: [
                        new OA\Property(property: 'message', type: 'string', example: 'Unauthenticated.'),
                    ],
                ),
            ),
            new OA\Response(
                response: 404,
                description: 'Манифест не найден в хранилище',
                content: new OA\JsonContent(
                    properties: [
                        new OA\Property(property: 'message', type: 'string', example: 'Манифест не найден.'),
                    ],
                ),
            ),
        ],
    )]
    public function manifest(): JsonResponse
    {
        $disk = Storage::disk($this->disk());
        $key = config('moonwell.launcher.manifest_key');

        if (! $disk->exists($key)) {
            return response()->json(['message' => 'Манифест не найден.'], 404);
        }

        $manifest = json_decode($disk->get($key), true);

        return response()->json($manifest);
    }

    #[OA\Get(
        path: '/api/launcher/download/{path}',
        summary: 'Получить ссылку на скачивание файла',
        description: 'Возвращает временную presigned-ссылку на файл в S3. Лаунчер скачивает файл напрямую из хранилища по этой ссылке. Путь может содержать вложенные директории (например `Data/ruRU/patch-ruRU-3.MPQ`).',
        tags: ['Launcher'],
        security: [['bearerAuth' => []]],
        parameters: [
            new OA\Parameter(
                name: 'path',
                in: 'path',
                required: true,
                description: 'Относительный путь к файлу из манифеста',
                schema: new OA\Schema(type: 'string'),
                example: 'Data/common.MPQ',
            ),
        ],
        responses: [
            new OA\Response(
                response: 200,
                description: 'Временная ссылка на скачивание',
                content: new OA\JsonContent(
                    properties: [
                        new OA\Property(property: 'url', type: 'string', format: 'uri', description: 'Presigned URL для скачивания'),
                        new OA\Property(property: 'expires_in', type: 'integer', description: 'Время жизни ссылки в секундах', example: 3600),
                    ],
                ),
            ),
            new OA\Response(
                response: 401,
                description: 'Не авторизован',
                content: new OA\JsonContent(
                    properties: [
                        new OA\Property(property: 'message', type: 'string', example: 'Unauthenticated.'),
                    ],
                ),
            ),
            new OA\Response(
                response: 404,
                description: 'Файл не найден',
                content: new OA\JsonContent(
                    properties: [
                        new OA\Property(property: 'message', type: 'string', example: 'Файл не найден.'),
                    ],
                ),
            ),
        ],
    )]
    public function download(string $path): JsonResponse
    {
        $disk = Storage::disk($this->disk());
        $fullPath = $this->basePath() . '/' . $path;

        if (! $disk->exists($fullPath)) {
            return response()->json(['message' => 'Файл не найден.'], 404);
        }

        $ttlMinutes = config('moonwell.launcher.download_ttl', 60);
        $url = $disk->temporaryUrl($fullPath, now()->addMinutes($ttlMinutes));

        return response()->json([
            'url' => $url,
            'expires_in' => $ttlMinutes * 60,
        ]);
    }

    #[OA\Post(
        path: '/api/service/update-appcast',
        summary: 'Обновить appcast лаунчера',
        description: 'Служебный endpoint для разработчика лаунчера. Принимает готовый XML appcast в формате RSS 2.0/Sparkle, который использует Flutter-пакет `upgrader`, и сохраняет его как публичный `/appcast.xml` на сервере. Тело запроса передается как raw XML, без JSON-обертки.',
        tags: ['Launcher Service'],
        security: [['launcherAuth' => []]],
        requestBody: new OA\RequestBody(
            required: true,
            content: new OA\MediaType(
                mediaType: 'application/xml',
                schema: new OA\Schema(
                    type: 'string',
                    example: "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<rss version=\"2.0\" xmlns:sparkle=\"http://www.andymatuschak.org/xml-namespaces/sparkle\">\n  <channel>\n    <title>Moonwell Launcher - Appcast</title>\n    <item>\n      <title>Version 1.2.0</title>\n      <description>Описание изменений для окна обновления.</description>\n      <pubDate>Sat, 16 May 2026 12:00:00 +0400</pubDate>\n      <enclosure url=\"https://moonwell.su/launcher/download\" sparkle:version=\"1.2.0\" sparkle:os=\"windows\" />\n    </item>\n  </channel>\n</rss>",
                ),
            ),
        ),
        responses: [
            new OA\Response(
                response: 200,
                description: 'Appcast сохранен',
                content: new OA\JsonContent(
                    properties: [
                        new OA\Property(property: 'message', type: 'string', example: 'XML saved'),
                    ],
                ),
            ),
            new OA\Response(
                response: 403,
                description: 'Нет доступа или передано пустое тело запроса',
                content: new OA\JsonContent(
                    properties: [
                        new OA\Property(property: 'message', type: 'string', example: 'empty body'),
                    ],
                ),
            ),
            new OA\Response(
                response: 500,
                description: 'Не удалось сохранить appcast.xml',
                content: new OA\JsonContent(
                    properties: [
                        new OA\Property(property: 'message', type: 'string', example: 'Failed to save XML'),
                    ],
                ),
            ),
        ],
    )]
    public function updateAppcast(Request $request): JsonResponse
    {
        $xml = $request->getContent();

        if (blank($xml)) {
            return response()->json([
                'message' => 'empty body',
            ], 403);
        }

        $saved = Storage::disk('public')->put('appcast.xml', $xml);

        if (! $saved) {
            return response()->json([
                'message' => 'Failed to save XML',
            ], 500);
        }

        return response()->json([
            'message' => 'XML saved',
        ]);
    }

    private function disk(): string
    {
        return config('moonwell.launcher.disk', 's3');
    }

    private function basePath(): string
    {
        return config('moonwell.launcher.base_path', 'World of Warcraft');
    }
}
