<?php

namespace App\Services\Messaging;

use App\Models\ConversationModel;
use App\Models\MessageAttachmentModel;
use App\Models\MessageModel;
use App\Models\UserModel;
use App\Services\Billing\CreditService;
use App\Services\Notification\NotificationService;
use App\Services\Support\ActivityLogger;

/**
 * Owns messaging business rules. Per the spec, "Messaging" is a
 * credit-gated action for agencies (credit_costs.action = 'messaging') —
 * charged once per NEW conversation an agency starts, not per message,
 * matching how contact-reveal-style unlocks work elsewhere in the system.
 * Candidates never pay to message back.
 */
class MessagingService
{
    private ConversationModel $conversations;
    private MessageModel $messages;
    private MessageAttachmentModel $attachments;
    private UserModel $userModel;
    private CreditService $credits;
    private NotificationService $notifications;
    private ActivityLogger $activity;

    public function __construct()
    {
        $this->conversations = new ConversationModel();
        $this->messages      = new MessageModel();
        $this->attachments   = new MessageAttachmentModel();
        $this->userModel     = new UserModel();
        $this->credits       = new CreditService();
        $this->notifications = new NotificationService();
        $this->activity      = new ActivityLogger();
    }

    /**
     * Starts (or reuses) a conversation. $initiatorAgencyId is null when a
     * candidate initiates — only the agency side is credit-gated per the spec.
     */
    public function startConversation(int $initiatorUserId, int $recipientUserId, ?int $jobApplicationId, ?int $initiatorAgencyId): array
    {
        $existing = $this->conversations->betweenUsers([$initiatorUserId, $recipientUserId], $jobApplicationId);
        if ($existing) {
            return $existing;
        }

        if ($initiatorAgencyId !== null) {
            $cost = $this->credits->costFor('messaging');
            $this->credits->debit($initiatorAgencyId, $cost, 'conversations', null, $initiatorUserId);
        }

        $id = $this->conversations->insert(['job_application_id' => $jobApplicationId], true);
        $this->conversations->addParticipants($id, [$initiatorUserId, $recipientUserId]);

        return $this->conversations->find($id);
    }

    public function sendMessage(int $conversationId, int $senderId, string $body, array $attachments = []): array
    {
        if (! $this->conversations->isParticipant($conversationId, $senderId)) {
            throw new \App\Exceptions\ForbiddenException('You are not a participant in this conversation.');
        }

        $messageId = $this->messages->insert([
            'conversation_id' => $conversationId,
            'sender_id'       => $senderId,
            'body'            => $body,
            'is_read'         => 0,
            'created_at'      => date('Y-m-d H:i:s'),
        ], true);

        foreach ($attachments as $attachment) {
            $this->attachments->insert([
                'message_id'         => $messageId,
                'file_path'          => $attachment['file_path'],
                'original_filename'  => $attachment['original_filename'],
                'file_size_kb'       => $attachment['file_size_kb'] ?? null,
            ]);
        }

        $this->notifyOtherParticipants($conversationId, $senderId, $body);
        $this->activity->log($senderId, null, null, 'message_sent', ['conversation_id' => $conversationId]);

        return $this->messages->find($messageId);
    }

    public function markRead(int $conversationId, int $readerUserId): void
    {
        $this->messages->markConversationRead($conversationId, $readerUserId);
    }

    public function conversationsFor(int $userId): array
    {
        return $this->conversations->forUser($userId);
    }

    public function messagesFor(int $conversationId, int $requestingUserId, int $page = 1, int $perPage = 30): array
    {
        if (! $this->conversations->isParticipant($conversationId, $requestingUserId)) {
            throw new \App\Exceptions\ForbiddenException('You are not a participant in this conversation.');
        }
        return $this->messages->forConversation($conversationId, $page, $perPage);
    }

    private function notifyOtherParticipants(int $conversationId, int $senderId, string $body): void
    {
        $sender = $this->userModel->find($senderId);
        $participants = \Config\Database::connect()->table('conversation_participants')
            ->where('conversation_id', $conversationId)->where('user_id !=', $senderId)
            ->get()->getResultArray();

        foreach ($participants as $p) {
            $this->notifications->send((int) $p['user_id'], 'new_message', [
                'sender_name' => $sender->name,
                'preview'     => mb_substr($body, 0, 80),
            ]);
        }
    }
}
