<?php

namespace App\Services;

use App\Models\User;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;

/**
 * In-panel notifications for Thinkrax staff.
 *
 * Product applications write into this same table when a tenant signs up or
 * uploads a payment proof, which is what makes those events show up here
 * without any queue or webhook between the two systems.
 */
class Notifier
{
    /** @param  iterable<User>  $users */
    public function send(
        iterable $users,
        string $type,
        string $title,
        string $message,
        ?string $actionUrl = null,
        array $data = [],
    ): int {
        $rows = [];
        $now = now();

        foreach ($users as $user) {
            $rows[] = [
                'id' => Str::uuid()->toString(),
                'type' => $type,
                'notifiable_type' => User::class,
                'notifiable_id' => $user->id,
                'data' => json_encode([
                    'title' => $title,
                    'message' => $message,
                    'action_url' => $actionUrl,
                ] + $data, JSON_UNESCAPED_UNICODE),
                'read_at' => null,
                'created_at' => $now,
                'updated_at' => $now,
            ];
        }

        if ($rows === []) {
            return 0;
        }

        DB::table('notifications')->insert($rows);

        return count($rows);
    }

    public function sendToRoles(
        array $roles,
        string $type,
        string $title,
        string $message,
        ?string $actionUrl = null,
        array $data = [],
    ): int {
        return $this->send(
            User::query()->active()->whereIn('role', $roles)->get(),
            $type, $title, $message, $actionUrl, $data,
        );
    }

    public function unreadCountFor(User $user): int
    {
        return (int) DB::table('notifications')
            ->where('notifiable_type', User::class)
            ->where('notifiable_id', $user->id)
            ->whereNull('read_at')
            ->count();
    }

    public function markAllRead(User $user): void
    {
        DB::table('notifications')
            ->where('notifiable_type', User::class)
            ->where('notifiable_id', $user->id)
            ->whereNull('read_at')
            ->update(['read_at' => now(), 'updated_at' => now()]);
    }

    public function markRead(string $id, User $user): void
    {
        DB::table('notifications')
            ->where('id', $id)
            ->where('notifiable_type', User::class)
            ->where('notifiable_id', $user->id)
            ->update(['read_at' => now(), 'updated_at' => now()]);
    }

    /** @return Collection<int,object> */
    public function recentFor(User $user, int $limit = 20): Collection
    {
        return DB::table('notifications')
            ->where('notifiable_type', User::class)
            ->where('notifiable_id', $user->id)
            ->orderByDesc('created_at')
            ->limit($limit)
            ->get()
            ->map(function ($row) {
                $row->payload = json_decode((string) $row->data, true) ?: [];

                return $row;
            });
    }

    public function prune(int $days = 90): int
    {
        return DB::table('notifications')
            ->whereNotNull('read_at')
            ->where('created_at', '<', now()->subDays($days))
            ->delete();
    }
}
