<?php

namespace App\Http\Controllers\Api\Tenant;

use App\Http\Controllers\Controller;
use App\Models\Event;
use App\Models\EventJoinToken;
use App\Support\JoinTokenLayoutRegistry;
use App\Support\TenantMemberPermissions;
use Dompdf\Dompdf;
use Dompdf\Options;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use SimpleSoftwareIO\QrCode\Facades\QrCode;

class EventJoinTokenLayoutController extends Controller
{
    /**
     * Mapping of preset keys to portrait background assets.
     *
     * @var array<string, string>
     */
    private const BACKGROUND_PRESETS = [
        'bg-blue-floral' => 'storage/layouts/backgrounds-portrait/bg-blue-floral.png',
        'bg-goldframe' => 'storage/layouts/backgrounds-portrait/bg-goldframe.png',
        'gr-green-floral' => 'storage/layouts/backgrounds-portrait/gr-green-floral.png',
    ];

    public function index(Request $request, Event $event, EventJoinToken $joinToken)
    {
        $this->ensureBelongsToEvent($event, $joinToken);
        TenantMemberPermissions::ensureEventPermission($request, $event, 'join-tokens:manage');

        $layouts = JoinTokenLayoutRegistry::toResponse(function (string $layoutId, string $format) use ($event, $joinToken) {
            return route('api.v1.tenant.events.join-tokens.layouts.download', [
                'event' => $event,
                'joinToken' => $joinToken,
                'layout' => $layoutId,
                'format' => $format,
            ]);
        });

        return response()->json([
            'data' => $layouts,
        ]);
    }

    public function download(Request $request, Event $event, EventJoinToken $joinToken, string $layout, string $format)
    {
        $this->ensureBelongsToEvent($event, $joinToken);
        TenantMemberPermissions::ensureEventPermission($request, $event, 'join-tokens:manage');

        $layoutConfig = JoinTokenLayoutRegistry::find($layout);

        if (! $layoutConfig) {
            abort(404, 'Layout nicht gefunden.');
        }

        if (! in_array($format, ['pdf', 'png'], true)) {
            abort(404, 'Unbekanntes Exportformat.');
        }

        $layoutConfig = $this->applyCustomization($layoutConfig, $joinToken);

        $tokenUrl = url('/e/'.$joinToken->token);

        $qrPngDataUri = 'data:image/png;base64,'.base64_encode(
            QrCode::format('png')
                ->margin(0)
                ->size($layoutConfig['qr']['size_px'])
                ->generate($tokenUrl)
        );

        $backgroundStyle = $this->buildBackgroundStyle($layoutConfig);
        $eventName = $this->resolveEventName($event);
        $backgroundImage = $layoutConfig['background_image'] ?? null;

        $viewData = [
            'layout' => $layoutConfig,
            'event' => $event,
            'eventName' => $eventName,
            'token' => $joinToken,
            'tokenUrl' => $tokenUrl,
            'qrPngDataUri' => $qrPngDataUri,
            'backgroundStyle' => $backgroundStyle,
            'backgroundImage' => $backgroundImage,
            'customization' => $joinToken->metadata['layout_customization'] ?? null,
            'advancedLayout' => $this->buildAdvancedLayout(
                $layoutConfig,
                $joinToken->metadata['layout_customization'] ?? null,
                $qrPngDataUri,
                $tokenUrl,
                $eventName
            ),
        ];

        $html = view('layouts.join-token.pdf', $viewData)->render();

        $options = new Options;
        $options->set('isHtml5ParserEnabled', true);
        $options->set('isRemoteEnabled', true);
        $options->set('defaultFont', 'Helvetica');

        $dompdf = new Dompdf($options);
        $dompdf->setPaper(strtoupper($layoutConfig['paper']), $layoutConfig['orientation'] === 'landscape' ? 'landscape' : 'portrait');
        $dompdf->loadHtml($html, 'UTF-8');
        $dompdf->render();

        $pdfBinary = $dompdf->output();
        $filenameStem = sprintf('%s-%s', Str::slug($eventName ?: 'event'), $layoutConfig['id']);

        if ($format === 'png') {
            if (! class_exists(\Imagick::class)) {
                abort(500, 'PNG-Export erfordert Imagick.');
            }

            try {
                $imagick = new \Imagick;
                $imagick->setResolution(300, 300);
                $imagick->setBackgroundColor(new \ImagickPixel('white'));
                $imagick->setOption('pdf:use-cropbox', 'true');
                $imagick->readImageBlob($pdfBinary);
                $imagick->setIteratorIndex(0);

                $flattened = $imagick->mergeImageLayers(\Imagick::LAYERMETHOD_FLATTEN);
                $flattened->setImageFormat('png');
                $flattened->setImageUnits(\Imagick::RESOLUTION_PIXELSPERINCH);
                $flattened->setImageAlphaChannel(\Imagick::ALPHACHANNEL_REMOVE);

                $pngBinary = $flattened->getImagesBlob();

                $flattened->clear();
                $flattened->destroy();
                $imagick->clear();
                $imagick->destroy();
            } catch (\Throwable $exception) {
                report($exception);
                abort(500, 'PNG-Export konnte nicht erzeugt werden.');
            }

            return response($pngBinary)
                ->header('Content-Type', 'image/png')
                ->header('Content-Disposition', 'attachment; filename="'.$filenameStem.'.png"');
        }

        return response($pdfBinary)
            ->header('Content-Type', 'application/pdf')
            ->header('Content-Disposition', 'attachment; filename="'.$filenameStem.'.pdf"');
    }

    private function ensureBelongsToEvent(Event $event, EventJoinToken $joinToken): void
    {
        if ($joinToken->event_id !== $event->id) {
            abort(404);
        }
    }

    private function resolveEventName(Event $event): string
    {
        $name = $event->name;

        if (is_array($name)) {
            $locale = $event->default_locale ?? 'de';

            return $name[$locale] ?? $name['de'] ?? reset($name) ?: 'Event';
        }

        return is_string($name) && $name !== '' ? $name : 'Event';
    }

    private function applyCustomization(array $layout, EventJoinToken $joinToken): array
    {
        $customization = data_get($joinToken->metadata, 'layout_customization');

        if (! is_array($customization)) {
            return $layout;
        }

        $layoutId = $customization['layout_id'] ?? null;
        if (is_string($layoutId) && isset($layout['id']) && $layoutId !== $layout['id']) {
            // Allow customization to target a specific layout; if mismatch, skip style overrides.
            // General text overrides are still applied below.
        }

        $colorKeys = [
            'accent' => 'accent_color',
            'text' => 'text_color',
            'background' => 'background_color',
            'secondary' => 'secondary_color',
            'badge' => 'badge_color',
        ];

        foreach ($colorKeys as $layoutKey => $customKey) {
            if (isset($customization[$customKey]) && is_string($customization[$customKey])) {
                $layout[$layoutKey] = $customization[$customKey];
            }
        }

        if (isset($customization['background_gradient']) && is_array($customization['background_gradient'])) {
            $layout['background_gradient'] = $customization['background_gradient'];
        }

        foreach (['headline' => 'name', 'subtitle', 'description', 'badge_label', 'instructions_heading', 'link_heading', 'cta_label', 'cta_caption', 'link_label'] as $customKey => $layoutKey) {
            if (isset($customization[$customKey]) && is_string($customization[$customKey])) {
                $layout[$layoutKey] = $customization[$customKey];
            }
        }

        if (array_key_exists('instructions', $customization) && is_array($customization['instructions'])) {
            $layout['instructions'] = array_values(array_filter($customization['instructions'], fn ($value) => is_string($value) && trim($value) !== ''));
        }

        if (! empty($customization['logo_data_url']) && is_string($customization['logo_data_url'])) {
            $layout['logo_url'] = $customization['logo_data_url'];
        } elseif (! empty($customization['logo_url']) && is_string($customization['logo_url'])) {
            $layout['logo_url'] = $customization['logo_url'];
        }

        if (! empty($customization['background_preset']) && is_string($customization['background_preset'])) {
            $presetImage = $this->resolveBackgroundPreset($customization['background_preset']);
            if ($presetImage) {
                $layout['background_image'] = $presetImage;
                $layout['background_preset'] = $customization['background_preset'];
            }
        }

        return $layout;
    }

    private function buildBackgroundStyle(array $layout): string
    {
        if (! empty($layout['background_image']) && is_string($layout['background_image'])) {
            return sprintf('url(%s) center center / cover no-repeat', $layout['background_image']);
        }

        $gradient = $layout['background_gradient'] ?? null;

        if (is_array($gradient) && ! empty($gradient['stops'])) {
            $angle = $gradient['angle'] ?? 180;
            $stops = implode(',', $gradient['stops']);

            return sprintf('linear-gradient(%ddeg,%s)', $angle, $stops);
        }

        return $layout['background'] ?? '#FFFFFF';
    }

    private function buildAdvancedLayout(array $layout, $customization, string $qrPngDataUri, string $tokenUrl, string $eventName): ?array
    {
        $customization = is_array($customization) ? $customization : [];
        $hasLayoutElements = is_array($layout['elements'] ?? null) && ! empty($layout['elements']);
        $isAdvancedRequested = ($customization['mode'] ?? null) === 'advanced';

        if (! $isAdvancedRequested && ! $hasLayoutElements) {
            return null;
        }

        $elements = $customization['elements'] ?? ($layout['elements'] ?? null);

        if (! is_array($elements) || empty($elements)) {
            return null;
        }

        $width = (int) ($layout['canvas_width'] ?? $layout['svg']['width'] ?? 1080);
        $height = (int) ($layout['canvas_height'] ?? $layout['svg']['height'] ?? 1520);
        $accent = $layout['accent'] ?? '#6366F1';
        $text = $layout['text'] ?? '#0F172A';
        $secondary = $layout['secondary'] ?? '#1F2937';
        $badge = $layout['badge'] ?? $accent;
        $backgroundImage = $layout['background_image'] ?? null;

        if (! $backgroundImage && ! empty($customization['background_preset']) && is_string($customization['background_preset'])) {
            $backgroundImage = $this->resolveBackgroundPreset($customization['background_preset']);
        }

        $resolved = [];

        foreach ($elements as $element) {
            if (! is_array($element) || empty($element['id']) || (! isset($element['type']) && ! isset($element['role']))) {
                continue;
            }

            $type = (string) ($element['role'] ?? $element['type']);
            $dimensions = $this->normalizeElementDimensions($type, $element, $width, $height);
            $content = $this->resolveElementContent($type, $customization, $layout, $eventName, $tokenUrl, $element['content'] ?? null);

            $style = [
                'position' => 'absolute',
                'left' => sprintf('%dpx', $dimensions['x']),
                'top' => sprintf('%dpx', $dimensions['y']),
                'width' => sprintf('%dpx', $dimensions['width']),
                'height' => sprintf('%dpx', $dimensions['height']),
            ];

            if (isset($element['rotation']) && is_numeric($element['rotation']) && (float) $element['rotation'] !== 0.0) {
                $style['transform'] = sprintf('rotate(%sdeg)', (float) $element['rotation']);
                $style['transform-origin'] = 'center center';
            }

            $fontSize = isset($element['font_size']) && is_numeric($element['font_size'])
                ? max(8, min(160, (float) $element['font_size']))
                : null;

            $lineHeight = isset($element['line_height']) && is_numeric($element['line_height'])
                ? max(0.5, min(3.0, (float) $element['line_height']))
                : null;

            $letterSpacing = isset($element['letter_spacing']) && is_numeric($element['letter_spacing'])
                ? (float) $element['letter_spacing']
                : null;

            $resolved[] = [
                'id' => (string) $element['id'],
                'type' => $type,
                'content' => $content,
                'align' => $element['align'] ?? null,
                'font_size' => $fontSize,
                'line_height' => $lineHeight,
                'letter_spacing' => $letterSpacing,
                'font_family' => $element['font_family'] ?? null,
                'fill' => $element['fill'] ?? null,
                'style' => $style,
                'style_string' => $this->styleToString($style),
                'width' => $dimensions['width'],
                'height' => $dimensions['height'],
                'asset' => match ($type) {
                    'qr' => $qrPngDataUri,
                    'logo' => $customization['logo_data_url'] ?? $customization['logo_url'] ?? $layout['logo_url'] ?? null,
                    default => null,
                },
            ];
        }

        if (empty($resolved)) {
            return null;
        }

        return [
            'width' => $width,
            'height' => $height,
            'background' => $layout['background'] ?? '#FFFFFF',
            'background_image' => $backgroundImage,
            'background_gradient' => $layout['background_gradient'] ?? null,
            'accent' => $accent,
            'text' => $text,
            'secondary' => $secondary,
            'badge' => $badge,
            'qr_src' => $qrPngDataUri,
            'logo_src' => $customization['logo_data_url'] ?? $customization['logo_url'] ?? $layout['logo_url'] ?? null,
            'elements' => $resolved,
        ];
    }

    private function resolveBackgroundPreset(string $preset): ?string
    {
        $path = self::BACKGROUND_PRESETS[$preset] ?? null;

        if (! $path) {
            return null;
        }

        $fullPath = public_path($path);

        if (! file_exists($fullPath) || ! is_readable($fullPath)) {
            return null;
        }

        $mime = mime_content_type($fullPath) ?: 'image/png';
        $data = @file_get_contents($fullPath);

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

        return 'data:'.$mime.';base64,'.base64_encode($data);
    }

    private function resolveElementContent(string $type, array $customization, array $layout, string $eventName, string $tokenUrl, $fallback = null): ?string
    {
        return match ($type) {
            'headline' => $customization['headline'] ?? $eventName,
            'subtitle' => $customization['subtitle'] ?? ($layout['subtitle'] ?? ''),
            'description' => $customization['description'] ?? ($layout['description'] ?? ''),
            'link' => (isset($customization['link_label']) && trim($customization['link_label']) !== '')
                ? $customization['link_label']
                : $tokenUrl,
            'badge' => $customization['badge_label'] ?? ($layout['badge_label'] ?? 'Digitale Gästebox'),
            'cta' => $customization['cta_label'] ?? ($layout['cta_label'] ?? 'Scan mich & starte direkt'),
            default => $fallback !== null ? (string) $fallback : null,
        };
    }

    private function styleToString(array $style): string
    {
        return implode(';', array_map(
            static fn ($key, $value) => sprintf('%s:%s', $key, $value),
            array_keys($style),
            $style
        ));
    }

    private function normalizeElementDimensions(string $type, array $element, int $canvasWidth, int $canvasHeight): array
    {
        $minDimensions = match ($type) {
            'qr' => ['width' => 240, 'height' => 240],
            'logo' => ['width' => 140, 'height' => 100],
            'badge' => ['width' => 160, 'height' => 60],
            'cta' => ['width' => 200, 'height' => 80],
            'text' => ['width' => 200, 'height' => 120],
            default => ['width' => 160, 'height' => 80],
        };

        $width = (int) round((float) ($element['width'] ?? $minDimensions['width']));
        $height = (int) round((float) ($element['height'] ?? $minDimensions['height']));

        $width = max($minDimensions['width'], min($width, $canvasWidth));
        $height = max($minDimensions['height'], min($height, $canvasHeight));

        if ($type === 'qr') {
            $size = (int) round(max($width, $height));
            $size = max(240, min($size, min($canvasWidth, $canvasHeight)));
            $width = $height = $size;
        }

        $maxX = max($canvasWidth - $width, 0);
        $maxY = max($canvasHeight - $height, 0);

        $x = (int) round((float) ($element['x'] ?? 0));
        $y = (int) round((float) ($element['y'] ?? 0));

        $x = max(0, min($x, $maxX));
        $y = max(0, min($y, $maxY));

        return [
            'x' => $x,
            'y' => $y,
            'width' => $width,
            'height' => $height,
        ];
    }
}
