<?php

namespace App\Controllers;

use App\Models\EventClientModel;
use App\Models\EventModel;
use App\Models\EventOrganizerModel;
use App\Models\EventOrganizerPasswordCodeModel;
use App\Models\EventRegistrationFieldModel;
use App\Models\EventRegistrationModel;
use App\Models\GuestModel;
use Config\Lookups;
use Throwable;

class EventController extends BaseController
{
    /**
     * The mockup's stat cards included per-event Revenue — this schema
     * has no link between an event and a folio/order (folio_line_items'
     * "event" category exists, but nothing ties a specific charge back
     * to a specific event — see the CreateEventsTable/CreateFoliosTable
     * migrations), so it can't be computed honestly. Replaced with real
     * counts (Total/Upcoming/Confirmed/Inquiries) instead of a fabricated
     * revenue figure.
     */
    public function index()
    {
        $events = (new EventModel())->withClient()->orderBy('events.event_date', 'desc')->findAll();

        $today = date('Y-m-d');
        $in30  = date('Y-m-d', strtotime('+30 days'));

        $statusCounts = [];
        foreach (array_keys((new Lookups())->eventStatuses) as $key) {
            $statusCounts[$key] = (new EventModel())->where('status', $key)->countAllResults();
        }

        return view('events/index', [
            'pageTitle'    => 'Events',
            'pageSubtitle' => 'Manage events, bookings and inquiries.',
            'events'       => $events,
            'lookups'      => new Lookups(),
            'stats'        => [
                'total'    => count($events),
                'upcoming' => (new EventModel())->where('event_date >=', $today)->where('event_date <=', $in30)->where('status !=', 'cancelled')->countAllResults(),
                'statusCounts' => $statusCounts,
            ],
        ]);
    }

    public function new()
    {
        return view('events/form', [
            'pageTitle'      => 'New Event',
            'event'          => null,
            'guestId'        => (int) ($this->request->getGet('guest_id') ?? 0),
            'eventClientId'  => (int) ($this->request->getGet('event_client_id') ?? 0),
            'lookups'        => new Lookups(),
        ]);
    }

    public function create()
    {
        $guestId       = (int) $this->request->getPost('guest_id');
        $eventClientId = (int) $this->request->getPost('event_client_id');

        if (($guestId > 0) === ($eventClientId > 0)) {
            return redirect()->back()->withInput()->with('error', 'An event needs exactly one client: an existing guest OR an event client, not both/neither.');
        }

        $model = new EventModel();
        $data = [
            'event_name'          => $this->request->getPost('event_name'),
            'event_type'          => $this->request->getPost('event_type') ?: null,
            'guest_id'            => $guestId > 0 ? $guestId : null,
            'event_client_id'     => $eventClientId > 0 ? $eventClientId : null,
            'event_date'          => $this->request->getPost('event_date'),
            'expected_headcount'  => $this->request->getPost('expected_headcount') ?: null,
            'venue_space'         => $this->request->getPost('venue_space') ?: null,
            'description'         => $this->request->getPost('description') ?: null,
            'status'              => 'inquiry',
            'contract_notes'      => $this->request->getPost('contract_notes') ?: null,
        ];

        if (! $model->insert($data)) {
            return $this->redirectWithModelErrors($model);
        }

        $this->logActivity('events', 'Created event "' . $data['event_name'] . '" for ' . $data['event_date'] . '.');

        return redirect()->to('events/' . $model->getInsertID())->with('success', 'Event created.');
    }

    /**
     * Redesigned to match the mockup batch. Client Info panel pulls the
     * full guest/event-client record (phone/email/etc.) rather than just
     * the name withClient() already carries — same "no revenue" honesty
     * call as the list page, no folio/billing tie-in invented here either.
     */
    public function show(int $id)
    {
        $event = (new EventModel())->withClient()->where('events.id', $id)->first();

        if ($event === null) {
            return redirect()->to('events')->with('error', 'Event not found.');
        }

        $guest = $event->guest_id ? (new GuestModel())->find($event->guest_id) : null;
        $eventClient = $event->event_client_id ? (new EventClientModel())->find($event->event_client_id) : null;

        $daysUntil = (int) round((strtotime($event->event_date) - strtotime(date('Y-m-d'))) / 86400);

        $eventModel = new EventModel();
        $token = $eventModel->ensurePublicToken($id);

        $registerUrl = site_url('event-register/' . $token);
        $checkinUrl  = site_url('event-checkin/' . $token);
        $qrDataUri   = $eventModel->registrationQrDataUri($checkinUrl);

        $registrations = (new EventRegistrationModel())->forEvent($id);
        $customFields  = (new EventRegistrationFieldModel())->forEvent($id);
        $organizer     = $event->organizer_id ? (new EventOrganizerModel())->find($event->organizer_id) : null;

        return view('events/show', [
            'pageTitle'      => $event->event_name,
            'event'          => $event,
            'guest'          => $guest,
            'eventClient'    => $eventClient,
            'organizer'      => $organizer,
            'daysUntil'      => $daysUntil,
            'registerUrl'    => $registerUrl,
            'checkinUrl'     => $checkinUrl,
            'qrDataUri'      => $qrDataUri,
            'registrations'  => $registrations,
            'customFields'   => $customFields,
            'lookups'        => new Lookups(),
        ]);
    }

    // ---- Custom registration field management ----

    public function addField(int $eventId)
    {
        if ((new EventModel())->find($eventId) === null) {
            return redirect()->to('events')->with('error', 'Event not found.');
        }

        $fields = new EventRegistrationFieldModel();
        $data = [
            'event_id'    => $eventId,
            'label'       => $this->request->getPost('label'),
            'field_type'  => $this->request->getPost('field_type'),
            'options'     => $this->encodeOptions($this->request->getPost('options')),
            'is_required' => $this->request->getPost('is_required') ? 1 : 0,
            'sort_order'  => $fields->nextSortOrder($eventId),
        ];

        if (! $fields->insert($data)) {
            return $this->redirectWithModelErrors($fields);
        }

        $this->logActivity('events', 'Added registration question "' . $data['label'] . '" to event #' . $eventId . '.');

        return redirect()->to('events/' . $eventId)->with('success', 'Question added.');
    }

    public function deleteField(int $eventId, int $fieldId)
    {
        $fields = new EventRegistrationFieldModel();

        if ($fields->where('event_id', $eventId)->find($fieldId) === null) {
            return redirect()->to('events/' . $eventId)->with('error', 'Question not found.');
        }

        $fields->delete($fieldId);
        $this->logActivity('events', 'Removed a registration question from event #' . $eventId . '.');

        return redirect()->to('events/' . $eventId)->with('success', 'Question removed.');
    }

    public function moveFieldUp(int $eventId, int $fieldId)
    {
        (new EventRegistrationFieldModel())->moveUp($fieldId, $eventId);

        return redirect()->to('events/' . $eventId);
    }

    public function moveFieldDown(int $eventId, int $fieldId)
    {
        (new EventRegistrationFieldModel())->moveDown($fieldId, $eventId);

        return redirect()->to('events/' . $eventId);
    }

    public function registrationAnswers(int $eventId, int $registrationId)
    {
        $event = (new EventModel())->find($eventId);
        $registrationModel = new EventRegistrationModel();
        $registration = $registrationModel->where('event_id', $eventId)->find($registrationId);

        if ($event === null || $registration === null) {
            return redirect()->to('events/' . $eventId)->with('error', 'Registration not found.');
        }

        $fields = (new EventRegistrationFieldModel())->forEvent($eventId);
        $values = $registrationModel->decodedCustomFieldValues($registration);

        return view('events/registration_answers', [
            'pageTitle'    => 'Answers — ' . $registration->full_name,
            'event'        => $event,
            'registration' => $registration,
            'fields'       => $fields,
            'values'       => $values,
            'baseUrl'      => 'events',
        ]);
    }

    /** newline/comma-separated textarea -> JSON-encoded list, or null if blank. Shared shape with EventOrganizerController's identical action. */
    private function encodeOptions(?string $raw): ?string
    {
        if (empty(trim((string) $raw))) {
            return null;
        }

        $options = array_values(array_filter(array_map('trim', preg_split('/[\r\n,]+/', $raw)), static fn ($v) => $v !== ''));

        return empty($options) ? null : json_encode($options);
    }

    public function edit(int $id)
    {
        $event = (new EventModel())->find($id);

        if ($event === null) {
            return redirect()->to('events')->with('error', 'Event not found.');
        }

        return view('events/form', [
            'pageTitle'     => 'Edit Event',
            'event'         => $event,
            'guestId'       => $event->guest_id,
            'eventClientId' => $event->event_client_id,
            'lookups'       => new Lookups(),
        ]);
    }

    public function update(int $id)
    {
        $model = new EventModel();

        if ($model->find($id) === null) {
            return redirect()->to('events')->with('error', 'Event not found.');
        }

        $data = [
            'event_name'         => $this->request->getPost('event_name'),
            'event_type'         => $this->request->getPost('event_type') ?: null,
            'event_date'         => $this->request->getPost('event_date'),
            'expected_headcount' => $this->request->getPost('expected_headcount') ?: null,
            'venue_space'        => $this->request->getPost('venue_space') ?: null,
            'description'        => $this->request->getPost('description') ?: null,
            'status'             => $this->request->getPost('status'),
            'contract_notes'     => $this->request->getPost('contract_notes') ?: null,
        ];

        if (! $model->update($id, $data)) {
            return $this->redirectWithModelErrors($model);
        }

        $this->logActivity('events', 'Updated event "' . $data['event_name'] . '".');

        return redirect()->to('events/' . $id)->with('success', 'Event updated.');
    }

    // ---- Event Organizer invite ----

    /**
     * Creates (or, for a returning organizer being assigned a second
     * event, reuses) an event_organizers login and points this event at
     * it, then texts a set-password link. No password is set here —
     * the organizer completes that themselves via
     * EventOrganizerPasswordController, identical to how they'd
     * self-serve a forgotten password later.
     */
    public function inviteOrganizer(int $eventId)
    {
        $event = (new EventModel())->find($eventId);

        if ($event === null) {
            return redirect()->to('events')->with('error', 'Event not found.');
        }

        $name  = trim((string) $this->request->getPost('organizer_name'));
        $email = trim((string) $this->request->getPost('organizer_email'));
        $phone = trim((string) $this->request->getPost('organizer_phone'));

        if ($name === '' || $email === '' || $phone === '') {
            return redirect()->to('events/' . $eventId)->with('error', 'Organizer name, email, and phone are all required.');
        }

        $organizers = new EventOrganizerModel();
        $existing   = $organizers->findByEmail($email);

        if ($existing !== null) {
            $organizerId = $existing->id;
        } else {
            $data = ['full_name' => $name, 'email' => $email, 'phone' => $phone, 'is_active' => 1];

            if (! $organizers->insert($data)) {
                return $this->redirectWithModelErrors($organizers);
            }

            $organizerId = $organizers->getInsertID();
        }

        (new EventModel())->update($eventId, ['organizer_id' => $organizerId]);

        $this->sendOrganizerCode($organizerId, $phone, 'invite');

        $this->logActivity('events', "Invited {$name} as organizer for \"{$event->event_name}\".");

        return redirect()->to('events/' . $eventId)->with('success', 'Organizer invited — a set-password link was texted to them.');
    }

    public function resendOrganizerInvite(int $eventId)
    {
        $event = (new EventModel())->find($eventId);

        if ($event === null || $event->organizer_id === null) {
            return redirect()->to('events/' . $eventId)->with('error', 'This event has no organizer to re-invite.');
        }

        $organizer = (new EventOrganizerModel())->find($event->organizer_id);

        if ($organizer === null || empty($organizer->phone)) {
            return redirect()->to('events/' . $eventId)->with('error', 'Organizer has no phone number on file.');
        }

        $this->sendOrganizerCode($organizer->id, $organizer->phone, 'invite');
        $this->logActivity('events', "Re-sent the organizer invite for \"{$event->event_name}\".");

        return redirect()->to('events/' . $eventId)->with('success', 'Invite re-sent.');
    }

    private function sendOrganizerCode(int $organizerId, string $phone, string $context): void
    {
        $code = (new EventOrganizerPasswordCodeModel())->generateFor($organizerId);
        $url  = site_url('organizer/set-password');

        try {
            service('hubtelService')->sendSms(
                $phone,
                "You've been invited to manage an event on MaxYields PMS. Set your password at {$url} using code {$code} (expires in 10 minutes).",
            );
        } catch (Throwable $e) {
            log_message('error', 'Event organizer ' . $context . ' SMS failed: ' . $e->getMessage());
        }
    }
}
