<?php

declare(strict_types=1);

namespace App\Modules\PublicInvitation;

use App\Services\QrCodeService;

/**
 * PublicInvitationService — Service coordinating guest identity resolution,
 * QR code rendering, and personal invitation URL generation.
 *
 * No SQL database queries or HTML rendering.
 *
 * @package Inveetaire
 * @ref     PILOT-003
 */
class PublicInvitationService
{
    private PublicInvitationModel $model;

    public function __construct()
    {
        $this->model = new PublicInvitationModel();
    }

    /**
     * Look up a workspace by its URL slug.
     * Used by LivePreviewController to resolve and verify workspace ownership.
     *
     * @param string $slug
     * @return array|null Workspace row or null if not found.
     */
    public function getWorkspaceBySlug(string $slug): ?array
    {
        return $this->model->getWorkspaceBySlug($slug);
    }

    /**
     * Resolve a public guest token and workspace slug into a structured invitation payload.
     *
     * @param string $workspaceSlug
     * @param string $token
     * @return array|null Structured guest payload or null if invalid token/slug.
     */
    public function resolveGuestIdentity(string $workspaceSlug, string $token): ?array
    {
        // 1. Resolve workspace
        $workspace = $this->model->getWorkspaceBySlug($workspaceSlug);
        if ($workspace === null) {
            return null;
        }

        $workspaceId = (int) $workspace['id'];

        // 2. Resolve guest within workspace context (strict workspace isolation)
        $guest = $this->model->getGuestByToken($workspaceId, $token);
        if ($guest === null) {
            return null;
        }

        $guestId = (int) $guest['id'];

        // 3. Resolve active invitation config
        $invitation = $this->model->getInvitationByWorkspace($workspaceId);
        if ($invitation === null) {
            return null;
        }

        // 3b. Verify workspace is 'active' and invitation is 'published' for public access
        if (($workspace['workspace_status'] ?? '') !== 'active' || ($invitation['invitation_status'] ?? '') !== 'published') {
            return null;
        }

        // 3c. Record invitation open timestamp & view count (non-blocking analytics update)
        try {
            $guestModel = new \App\Modules\Guest\GuestModel();
            $guestModel->recordInvitationOpen($workspaceId, $guestId);
        } catch (\Throwable $e) {
            // Fail-safe: ignore analytics recording glitches to keep invitation rendering resilient
        }

        // 4. Resolve multi-event assignments
        $events = $this->model->getAssignedEvents($workspaceId, $guestId);

        // 5. Generate personal URL
        $personalUrl = $this->generatePersonalUrl($workspaceSlug, $token);

        // 6. Coordinate QR Code generation (Base64 PNG/SVG payload)
        $qrCodeSvg = \App\Services\QrCodeService::generateGuestSvg($token);
        $qrCodeBase64 = 'data:image/svg+xml;base64,' . base64_encode($qrCodeSvg);

        // Return structured, clean resolved representation (No HTML, No SQL)
        return [
            'workspace'    => $workspace,
            'guest'        => $guest,
            'invitation'   => $invitation,
            'events'       => $events,
            'personal_url' => $personalUrl,
            'qr_code'      => $qrCodeBase64
        ];
    }

    /**
     * Resolve all components and compile them using the InvitationRenderer.
     *
     * @param string $workspaceSlug
     * @param string $token
     * @return array|null Final compiled view payload or null.
     */
    public function getRenderedPayload(string $workspaceSlug, string $token): ?array
    {
        $identity = $this->resolveGuestIdentity($workspaceSlug, $token);
        if ($identity === null) {
            return null;
        }

        $invitation = $identity['invitation'];
        $themeVersionId = (int) ($invitation['theme_version_id'] ?? 1);
        $invitationId = (int) $invitation['id'];

        // Load Theme details
        $theme = $this->model->getThemeByVersionId($themeVersionId);
        if ($theme === null) {
            // Default fallback structure if version table data is incomplete
            $theme = [
                'id'          => 1,
                'name'        => 'Klasik Putih',
                'slug'        => 'klasik-putih',
                'description' => 'Default theme fallback'
            ];
        }

        // Load Sections layout
        $sections = $this->model->getInvitationSections($invitationId);

        // Compile through the renderer
        $renderer = new InvitationRenderer();
        $compiled = $renderer->prepareRenderData(
            $theme,
            $sections,
            $identity['guest'],
            $invitation,
            $identity['events']
        );

        // Add personal URL and QR code back into the final payload
        $compiled['workspace']    = $identity['workspace'];
        $compiled['personal_url'] = $identity['personal_url'];
        $compiled['qr_code'] = $identity['qr_code'];


        // Retrieve previous RSVP/Wish records if present
        $guestId = (int) $identity['guest']['id'];
        $compiled['rsvp'] = $this->model->getGuestRsvp($guestId);
        $compiled['wish'] = $this->model->getGuestWish($guestId);

        // Retrieve wedding gift data
        $giftSettings = $this->model->getGiftSettings($invitationId);
        if ($giftSettings && !empty($giftSettings['is_enabled'])) {
            $compiled['gift_settings'] = $giftSettings;
            $compiled['gift_accounts'] = $this->model->getGiftAccounts((int) $giftSettings['id']);
        } else {
            $compiled['gift_settings'] = null;
            $compiled['gift_accounts'] = [];
        }

        return $compiled;
    }

    /**
     * Generate the Guest's personal invitation URL dynamically.
     * Format: https://{host}/{workspace_slug}/i/{guest_token}
     *
     * @param string $workspaceSlug
     * @param string $token
     * @return string
     */
    public function generatePersonalUrl(string $workspaceSlug, string $token): string
    {
        return url("/{$workspaceSlug}/i/{$token}");
    }

    /**
     * Resolve and compile the invitation render payload for Live Preview mode.
     *
     * Differences from getRenderedPayload():
     *   - Accepts both 'draft' and 'published' invitation_status.
     *   - Does NOT call recordInvitationOpen() — zero analytics writes.
     *   - Does NOT require a guest token — injects a virtual guest object.
     *   - Returns $payload['live_preview'] = true so the theme renderer can
     *     receive the context flag (ThemeRenderer extracts it via extract()).
     *
     * This method MUST NEVER write to the database.
     *
     * @param int $workspaceId The couple's active workspace ID (from session).
     * @return array|null Compiled render payload or null if workspace/invitation missing.
     */
    public function getRenderedPayloadForPreview(int $workspaceId): ?array
    {
        // 1. Resolve workspace by ID (not slug) — couple already validated by WorkspaceMiddleware
        $workspace = $this->model->getWorkspaceById($workspaceId);
        if ($workspace === null) {
            return null;
        }

        $workspaceSlug = (string) ($workspace['slug'] ?? '');

        // 2. Resolve invitation — bypass publication status check
        $invitation = $this->model->getInvitationByWorkspace($workspaceId);
        if ($invitation === null) {
            return null;
        }

        $themeVersionId = (int) ($invitation['theme_version_id'] ?? 1);
        $invitationId   = (int) $invitation['id'];

        // 3. Inject a virtual guest — no DB lookup, no real token
        $virtualGuest = [
            'id'           => 0,
            'workspace_id' => $workspaceId,
            'guest_code'   => 'preview',
            'full_name'    => 'Live Preview',
            'honorific'    => '',
            'side'         => 'preview',
            'guest_group'  => 'Preview',
            'invited_pax'  => 2,
            'phone_number' => '',
            'notes'        => '',
        ];

        // 4. Load Theme details
        $theme = $this->model->getThemeByVersionId($themeVersionId);
        if ($theme === null) {
            $theme = [
                'id'          => 1,
                'name'        => 'Klasik Putih',
                'slug'        => 'klasik-putih',
                'description' => 'Default theme fallback'
            ];
        }

        // 5. Load all wedding events for this workspace (virtual guest sees all)
        $events = $this->model->getAllEvents($workspaceId);

        // 6. Load invitation sections
        $sections = $this->model->getInvitationSections($invitationId);

        // 7. Compile through the standard InvitationRenderer (zero changes to renderer)
        $renderer = new InvitationRenderer();
        $compiled  = $renderer->prepareRenderData(
            $theme,
            $sections,
            $virtualGuest,
            $invitation,
            $events
        );

        // 8. Assemble final payload — mirror production structure exactly
        $compiled['workspace']    = $workspace;
        $compiled['personal_url'] = url("/{$workspaceSlug}/i/preview");
        $compiled['qr_code']      = ''; // No real QR in preview
        $compiled['rsvp']         = null; // Virtual guest has no prior RSVP
        $compiled['wish']         = null; // Virtual guest has no prior wish
        $compiled['live_preview'] = true; // Context flag — received by theme as $live_preview

        return $compiled;
    }
}
