<?php

declare(strict_types=1);

namespace App\Modules\Invitation;

use App\Core\Database;
use App\Core\Logger;
use App\Modules\Workspace\WorkspaceService;

/**
 * InvitationService — Service Layer for the Invitation module.
 *
 * Coordinates validation, transactions, updates across tables, and triggers
 * the workspace lifecycle phase evaluation when status transitions.
 *
 * @package Inveetaire
 * @ref     PILOT-001
 */
class InvitationService
{
    private InvitationModel $model;

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

    /**
     * Retrieve workspace details by ID, including the URL slug.
     * Used by the edit controller to build the Live Preview URL.
     *
     * @param int $workspaceId
     * @return array|null
     */
    public function getWorkspaceDetails(int $workspaceId): ?array
    {
        return $this->model->getWorkspace($workspaceId);
    }

    /**
     * Retrieve complete invitation details by workspace ID.
     *
     * @param int $workspaceId
     * @return array
     */
    public function getInvitationDetails(int $workspaceId): array
    {
        $invitation = $this->model->getInvitationByWorkspace($workspaceId);
        if ($invitation === null) {
            throw new \RuntimeException("No invitation initialized for this workspace.");
        }

        $event = $this->model->getPrimaryEvent($workspaceId);
        $hero = $this->model->getHeroSection((int) $invitation['id']);
        $rsvp = $this->model->getRsvpSection((int) $invitation['id']);
        $wishes = $this->model->getWishesSection((int) $invitation['id']);

        // Decode JSON section config safely
        $heroConfig = [];
        if ($hero !== null && isset($hero['section_config'])) {
            $decoded = json_decode((string) $hero['section_config'], true);
            if (is_array($decoded)) {
                $heroConfig = $decoded;
            }
        }

        $rsvpConfig = [];
        if ($rsvp !== null && isset($rsvp['section_config'])) {
            $decodedRsvp = json_decode((string) $rsvp['section_config'], true);
            if (is_array($decodedRsvp)) {
                $rsvpConfig = $decodedRsvp;
            }
        }

        $wishesConfig = [];
        if ($wishes !== null && isset($wishes['section_config'])) {
            $decodedWishes = json_decode((string) $wishes['section_config'], true);
            if (is_array($decodedWishes)) {
                $wishesConfig = $decodedWishes;
            }
        }

        // Parse date and time from primary event date if available, fallback to workspace date
        $weddingDate = (string) ($invitation['wedding_date'] ?? date('Y-m-d'));
        $weddingTime = '12:00'; // Default fallback

        if ($event !== null && isset($event['event_date'])) {
            $dateTime = new \DateTime((string) $event['event_date']);
            $weddingDate = $dateTime->format('Y-m-d');
            $weddingTime = $dateTime->format('H:i');
        }

        $events = $this->model->getAllEvents($workspaceId);

        // Load invitation times (sessions) for each event
        $eventSessionModel = new EventSessionModel();
        foreach ($events as &$evt) {
            $evtId = (int) ($evt['id'] ?? 0);
            $sessions = $eventSessionModel->getSessionsByEventId($workspaceId, $evtId);
            $times = [];
            if (!empty($sessions)) {
                foreach ($sessions as $s) {
                    if (!empty($s['start_time'])) {
                        $times[] = (new \DateTime($s['start_time']))->format('H:i');
                    }
                }
            }
            if (empty($times) && !empty($evt['event_date'])) {
                $times[] = (new \DateTime($evt['event_date']))->format('H:i');
            }
            $evt['invitation_times'] = $times;
        }
        unset($evt);

        return [
            'id'                        => (int) $invitation['id'],
            'workspace_id'              => $workspaceId,
            'theme_version_id'          => (int) $invitation['theme_version_id'],
            'groom_display_name'        => (string) $invitation['groom_display_name'],
            'bride_display_name'        => (string) $invitation['bride_display_name'],
            'love_story'                => (string) $invitation['love_story'],
            'invitation_status'         => (string) $invitation['invitation_status'],
            'events'                    => $events,
            'wedding_date'              => $weddingDate,
            'wedding_time'              => $weddingTime,
            'venue_name'                => (string) ($event['venue_name'] ?? ''),
            'venue_address'             => (string) ($event['venue_address'] ?? ''),
            'maps_url'                  => (string) ($event['maps_url'] ?? ''),
            'cover_image'               => (string) ($heroConfig['cover_image'] ?? ''),
            'hero_quote'                => (string) ($heroConfig['hero_quote'] ?? ''),
            'rsvp_enabled'              => (bool) ($invitation['rsvp_enabled'] ?? ($rsvp['is_visible'] ?? true)),
            'rsvp_closes_at'            => (string) ($invitation['rsvp_closes_at'] ?? ($rsvpConfig['rsvp_deadline'] ?? '')),
            'rsvp_allow_edit'           => (bool) ($invitation['rsvp_allow_edit'] ?? ($rsvpConfig['allow_edit'] ?? true)),
            'rsvp_notes_enabled'        => (bool) ($rsvpConfig['notes_enabled'] ?? true),
            'wishes_enabled'            => (bool) ($invitation['wish_wall_enabled'] ?? ($wishes['is_visible'] ?? true)),
            'wishes_display_public'     => (bool) ($wishesConfig['display_public'] ?? true),
            'wishes_require_moderation' => (bool) ($wishesConfig['require_moderation'] ?? false),
            'wishes_max_char_limit'     => (int) ($wishesConfig['max_char_limit'] ?? 1000),
            'wishes_sort_order'         => (string) ($wishesConfig['sort_order'] ?? 'newest')
        ];
    }

    /**
     * Retrieve all active theme options.
     *
     * @return array
     */
    public function getThemeOptions(): array
    {
        return $this->model->getActiveThemeVersions();
    }

    /**
     * Atomically validate and save invitation updates.
     *
     * @param int   $workspaceId
     * @param array $data
     * @return void
     * @throws \Exception
     */
    public function updateInvitationDetails(int $workspaceId, array $data): void
    {
        // 1. Validate input first
        $validator = new InvitationValidator();
        $errors = $validator->validate($data, $workspaceId);

        if (!empty($errors)) {
            $errorMsg = reset($errors);
            throw new \InvalidArgumentException($errorMsg);
        }

        $db = Database::getConnection();
        $db->beginTransaction();

        try {
            // 2. Update main invitation row
            $this->model->updateInvitation($workspaceId, $data);

            // 3. Save multi-ceremony events if provided
            if (isset($data['events']) && is_array($data['events'])) {
                $this->model->saveEvents($workspaceId, $data);

                // Sync workspace date with primary event date
                $primaryEvt = $this->model->getPrimaryEvent($workspaceId);
                if ($primaryEvt && !empty($primaryEvt['event_date'])) {
                    $primaryDate = (new \DateTime($primaryEvt['event_date']))->format('Y-m-d');
                    $this->model->updateWorkspaceDate($workspaceId, $primaryDate);
                }
            } else if (!empty($data['wedding_date'])) {
                // Legacy single-event fallback
                $weddingDate = trim((string) $data['wedding_date']);
                $weddingTime = trim((string) ($data['wedding_time'] ?? '12:00'));
                $eventDateString = "{$weddingDate} {$weddingTime}:00";

                $this->model->updateWorkspaceDate($workspaceId, $weddingDate);
                $this->model->upsertPrimaryEvent($workspaceId, [
                    'event_date'    => $eventDateString,
                    'venue_name'    => trim((string) ($data['venue_name'] ?? '')),
                    'venue_address' => trim((string) ($data['venue_address'] ?? '')),
                    'maps_url'      => trim((string) ($data['maps_url'] ?? ''))
                ]);
            }

            // 6. Update cover image & hero quote in hero section_config
            $invitation = $this->model->getInvitationByWorkspace($workspaceId);
            if ($invitation !== null) {
                $invId = (int) $invitation['id'];
                $this->model->updateHeroSectionConfig($invId, [
                    'cover_image' => trim((string) ($data['cover_image'] ?? '')),
                    'hero_quote'  => trim((string) ($data['hero_quote'] ?? ''))
                ]);

                // Update RSVP section_config & is_visible
                $rsvpEnabled = isset($data['rsvp_enabled']) ? (bool) $data['rsvp_enabled'] : true;
                $rsvpClosesAt = trim((string) ($data['rsvp_closes_at'] ?? ''));
                $rsvpAllowEdit = isset($data['rsvp_allow_edit']) ? (bool) $data['rsvp_allow_edit'] : true;
                $rsvpNotesEnabled = isset($data['rsvp_notes_enabled']) ? (bool) $data['rsvp_notes_enabled'] : true;

                $this->model->saveRsvpSectionConfig($invId, $rsvpEnabled, [
                    'rsvp_deadline' => $rsvpClosesAt !== '' ? $rsvpClosesAt : null,
                    'allow_edit'    => $rsvpAllowEdit,
                    'notes_enabled' => $rsvpNotesEnabled,
                ]);

                // Update Wishes section_config & is_visible
                $wishesEnabled = isset($data['wishes_enabled']) ? (bool) $data['wishes_enabled'] : true;
                $wishesDisplayPublic = isset($data['wishes_display_public']) ? (bool) $data['wishes_display_public'] : true;
                $wishesRequireMod = isset($data['wishes_require_moderation']) ? (bool) $data['wishes_require_moderation'] : false;
                $wishesMaxChar = max(50, min(5000, (int) ($data['wishes_max_char_limit'] ?? 1000)));
                $wishesSort = in_array(($data['wishes_sort_order'] ?? ''), ['newest', 'oldest'], true) ? $data['wishes_sort_order'] : 'newest';

                $this->model->saveWishesSectionConfig($invId, $wishesEnabled, [
                    'display_public'     => $wishesDisplayPublic,
                    'require_moderation' => $wishesRequireMod,
                    'max_char_limit'     => $wishesMaxChar,
                    'sort_order'         => $wishesSort,
                ]);
            }

            // 7. If status is published, update activated_at to enable active phase
            if ($data['invitation_status'] === 'published') {
                $this->model->updateWorkspaceActivatedAt($workspaceId);
            }

            $db->commit();

            // 8. Re-evaluate workspace operational phase lifecycle post-transaction
            try {
                $workspaceService = new WorkspaceService();
                $workspaceService->evaluatePhase($workspaceId);
            } catch (\Throwable $phaseEx) {
                // Fail-safe: log but do not abort client transaction for phase evaluator glitches
                Logger::error("InvitationService: phase evaluation failed post-commit", [
                    'workspace_id' => $workspaceId,
                    'error'        => $phaseEx->getMessage()
                ]);
            }

        } catch (\Throwable $e) {
            if ($db->inTransaction()) {
                $db->rollBack();
            }
            Logger::error("InvitationService: update failed. Transaction rolled back.", [
                'workspace_id' => $workspaceId,
                'error'        => $e->getMessage()
            ]);
            throw $e;
        }
    }
}
