<?php

declare(strict_types=1);

namespace App\Modules\Workspace;

use App\Core\Database;
use App\Core\Logger;

/**
 * WorkspaceService — Workspace Provisioning Service Layer
 *
 * Implements atomic transactional provisioning workflow (WS-001B).
 * Coordinates validation of slug uniqueness, secure password generation,
 * insertions of child tables, audit log emission, and commit/rollback.
 *
 * @package Inveetaire
 * @ref     SPRINT3_PLANNING.md, SPRINT3_EXECUTION_GUIDE.md
 */
class WorkspaceService
{
    private WorkspaceModel $model;

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

    /**
     * Atomically provision a new workspace and Couple user and default configurations.
     *
     * @param array  $workspaceData Validated workspace data.
     * @param array  $ownerData     Validated couple owner account data.
     * @param array  $crewIds       Optional crew user IDs to assign.
     * @param int    $adminId       Super Admin user ID performing the provisioning.
     * @param string $ipAddress     Super Admin request IP address.
     * @return array{workspace_id: int, user_id: int, email: string, username: string, plain_password: string}
     * @throws \RuntimeException
     */
    public function provision(array $workspaceData, array $ownerData, array $crewIds, int $adminId, string $ipAddress): array
    {
        $slug = trim((string) ($workspaceData['slug'] ?? ''));

        // 1. Verify slug uniqueness
        if ($this->model->slugExists($slug)) {
            throw new \RuntimeException("The URL slug '{$slug}' is already taken.");
        }

        // Verify username uniqueness
        if ($this->model->usernameExists($ownerData['username'])) {
            throw new \RuntimeException("The username '{$ownerData['username']}' is already taken.");
        }

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

        try {
            // 2. Insert Workspace
            $workspaceId = $this->model->insertWorkspace([
                'slug'               => $slug,
                'groom_name'         => $workspaceData['groom_name'],
                'bride_name'         => $workspaceData['bride_name'],
                'wedding_date'       => $workspaceData['wedding_date'],
                'event_type_id'      => (int) $workspaceData['event_type_id'],
                'commercial_plan_id' => (int) $workspaceData['commercial_plan_id'],
                'custom_guest_limit' => (!empty($workspaceData['custom_guest_limit']) && (int) $workspaceData['commercial_plan_id'] === 3) ? (int) $workspaceData['custom_guest_limit'] : null,
                'workspace_status'   => 'draft', // Part D: Initial status is Draft
                'created_by'         => $adminId
            ]);

            // 3. Collect Couple Owner Credentials
            $email = trim((string) ($ownerData['email'] ?? ''));
            $username = trim((string) ($ownerData['username'] ?? ''));
            $plainPassword = (string) $ownerData['password'];
            $passwordHash = password_hash($plainPassword, PASSWORD_DEFAULT);

            // 4. Insert Couple User
            $userId = $this->model->insertUser([
                'email'         => $email,
                'username'      => $username,
                'display_name'  => $ownerData['display_name'],
                'password_hash' => $passwordHash,
                'role_id'       => 2, // Role ID 2 = Couple
                'workspace_id'  => $workspaceId,
                'is_active'     => 1
            ]);

            // 4.5. Assign Crew (if any)
            foreach ($crewIds as $crewUserId) {
                $crewUserId = (int) $crewUserId;
                if ($crewUserId > 0) {
                    $stmtUser = $db->prepare('SELECT display_name, username FROM users WHERE id = ?');
                    $stmtUser->execute([$crewUserId]);
                    $crewUser = $stmtUser->fetch(\PDO::FETCH_ASSOC);
                    $crewName = $crewUser ? ($crewUser['display_name'] ?: $crewUser['username']) : 'Crew';

                    $stmtCrew = $db->prepare('
                        INSERT INTO crew_accounts (workspace_id, user_id, display_name, desk_label, is_active)
                        VALUES (?, ?, ?, ?, 1)
                        ON DUPLICATE KEY UPDATE is_active = 1
                    ');
                    $stmtCrew->execute([$workspaceId, $crewUserId, $crewName, 'Desk']);
                }
            }

            // 5. Insert Retention Policy based on Commercial Plan
            // Plan 1 (Starter) = 180 days active / 365 archive
            // Plan 2 (Pro) = 365 days active / 730 archive
            // Plan 3 (Custom) = 365 days active / 730 archive
            $planId = (int) $workspaceData['commercial_plan_id'];
            $activeDays  = ($planId >= 2) ? 365 : 180;
            $archiveDays = ($planId >= 2) ? 730 : 365;

            $this->model->insertRetentionPolicy([
                'workspace_id'           => $workspaceId,
                'active_retention_days'  => $activeDays,
                'archive_retention_days' => $archiveDays,
                'is_overridden'          => 0
            ]);

            // 6. Insert Invitation
            $invitationId = $this->model->insertInvitation([
                'workspace_id'       => $workspaceId,
                'theme_version_id'   => 1,
                'groom_display_name' => $workspaceData['groom_name'],
                'bride_display_name' => $workspaceData['bride_name'],
                'invitation_status'  => 'draft'
            ]);

            // 7. Insert Default Invitation Sections
            $sections = [
                ['slug' => 'hero',    'visible' => 1, 'order' => 1],
                ['slug' => 'story',   'visible' => 1, 'order' => 2],
                ['slug' => 'gallery', 'visible' => 1, 'order' => 3],
                ['slug' => 'venue',   'visible' => 1, 'order' => 4],
                ['slug' => 'rsvp',    'visible' => 1, 'order' => 5],
                ['slug' => 'wishes',  'visible' => 1, 'order' => 6],
                ['slug' => 'gift',    'visible' => 0, 'order' => 7],
                ['slug' => 'music',   'visible' => 0, 'order' => 8]
            ];
            foreach ($sections as $section) {
                $this->model->insertInvitationSection([
                    'invitation_id' => $invitationId,
                    'section_slug'  => $section['slug'],
                    'is_visible'    => $section['visible'],
                    'sort_order'    => $section['order']
                ]);
            }

            // 8. Insert Default Message Templates
            $templates = [
                [
                    'type' => 'invitation',
                    'text' => 'Hello {{guest_name}}, we invite you to our wedding: {{invitation_url}}'
                ],
                [
                    'type' => 'h1_reminder',
                    'text' => 'Hello {{guest_name}}, this is a reminder that our wedding ceremony is tomorrow.'
                ],
                [
                    'type' => 'wedding_day_reminder',
                    'text' => 'Hello {{guest_name}}, today is our wedding day! We look forward to seeing you.'
                ]
            ];
            foreach ($templates as $template) {
                $this->model->insertMessageTemplate([
                    'workspace_id'  => $workspaceId,
                    'template_type' => $template['type'],
                    'body_text'     => $template['text'],
                    'is_default'    => 1
                ]);
            }

            // 9. Insert Printer Configuration
            $this->model->insertPrinterConfiguration([
                'workspace_id'           => $workspaceId,
                'angpao_label_enabled'   => 0,
                'souvenir_label_enabled' => 0,
                'vip_label_enabled'      => 0,
                'copies_per_checkin'     => 1
            ]);

            // 10. Audit Log
            $this->model->insertAuditLog([
                'actor_type'         => 'super_admin',
                'actor_id'           => $adminId,
                'actor_display_name' => 'Super Admin',
                'workspace_id'       => $workspaceId,
                'action_category'    => 'system',
                'action_type'        => 'workspace.provisioned',
                'target_entity_type' => 'workspace',
                'target_entity_id'   => $workspaceId,
                'after_state'        => json_encode([
                    'slug'               => $slug,
                    'groom_name'         => $workspaceData['groom_name'],
                    'bride_name'         => $workspaceData['bride_name'],
                    'wedding_date'       => $workspaceData['wedding_date'],
                    'commercial_plan_id' => $planId,
                    'event_type_id'      => (int) $workspaceData['event_type_id']
                ]),
                'ip_address'         => $ipAddress
            ]);

            $db->commit();

            return [
                'workspace_id'   => $workspaceId,
                'user_id'        => $userId,
                'email'          => $email,
                'username'       => $username,
                'plain_password' => $plainPassword
            ];

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

    /**
     * Generate printable 12-char hex password.
     *
     * @return string
     */
    private function generateSecurePassword(): string
    {
        return bin2hex(random_bytes(6));
    }

    /**
     * Evaluate the setup checklist for a workspace and calculate progress.
     *
     * @param int $workspaceId
     * @return array
     */
    public function getChecklist(int $workspaceId): array
    {
        // 1. Workspace Provisioned - Always completed
        $step1 = true;

        // 2. Configure Invitation - Complete when status !== 'draft'
        $invStatus = $this->model->getInvitationStatus($workspaceId);
        $step2 = ($invStatus !== null && $invStatus !== 'draft');

        // 3. Select Theme - Complete when theme_version_id is assigned
        $themeVerId = $this->model->getThemeVersionId($workspaceId);
        $step3 = ($themeVerId !== null && $themeVerId > 0);

        // 4. Add First Guest - Complete when guest count > 0
        $guestCount = $this->model->getGuestCount($workspaceId);
        $step4 = ($guestCount > 0);

        // 5. Configure Printer - Complete when config differs from default state
        $printerConfig = $this->model->getPrinterConfig($workspaceId);
        $step5 = false;
        if ($printerConfig !== null) {
            $step5 = (
                (int) ($printerConfig['angpao_label_enabled'] ?? 0) !== 0 ||
                (int) ($printerConfig['souvenir_label_enabled'] ?? 0) !== 0 ||
                (int) ($printerConfig['vip_label_enabled'] ?? 0) !== 0 ||
                (int) ($printerConfig['copies_per_checkin'] ?? 1) !== 1 ||
                ($printerConfig['label_width_mm'] ?? null) !== null ||
                ($printerConfig['label_height_mm'] ?? null) !== null ||
                ($printerConfig['angpao_label_fields'] ?? null) !== null
            );
        }

        // 6. Create First Wedding Event - Complete when wedding event count > 0
        $eventCount = $this->model->getWeddingEventCount($workspaceId);
        $step6 = ($eventCount > 0);

        // 7. Ready To Send - Completed only if steps 1-6 are all satisfied
        $prereqsSatisfied = ($step1 && $step2 && $step3 && $step4 && $step5 && $step6);
        $step7 = $prereqsSatisfied;

        // Calculate progress percentage
        $steps = [
            1 => ['name' => 'Workspace Provisioned',       'completed' => $step1, 'slug' => 'workspace_provisioned'],
            2 => ['name' => 'Configure Invitation',        'completed' => $step2, 'slug' => 'configure_invitation'],
            3 => ['name' => 'Select Theme',               'completed' => $step3, 'slug' => 'select_theme'],
            4 => ['name' => 'Add First Guest',             'completed' => $step4, 'slug' => 'add_first_guest'],
            5 => ['name' => 'Configure Printer',           'completed' => $step5, 'slug' => 'configure_printer'],
            6 => ['name' => 'Create First Wedding Event',  'completed' => $step6, 'slug' => 'create_first_event'],
            7 => ['name' => 'Ready To Send',               'completed' => $step7, 'slug' => 'ready_to_send']
        ];

        $completedCount = 0;
        foreach ($steps as $step) {
            if ($step['completed']) {
                $completedCount++;
            }
        }

        $progressPercentage = (int) round(($completedCount / 7) * 100);

        return [
            'steps'                  => $steps,
            'progress_percentage'    => $progressPercentage,
            'ready_to_send_unlocked' => $prereqsSatisfied
        ];
    }

    /**
     * Evaluate the operational lifecycle phase for a workspace.
     *
     * @param int $workspaceId
     * @return string Current evaluated phase
     */
    public function evaluatePhase(int $workspaceId): string
    {
        $details = $this->model->getWorkspaceDetails($workspaceId);
        if ($details === null) {
            throw new \RuntimeException("Workspace not found during phase evaluation");
        }

        $currentStatus = $details['workspace_status'];
        $weddingDate   = $details['wedding_date'];
        $activatedAt   = $details['activated_at'];

        // Archived and Purged states are terminal and cannot be changed or revived
        if ($currentStatus === 'archived' || $currentStatus === 'purged') {
            return $currentStatus;
        }

        $today = date('Y-m-d');
        $targetPhase = $currentStatus;

        if ($weddingDate < $today) {
            $targetPhase = 'post_event';
        } elseif ($weddingDate === $today) {
            $targetPhase = 'live';
        } else {
            // weddingDate is in the future
            if ($activatedAt !== null) {
                $targetPhase = 'active';
            } else {
                // If activatedAt is null, check configuration state to decide setup vs provisioned
                $invStatus = $this->model->getInvitationStatus($workspaceId);
                $guestCount = $this->model->getGuestCount($workspaceId);
                $eventCount = $this->model->getWeddingEventCount($workspaceId);

                // If they have configured invitations beyond draft, added guests, or created events: setup phase
                if (($invStatus !== null && $invStatus !== 'draft') || $guestCount > 0 || $eventCount > 0) {
                    $targetPhase = 'setup';
                } else {
                    $targetPhase = 'provisioned';
                }
            }
        }

        // Only persist and write log if the status actually changed (prevents loops/unnecessary updates)
        if ($targetPhase !== $currentStatus) {
            $this->model->updateWorkspaceStatus($workspaceId, $targetPhase);

            $this->model->insertAuditLog([
                'actor_type'         => 'system',
                'actor_id'           => null,
                'actor_display_name' => 'System Lifecycle Evaluator',
                'workspace_id'       => $workspaceId,
                'action_category'    => 'system',
                'action_type'        => 'workspace.phase_transition',
                'target_entity_type' => 'workspace',
                'target_entity_id'   => $workspaceId,
                'after_state'        => json_encode([
                    'old_phase' => $currentStatus,
                    'new_phase' => $targetPhase,
                    'reason'    => 'Automatic date/event boundary transition'
                ]),
                'ip_address'         => '127.0.0.1'
            ]);
        }

        return $targetPhase;
    }
}


