<?php

declare(strict_types=1);

namespace App\Modules\Operation;

use App\Core\BaseController;
use App\Core\Request;
use App\Core\Session;
use App\Core\ErrorHandler;

/**
 * OperationController — HTTP Handler for Wedding Day operations (scanner, check-in, dashboard).
 *
 * Thin controller design coordinating request routing, redirects, session messages, and views mapping.
 *
 * @package Inveetaire
 * @ref     RC-013
 */
class OperationController extends BaseController
{
    protected string $layout = 'crew';
    private OperationService $service;
    private OperationModel $model;

    public function __construct()
    {
        // Dynamically set layout based on the active route prefix role snapshot
        $role = Session::get('role_snapshot') === 'crew' ? 'crew' : 'couple';
        $this->layout = $role;

        parent::__construct();
        $this->service = new OperationService();
        $this->model = new OperationModel();
    }

    /**
     * Show the Guest Check-in page (Scanner + Search).
     * Route: GET /app/{role}/operation/event/{event_id}/checkin
     *
     * @param Request $request
     * @return void
     */
    public function showScanner(Request $request): void
    {
        if ($request->get('ajax') === '1') {
            $this->layout = 'ajax';
        }
        $workspaceId = (int) Session::get('active_workspace_id');
        $eventId = (int) $request->param('event_id');

        // Ensure pilot events exist and guests are migrated
        $this->model->ensurePilotEventsExist($workspaceId);
        $this->model->migrateExistingGuestsEvents($workspaceId);

        // If no event context is selected, redirect to the first available wedding event
        if ($eventId <= 0) {
            $db = \App\Core\Database::getConnection();
            $stmt = $db->prepare('SELECT id FROM wedding_events WHERE workspace_id = :workspace_id AND is_deleted = 0 ORDER BY sort_order ASC LIMIT 1');
            $stmt->execute(['workspace_id' => $workspaceId]);
            $firstEventId = (int) $stmt->fetchColumn();

            $role = Session::get('role_snapshot') === 'crew' ? 'crew' : 'couple';
            if ($firstEventId > 0) {
                $this->redirect("/app/{$role}/operation/event/{$firstEventId}/checkin");
            } else {
                $this->redirect("/app/{$role}/dashboard");
            }
            return;
        }

        $event = $this->model->getEventById($workspaceId, $eventId);
        if ($event === null) {
            ErrorHandler::renderNotFound();
            return;
        }

        // Fetch event specific metrics
        $stats = $this->model->getEventAttendanceStats($workspaceId, $eventId);

        // Search Guest Query logic
        $search = trim((string) $request->get('search'));
        $searchResults = [];
        if ($search !== '') {
            $db = \App\Core\Database::getConnection();
            $stmtSearch = $db->prepare('
                SELECT id, guest_code, full_name, phone_number, guest_group, invited_pax 
                FROM guests 
                WHERE workspace_id = :workspace_id 
                  AND is_deleted = 0 
                  AND (full_name LIKE :query1 OR phone_number LIKE :query2 OR guest_code LIKE :query3)
                ORDER BY full_name ASC LIMIT 50
            ');
            $stmtSearch->execute([
                'workspace_id' => $workspaceId,
                'query1'       => '%' . $search . '%',
                'query2'       => '%' . $search . '%',
                'query3'       => '%' . $search . '%'
            ]);
            $searchResults = $stmtSearch->fetchAll(\PDO::FETCH_ASSOC);
        }

        $this->render('operation/scanner', [
            'title'          => 'Guest Check-in — ' . $event['event_name'],
            'event'          => $event,
            'stats'          => $stats,
            'search'         => $search,
            'searchResults'  => $searchResults,
            'errors'         => Session::get('errors') ?? [],
            'success'        => Session::get('success') ?? null
        ]);

        Session::set('errors', null);
        Session::set('success', null);
    }

    /**
     * Handle scanner input token submission.
     * Route: POST /app/{role}/operation/event/{event_id}/checkin
     *
     * @param Request $request
     * @return void
     */
    public function submitScanner(Request $request): void
    {
        $workspaceId = (int) Session::get('active_workspace_id');
        $eventId = (int) $request->param('event_id');
        $token = trim((string) $request->post('guest_token'));
        $role = Session::get('role_snapshot') === 'crew' ? 'crew' : 'couple';

        if ($token === '') {
            Session::set('errors', ['guest_token' => 'Scanned code cannot be blank.']);
            $this->redirect("/app/{$role}/operation/event/{$eventId}/checkin");
            return;
        }

        if (!preg_match('/^[A-Z0-9]{5}$/', $token)) {
            Session::set('errors', ['guest_token' => 'Invalid invitation code format. Must be 5 uppercase alphanumeric characters.']);
            $this->redirect("/app/{$role}/operation/event/{$eventId}/checkin");
            return;
        }

        try {
            // Resolve to verify guest exists before opening profile
            $this->service->resolveGuest($workspaceId, $token);
            $this->redirect("/app/{$role}/operation/event/{$eventId}/guest/{$token}");
        } catch (\InvalidArgumentException $e) {
            Session::set('errors', ['guest_token' => 'Invalid code scanned: ' . $token]);
            $this->redirect("/app/{$role}/operation/event/{$eventId}/checkin");
        }
    }

    /**
     * Display resolved guest check-in dashboard details.
     * Route: GET /app/{role}/operation/event/{event_id}/guest/{guest_code}
     *
     * @param Request $request
     * @return void
     */
    public function showGuest(Request $request): void
    {
        $workspaceId = (int) Session::get('active_workspace_id');
        $eventId = (int) $request->param('event_id');
        $token = (string) $request->param('guest_code');

        if (!preg_match('/^[A-Z0-9]{5}$/', $token)) {
            ErrorHandler::renderNotFound();
            return;
        }

        $event = $this->model->getEventById($workspaceId, $eventId);
        if ($event === null) {
            ErrorHandler::renderNotFound();
            return;
        }

        try {
            $payload = $this->service->resolveGuest($workspaceId, $token);
            $guest = $payload['guest'];
            $allowedEvents = $payload['events']; // List of events guest is invited to
            
            // Check if guest is explicitly invited to this event
            $isInvited = false;
            foreach ($allowedEvents as $ae) {
                if ((int) $ae['id'] === $eventId) {
                    $isInvited = true;
                    break;
                }
            }

            // Check if guest is already checked-in for this event
            $checkinRecord = $this->model->getCheckinRecord($workspaceId, (int) $guest['id'], $eventId);

            $printerConfig = $this->model->getPrinterConfig($workspaceId);

            $this->render('operation/guest', [
                'title'          => 'Guest Check-in Details',
                'guest'          => $guest,
                'event'          => $event,
                'allowed_events' => $allowedEvents,
                'is_invited'     => $isInvited,
                'checkin_record' => $checkinRecord,
                'printer_config' => $printerConfig,
                'has_wish'       => $payload['has_wish'],
                'errors'         => Session::get('errors') ?? [],
                'success'        => Session::get('success') ?? null
            ]);
        } catch (\InvalidArgumentException $e) {
            ErrorHandler::renderNotFound();
        } finally {
            Session::set('errors', null);
            Session::set('success', null);
        }
    }

    /**
     * Process check-in or manual override for a guest at a specific event.
     * Route: POST /app/{role}/operation/event/{event_id}/guest/{guest_code}/checkin
     *
     * @param Request $request
     * @return void
     */
    public function checkin(Request $request): void
    {
        $workspaceId = (int) Session::get('active_workspace_id');
        $eventId = (int) $request->param('event_id');
        $token = (string) $request->param('guest_code');
        $role = Session::get('role_snapshot') === 'crew' ? 'crew' : 'couple';

        if (!preg_match('/^[A-Z0-9]{5}$/', $token)) {
            ErrorHandler::renderNotFound();
            return;
        }

        try {
            $payload = $this->service->resolveGuest($workspaceId, $token);
            $guest = $payload['guest'];
            $guestId = (int) $guest['id'];
            $operatorId = (int) Session::get('user_id');

            // Fetch operator username/email
            $db = \App\Core\Database::getConnection();
            $stmtOp = $db->prepare('SELECT email FROM users WHERE id = :id LIMIT 1');
            $stmtOp->execute(['id' => $operatorId]);
            $operatorName = $stmtOp->fetchColumn() ?: 'Operator';

            $isOverride = $request->post('is_override') === '1';

            if ($isOverride) {
                // Perform Manual Override
                $reason = trim((string) $request->post('override_reason'));
                $notes = trim((string) $request->post('override_notes'));

                if ($reason === '') {
                    Session::set('errors', ['override_reason' => 'An override reason is required.']);
                    $this->redirect("/app/{$role}/operation/event/{$eventId}/guest/{$token}");
                    return;
                }

                $notesFormatted = "[OVERRIDE] Reason: " . $reason . ($notes !== '' ? ' | Notes: ' . $notes : '');

                $success = $this->model->checkInGuestForEvent($workspaceId, $guestId, $eventId, $operatorId, 'manual', $notesFormatted);

                if ($success) {
                    // Create audit log record
                    $stmtLog = $db->prepare('
                        INSERT INTO audit_logs (actor_type, actor_id, actor_display_name, workspace_id, action_category, action_type, target_entity_type, target_entity_id, after_state)
                        VALUES (:actor_type, :actor_id, :actor_name, :workspace_id, "checkin", "checkin.override", "guest", :guest_id, :after_state)
                    ');
                    $stmtLog->execute([
                        'actor_type'   => $role,
                        'actor_id'     => $operatorId,
                        'actor_name'   => $operatorName,
                        'workspace_id' => $workspaceId,
                        'guest_id'     => $guestId,
                        'after_state'  => json_encode([
                            'wedding_event_id' => $eventId,
                            'reason'           => $reason,
                            'notes'            => $notes,
                            'timestamp'        => date('Y-m-d H:i:s')
                        ])
                    ]);

                    Session::set('success', 'Manual check-in override completed successfully.');
                } else {
                    Session::set('errors', ['checkin' => 'Guest already checked in.']);
                }
            } else {
                // Perform standard check-in
                $notes = trim((string) $request->post('checkin_notes'));
                $success = $this->model->checkInGuestForEvent($workspaceId, $guestId, $eventId, $operatorId, 'manual', $notes);

                if ($success) {
                    // Standard audit log entry
                    $stmtLog = $db->prepare('
                        INSERT INTO audit_logs (actor_type, actor_id, actor_display_name, workspace_id, action_category, action_type, target_entity_type, target_entity_id, after_state)
                        VALUES (:actor_type, :actor_id, :actor_name, :workspace_id, "checkin", "checkin.success", "guest", :guest_id, :after_state)
                    ');
                    $stmtLog->execute([
                        'actor_type'   => $role,
                        'actor_id'     => $operatorId,
                        'actor_name'   => $operatorName,
                        'workspace_id' => $workspaceId,
                        'guest_id'     => $guestId,
                        'after_state'  => json_encode([
                            'wedding_event_id' => $eventId,
                            'notes'            => $notes,
                            'timestamp'        => date('Y-m-d H:i:s')
                        ])
                    ]);

                    Session::set('success', 'Guest checked in successfully.');
                } else {
                    Session::set('errors', ['checkin' => 'Guest already checked in.']);
                }
            }
        } catch (\InvalidArgumentException $e) {
            ErrorHandler::renderNotFound();
            return;
        }

        $this->redirect("/app/{$role}/operation/event/{$eventId}/guest/{$token}");
    }

    /**
     * Process check-in rejection for a guest.
     * Route: POST /app/{role}/operation/event/{event_id}/guest/{guest_code}/reject
     *
     * @param Request $request
     * @return void
     */
    public function reject(Request $request): void
    {
        $workspaceId = (int) Session::get('active_workspace_id');
        $eventId = (int) $request->param('event_id');
        $token = (string) $request->param('guest_code');
        $role = Session::get('role_snapshot') === 'crew' ? 'crew' : 'couple';

        if (!preg_match('/^[A-Z0-9]{5}$/', $token)) {
            ErrorHandler::renderNotFound();
            return;
        }

        try {
            $payload = $this->service->resolveGuest($workspaceId, $token);
            $guest = $payload['guest'];
            $guestId = (int) $guest['id'];
            $operatorId = (int) Session::get('user_id');

            // Fetch operator username/email
            $db = \App\Core\Database::getConnection();
            $stmtOp = $db->prepare('SELECT email FROM users WHERE id = :id LIMIT 1');
            $stmtOp->execute(['id' => $operatorId]);
            $operatorName = $stmtOp->fetchColumn() ?: 'Operator';

            // Create check-in reject audit log record
            $stmtLog = $db->prepare('
                INSERT INTO audit_logs (actor_type, actor_id, actor_display_name, workspace_id, action_category, action_type, target_entity_type, target_entity_id, after_state)
                VALUES (:actor_type, :actor_id, :actor_name, :workspace_id, "checkin", "checkin.rejected", "guest", :guest_id, :after_state)
            ');
            $stmtLog->execute([
                'actor_type'   => $role,
                'actor_id'     => $operatorId,
                'actor_name'   => $operatorName,
                'workspace_id' => $workspaceId,
                'guest_id'     => $guestId,
                'after_state'  => json_encode([
                    'wedding_event_id' => $eventId,
                    'action'           => 'rejected',
                    'timestamp'        => date('Y-m-d H:i:s')
                ])
            ]);

            Session::set('success', 'Guest entry restricted and rejected.');
        } catch (\Exception $e) {
            Session::set('errors', ['reject' => $e->getMessage()]);
        }

        $this->redirect("/app/{$role}/operation/event/{$eventId}/guest/{$token}");
    }

    /**
     * Cancel an existing check-in for a guest.
     * Route: POST /app/{role}/operation/event/{event_id}/guest/{guest_code}/cancel_checkin
     *
     * @param Request $request
     * @return void
     */
    public function cancelCheckin(Request $request): void
    {
        $workspaceId = (int) Session::get('active_workspace_id');
        $eventId = (int) $request->param('event_id');
        $token = (string) $request->param('guest_code');
        $role = Session::get('role_snapshot') === 'crew' ? 'crew' : 'couple';

        if (!preg_match('/^[A-Z0-9]{5}$/', $token)) {
            ErrorHandler::renderNotFound();
            return;
        }

        try {
            $payload = $this->service->resolveGuest($workspaceId, $token);
            $guest = $payload['guest'];
            $guestId = (int) $guest['id'];
            $operatorId = (int) Session::get('auth_user_id');

            // Fetch operator username/email
            $db = \App\Core\Database::getConnection();
            $stmtOp = $db->prepare('SELECT email FROM users WHERE id = :id LIMIT 1');
            $stmtOp->execute(['id' => $operatorId]);
            $operatorName = $stmtOp->fetchColumn() ?: 'Operator';

            $reason = trim((string) $request->post('cancel_reason'));

            $success = $this->model->cancelCheckInGuestForEvent($workspaceId, $guestId, $eventId);

            if ($success) {
                // Audit log entry
                $stmtLog = $db->prepare('
                    INSERT INTO audit_logs (actor_type, actor_id, actor_display_name, workspace_id, action_category, action_type, target_entity_type, target_entity_id, after_state)
                    VALUES (:actor_type, :actor_id, :actor_name, :workspace_id, "checkin", "checkin.cancel", "guest", :guest_id, :after_state)
                ');
                $stmtLog->execute([
                    'actor_type'   => $role,
                    'actor_id'     => $operatorId,
                    'actor_name'   => $operatorName,
                    'workspace_id' => $workspaceId,
                    'guest_id'     => $guestId,
                    'after_state'  => json_encode([
                        'wedding_event_id' => $eventId,
                        'reason'           => $reason,
                        'timestamp'        => date('Y-m-d H:i:s')
                    ])
                ]);

                Session::set('success', 'Check-in cancelled successfully.');
            } else {
                Session::set('errors', ['checkin' => 'Failed to cancel check-in.']);
            }
        } catch (\Exception $e) {
            Session::set('errors', ['checkin' => $e->getMessage()]);
        }

        $this->redirect("/app/{$role}/operation/event/{$eventId}/guest/{$token}");
    }

    /**
     * Show live attendance statistics dashboard.
     * Route: GET /app/crew/operation/dashboard
     *
     * @param Request $request
     * @return void
     */
    public function showDashboard(Request $request): void
    {
        // For general crew landing, redirect to event specific check-in
        $this->showScanner($request);
    }
}
