<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;

class PushMeetingStartMessagesToChat extends Command
{
    /**
     * The console command name.
     *
     * @var string
     */
    protected $name = 'meetings:push-chat';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Push scheduled meeting messages into chat when meeting time comes.';

    /**
     * Execute the console command.
     *
     * @return void
     */
    public function handle()
    {
        $originalTz = date_default_timezone_get();

        $yesterdaysDate = date('Y-m-d', strtotime('-1 days'));
        $tomorrowsDate = date('Y-m-d', strtotime('+1 days'));

        $appointmentRepository = app('App\\Repositories\\AppointmentRepository');
        $messageRepository = app('App\\Repositories\\MessageRepository');
        $postSeenRepository = app('App\\Repositories\\PostSeenRepository');
        $realtimeRepository = app('App\\Repositories\\RealTimeRepository');
        $messageGroupMembersRepository = app('App\\Repositories\\MessageGroupMembersRepository');
        $communityRepository = app('App\\Repositories\\CommunityRepository');
        $userRepository = app('App\\Repositories\\UserRepository');
        $resumeRepository = app('App\\Repositories\\ResumeRepository');

        // CLI/cron has no controller to set realtime type.
        // The polling endpoint uses type="update".
        $realtimeRepository->setType('update');

        $upcomingAppointments = $appointmentRepository->getUpcomingAppointments($yesterdaysDate, $tomorrowsDate);

        $processed = [];
        $pushedCount = 0;

        foreach ($upcomingAppointments as $appointment) {
            $appointmentId = (int) ($appointment->id ?? 0);
            if ($appointmentId <= 0) {
                continue;
            }

            if (isset($appointment->active) && (int) $appointment->active !== 1) {
                continue;
            }
            if (isset($appointment->is_cancelled) && (int) $appointment->is_cancelled === 1) {
                continue;
            }
            if (isset($appointment->cancelled_booking) && (int) $appointment->cancelled_booking === 1) {
                continue;
            }

            $canonicalAppointmentId = (int) ($appointment->appointment_id ?? 0);
            if ($canonicalAppointmentId <= 0) {
                $canonicalAppointmentId = $appointmentId;
            }

            if (isset($processed[$canonicalAppointmentId])) {
                continue;
            }
            $processed[$canonicalAppointmentId] = true;

            $aptTimeZone = (string) ($appointment->time_zone ?? '');
            if ($aptTimeZone === '') {
                $aptTimeZone = $originalTz;
            }

            $startTime = (string) ($appointment->start_time ?? '');
            $appointmentDate = (string) ($appointment->appointment_date ?? '');
            if ($startTime === '' || $appointmentDate === '') {
                continue;
            }

            try {
                $aptStart = new \DateTime($appointmentDate . ' ' . $startTime, new \DateTimeZone($aptTimeZone));
                $now = new \DateTime('now', new \DateTimeZone($aptTimeZone));

                // Push message up to 3 minutes BEFORE the meeting start time.
                // Trigger window: [now, now + 3 minutes]
                $windowEnd = clone $now;
                $windowEnd->add(new \DateInterval('PT3M'));

                if (! ($aptStart >= $now && $aptStart <= $windowEnd)) {
                    continue;
                }
            } catch (\Exception $e) {
                continue;
            }

            $sourceUserId = (int) ($appointment->source_user_id ?? 0);
            $sourceGroupId = (int) ($appointment->source_group_id ?? 0);
            $sourceCommunityId = (int) ($appointment->source_community_id ?? 0);
            $sourceCommunityGroupId = (int) ($appointment->source_community_group_id ?? 0);

            if ($sourceUserId <= 0 && $sourceGroupId <= 0 && $sourceCommunityGroupId <= 0) {
                continue;
            }

            if ($this->alreadyPushed($canonicalAppointmentId)) {
                continue;
            }

            $ownerId = (int) ($appointment->to_user ?? 0);
            $attendeeId = (int) ($appointment->user_id ?? 0);

            $ownerUser = $ownerId > 0 ? $userRepository->findByIdUsername($ownerId) : null;
            $attendeeUser = $attendeeId > 0 ? $userRepository->findByIdUsername($attendeeId) : null;

            $ownerName = $ownerUser && isset($ownerUser->fullname) ? $ownerUser->fullname : '';
            $attendeeName = $attendeeUser && isset($attendeeUser->fullname) ? $attendeeUser->fullname : (string) ($appointment->name ?? '');
            $attendeeEmail = $attendeeUser && isset($attendeeUser->email_address) ? $attendeeUser->email_address : (string) ($appointment->email ?? '');

            $resume = $ownerId > 0 ? $resumeRepository->getByUserId($ownerId) : null;
            $ownerUrl = $resume ? $resume->present()->url() : '';

            // Render clean chat message bodies.
            $attendeeText = '';
            $ownerText = '';
            try {
                $attendeeText = (string) view('themes.frontend.default.views.messages.user-invite', [
                    'appointment_id' => $appointmentId,
                    'fullname' => $ownerName,
                    'user_name' => $sourceGroupId > 0 || $sourceCommunityGroupId > 0 ? 'Everyone' : $attendeeName,
                    'toUid' => $attendeeId,
                    'toEmail' => $attendeeEmail,
                ])->render();

                $ownerText = (string) view('themes.frontend.default.views.messages.user-invite-own', [
                    'appointment_id' => $appointmentId,
                    'fullname' => $ownerName,
                    'toUid' => $attendeeId,
                    'toEmail' => $attendeeEmail,
                ])->render();
            } catch (\Exception $e) {
                continue;
            }

            $attendeeText = trim($attendeeText);
            $ownerText = trim($ownerText);

            try {
                if ($sourceUserId > 0) {
                    // Direct chat: push ONE message row (like group chat) and use text_sender
                    // so sender & receiver can see different text without duplicate DB rows.
                    if ($ownerId <= 0 || $attendeeId <= 0) {
                        continue;
                    }

                    $msgA = $messageRepository->send(
                        $attendeeId,
                        $attendeeText,
                        null,
                        $ownerId,
                        0,
                        [],
                        $sourceCommunityId,
                        0,
                        0,
                        0
                    );

                    // Store sender-facing version for the same row.
                    if ($msgA && \Schema::hasColumn('messages', 'text_sender')) {
                        try {
                            $msgA->text_sender = $ownerText;
                        } catch (\Exception $e) {
                            // ignore
                        }
                    }

                    $this->markMeetingPush($msgA, $canonicalAppointmentId);

                    if ($msgA && isset($msgA->id)) {
                        $msgA = $messageRepository->sendFromHold($ownerId, $msgA->id, 0, '', '', 'send');
                    }
                    $realtimeRepository->add($attendeeId, 'message');
                    $realtimeRepository->add($ownerId, 'message');

                    $pushedCount++;
                    $this->info('Pushed meeting chat message for appointment #' . $canonicalAppointmentId . ' (direct).');

                    continue;
                }

                // Group/community-group: push ONE message into the stream.
                // Store attendee-facing text in messages.text, and (optionally) store owner-facing
                // text in messages.text_sender so the sender can see a different version.
                $fromUserId = $ownerId > 0 ? $ownerId : ($attendeeId > 0 ? $attendeeId : 0);
                if ($fromUserId <= 0) {
                    continue;
                }

                if ($sourceGroupId > 0) {
                    // attendee-style
                    $msgA = $messageRepository->send(
                        '',
                        $attendeeText,
                        null,
                        $fromUserId,
                        $sourceGroupId,
                        [],
                        $sourceCommunityId,
                        0,
                        0,
                        0
                    );

                    // Store owner-facing message for the sender in the same row (meeting cron only).
                    if ($msgA && $ownerId > 0 && \Schema::hasColumn('messages', 'text_sender')) {
                        try {
                            $msgA->text_sender = $ownerText;
                        } catch (\Exception $e) {
                            // ignore
                        }
                    }

                    $this->markMeetingPush($msgA, $canonicalAppointmentId);

                    if ($msgA && isset($msgA->id)) {
                        $msgA = $messageRepository->sendFromHold($fromUserId, $msgA->id, 0, '', '', 'send');
                    }

                    $msgAId = (int) ($msgA->id ?? 0);

                    $realtimeRepository->add($fromUserId, 'message');

                    // Ensure all members get realtime update in cron context.
                    try {
                        $members = $messageGroupMembersRepository->getMembers($sourceGroupId);
                        if (is_array($members)) {
                            foreach ($members as $memberId) {
                                $memberId = (int) $memberId;
                                if ($memberId > 0) {
                                    // Ensure unread/new message count for every recipient (including sender).
                                    if ($msgAId > 0) {
                                        $postSeenRepository->addNewChat($memberId, 'chat_message', $sourceGroupId, $msgAId, $sourceCommunityId);
                                    }
                                    $realtimeRepository->add($memberId, 'message');
                                }
                            }
                        }
                    } catch (\Exception $e) {
                        // ignore
                    }

                    $pushedCount++;
                    $this->info('Pushed meeting chat message for appointment #' . $canonicalAppointmentId . ' (group).');

                    continue;
                }

                if ($sourceCommunityGroupId > 0) {
                    // attendee-style
                    $msgA = $messageRepository->send(
                        '',
                        $attendeeText,
                        null,
                        $fromUserId,
                        0,
                        [],
                        $sourceCommunityId,
                        0,
                        $sourceCommunityGroupId,
                        0
                    );

                    if ($msgA && $ownerId > 0 && \Schema::hasColumn('messages', 'text_sender')) {
                        try {
                            $msgA->text_sender = $ownerText;
                        } catch (\Exception $e) {
                            // ignore
                        }
                    }

                    $this->markMeetingPush($msgA, $canonicalAppointmentId);

                    if ($msgA && isset($msgA->id)) {
                        $msgA = $messageRepository->sendFromHold($fromUserId, $msgA->id, 0, '', '', 'send');
                    }

                    $msgAId = (int) ($msgA->id ?? 0);

                    $realtimeRepository->add($fromUserId, 'message');

                    // Ensure all community-group members get realtime update in cron context.
                    try {
                        $community = $communityRepository->getById($sourceCommunityId);
                        $memberIds = $userRepository->getCommunityMembersIds($sourceCommunityId, $sourceCommunityGroupId);
                        $memberIds = is_array($memberIds) ? $memberIds : [];
                        if ($community && isset($community->user_id)) {
                            $memberIds[] = (int) $community->user_id;
                        }
                        $memberIds[] = (int) $fromUserId;

                        $memberIds = array_values(array_unique(array_filter($memberIds, function ($v) {
                            return (int) $v > 0;
                        })));

                        foreach ($memberIds as $memberId) {
                            // Ensure unread/new message count for every recipient (including sender).
                            if ($msgAId > 0) {
                                $postSeenRepository->addNewChat((int) $memberId, 'group_message', $sourceCommunityGroupId, $msgAId, $sourceCommunityId);
                            }
                            $realtimeRepository->add((int) $memberId, 'message');
                        }
                    } catch (\Exception $e) {
                        // ignore
                    }

                    $pushedCount++;
                    $this->info('Pushed meeting chat message for appointment #' . $canonicalAppointmentId . ' (community-group).');

                    continue;
                }
            } catch (\Exception $e) {
                // Do not let one failure stop the command.
                continue;
            }
        }

        date_default_timezone_set($originalTz);

        $this->info('Done. Pushed: ' . $pushedCount);
    }

    private function alreadyPushed(int $canonicalAppointmentId): bool
    {
        try {
            $query = \DB::table('messages')
                ->where(function ($q) use ($canonicalAppointmentId) {
                    $q->where(function ($q2) use ($canonicalAppointmentId) {
                        $q2->where('message_type', 'meeting_push')
                            ->where('message_type_id', $canonicalAppointmentId);
                    });

                    if (\Schema::hasColumn('messages', 'meeting_appointment_id')) {
                        $q->orWhere('meeting_appointment_id', $canonicalAppointmentId);
                    }
                });

            return (bool) $query->first();
        } catch (\Exception $e) {
            return false;
        }
    }

    private function markMeetingPush($message, int $canonicalAppointmentId): void
    {
        if (! $message) {
            return;
        }

        try {
            if (\Schema::hasColumn('messages', 'message_type')) {
                $message->message_type = 'meeting_push';
            }
            if (\Schema::hasColumn('messages', 'message_type_id')) {
                $message->message_type_id = $canonicalAppointmentId;
            }
            if (\Schema::hasColumn('messages', 'meeting_appointment_id')) {
                $message->meeting_appointment_id = $canonicalAppointmentId;
            }
            $message->save();
        } catch (\Exception $e) {
            // ignore
        }
    }

    private function emailHtmlToChatText(string $html): string
    {
        $text = $html;

        // Convert links to "text (url)" so URLs survive strip_tags().
        $text = preg_replace('~<a\s+[^>]*href=["\']([^"\']+)["\'][^>]*>(.*?)</a>~is', '$2 ($1)', $text);

        // Preserve rough line breaks.
        $text = str_ireplace(["<br>", "<br/>", "<br />", "</p>", "</div>", "</tr>", "</h1>", "</h2>"], "\n", $text);
        $text = strip_tags($text);
        $text = html_entity_decode($text, ENT_QUOTES | ENT_HTML5);

        // Normalize whitespace.
        $text = preg_replace("/\r\n|\r/", "\n", $text);
        $text = preg_replace("/\n{3,}/", "\n\n", $text);
        $text = preg_replace("/[ \t]{2,}/", " ", $text);

        return trim($text);
    }
}
