<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\Event;
use App\Services\Photobooth\Exceptions\SparkboothUploadException;
use App\Services\Photobooth\SparkboothUploadService;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Str;

class SparkboothUploadController extends Controller
{
    public function __construct(private readonly SparkboothUploadService $service) {}

    public function store(Request $request): Response
    {
        $media = $this->resolveMedia($request);

        if (! $media) {
            return $this->respond(null, false, 'Media is required', null, 400, $request);
        }

        try {
            $result = $this->service->handleUpload(
                $media,
                $request->input('username'),
                $request->input('password')
            );

            /** @var Event $event */
            $event = $result['event'];

            return $this->respond($event, true, null, null, 200, $request);
        } catch (SparkboothUploadException $exception) {
            return $this->respond(null, false, $exception->getMessage(), null, $exception->statusCode ?? 400, $request);
        } catch (\Throwable) {
            return $this->respond(null, false, 'Upload failed, please retry.', null, 500, $request);
        }
    }

    protected function respond(?Event $event, bool $ok, ?string $message, ?string $url, int $status, Request $request): Response
    {
        $format = $this->resolveFormat($event, $request);

        if ($format === 'xml') {
            $payload = $ok
                ? $this->buildSuccessXml($url)
                : $this->buildFailureXml($message);

            return response($payload, $status, ['Content-Type' => 'application/xml']);
        }

        return response()->json([
            'status' => $ok,
            'error' => $ok ? null : $message,
            'url' => $url,
        ], $status);
    }

    protected function resolveFormat(?Event $event, Request $request): string
    {
        $preferred = $request->input('format');

        if ($preferred && in_array($preferred, ['json', 'xml'], true)) {
            return $preferred;
        }

        $event?->loadMissing('photoboothSetting');
        $configured = ($event?->photoboothSetting?->metadata ?? [])['sparkbooth_response_format'] ?? null;

        if ($configured && in_array($configured, ['json', 'xml'], true)) {
            return $configured;
        }

        return config('photobooth.sparkbooth.response_format', 'json') === 'xml' ? 'xml' : 'json';
    }

    protected function buildSuccessXml(?string $url): string
    {
        $urlAttribute = $url ? ' url="'.htmlspecialchars($url, ENT_QUOTES).'"' : '';

        return sprintf('<?xml version="1.0" encoding="UTF-8"?>'."\n".'<rsp status="ok"%s />', $urlAttribute);
    }

    protected function buildFailureXml(?string $message): string
    {
        $escaped = htmlspecialchars($message ?? 'Upload failed', ENT_QUOTES);

        return sprintf(
            '<?xml version="1.0" encoding="UTF-8"?>'."\n".'<rsp status="fail"><err msg="%s" /></rsp>',
            $escaped
        );
    }

    protected function resolveMedia(Request $request): ?UploadedFile
    {
        $file = $request->file('media');

        if ($file instanceof UploadedFile) {
            return $file;
        }

        $raw = $request->input('media');

        if (is_string($raw) && $raw !== '') {
            return $this->createUploadedFileFromBase64($raw);
        }

        return null;
    }

    protected function createUploadedFileFromBase64(string $raw): ?UploadedFile
    {
        $payload = $raw;

        if (Str::startsWith($raw, 'data:')) {
            $segments = explode(',', $raw, 2);
            $payload = $segments[1] ?? '';
        }

        $decoded = base64_decode($payload, true);

        if ($decoded === false) {
            return null;
        }

        $tmpPath = tempnam(sys_get_temp_dir(), 'sparkbooth-');

        if (! $tmpPath) {
            return null;
        }

        file_put_contents($tmpPath, $decoded);

        return new UploadedFile($tmpPath, 'upload.jpg', null, null, true);
    }
}
